Scroll WPF Listview to specific line

前端 未结 11 992
孤城傲影
孤城傲影 2020-11-30 02:34

WPF, Browserlike app.
I got one page containing a ListView. After calling a PageFunction I add a line to the ListView, and want to scroll the new line into view:

相关标签:
11条回答
  • 2020-11-30 03:08

    To overcome the virtualisation issue but still use ScrollIntoView and not hacking around in the guts of the ListView, you could also use your ViewModel objects to determine what is selected. Assuming that you have ViewModel objects in your list that feature an IsSelected property. You'd link the items to the ListView in XAML like this:

    <ListView Name="PersonsListView" ItemsSource="{Binding PersonVMs}">
      <ListView.ItemContainerStyle>
        <Style TargetType="{x:Type ListViewItem}">
          <Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}" />
        </Style>
      </ListView.ItemContainerStyle>
    </ListView>
    

    Then, the code-behind method can scroll to the first selected item with this:

    var firstSelected = PersonsListView.Items
        .OfType<TreeViewItemViewModel>().FirstOrDefault(x => x.IsSelected);
    if (firstSelected != null)
        CoObjectsListView.ScrollIntoView(firstSelected);
    

    This also works if the selected item is well out of view. In my experiment, the PersonsListView.SelectedItem property was null, but of course your ViewModel IsSelected property is always there. Be sure to call this method after all binding and loading has completed (with the right DispatcherPriority).

    Using the ViewCommand pattern, your ViewModel code could look like this:

    PersonVMs.ForEach(vm => vm.IsSelected = false);
    PersonVMs.Add(newPersonVM);
    newPersonVM.IsSelected = true;
    ViewCommandManager.InvokeLoaded("ScrollToSelectedPerson");
    
    0 讨论(0)
  • 2020-11-30 03:10

    I just had the same issue with ItemContainerGenerator.ContainerFromItem() and ItemContainerGenerator.ContainerFromIndex() returning null for items that clearly existed in the listbox. Decasteljau was right but I had to do some digging to figure out exactly what he meant. Here is the breakdown to save the next guy/gal some legwork.

    Long story short, ListBoxItems are destroyed if they are not within view. Consequently ContainerFromItem() and ContainerFromIndex() return null since the ListBoxItems do not exist. This is apparently a memory/performance saving feature detailed here: http://blogs.msdn.com/b/oren/archive/2010/11/08/wp7-silverlight-perf-demo-1-virtualizingstackpanel-vs-stackpanel-as-a-listbox-itemspanel.aspx

    The empty <ListBox.ItemsPanel> code is what turns off the virtualization. Sample code that fixed the issue for me:

    Data template:

    <phone:PhoneApplicationPage.Resources>
        <DataTemplate x:Key="StoryViewModelTemplate">
            <StackPanel>
              <your datatemplated stuff here/>
            </StackPanel>
        </DataTemplate>
    </phone:PhoneApplicationPage.Resources>
    

    Main body:

    <Grid x:Name="ContentPanel">
        <ListBox Name="lbResults" ItemsSource="{Binding SearchResults}" ItemTemplate="{StaticResource StoryViewModelTemplate}">
            <ListBox.ItemsPanel>
                 <ItemsPanelTemplate>
                     <StackPanel>
                     </StackPanel>
                 </ItemsPanelTemplate>
            </ListBox.ItemsPanel>
        </ListBox>
    </Grid>
    
    0 讨论(0)
  • 2020-11-30 03:13

    I think the problem here is that the ListViewItem is not created yet if the line is not visible. WPF creates the Visible on demand.

    So in this case you probably get null for the item, do you? (According to your comment, you do)

    I have found a link on MSDN forums that suggest accessing the Scrollviewer directly in order to scroll. To me the solution presented there looks very much like a hack, but you can decide for yourself.

    Here is the code snippet from the link above:

    VirtualizingStackPanel vsp =  
      (VirtualizingStackPanel)typeof(ItemsControl).InvokeMember("_itemsHost",
       BindingFlags.Instance | BindingFlags.GetField | BindingFlags.NonPublic, null, 
       _listView, null);
    
    double scrollHeight = vsp.ScrollOwner.ScrollableHeight;
    
    // itemIndex_ is index of the item which we want to show in the middle of the view
    double offset = scrollHeight * itemIndex_ / _listView.Items.Count;
    
    vsp.SetVerticalOffset(offset);
    
    0 讨论(0)
  • 2020-11-30 03:14

    not sure if this is the way to go but this currently works for me using WPF, MVVM Light, and .NET 3.5

    I added the SelectionChanged event for ListBox called "lbPossibleError_SelectionChanged" I added the SelectionChanged event for ListBox

    then behind this "lbPossibleError_SelectionChanged" event, here's the code enter image description here

    works as it should for me.

    0 讨论(0)
  • 2020-11-30 03:22

    One workaround to this is to change the ItemsPanel of the ListView. The default panel is the VirtualizingStackPanel which only creates the ListBoxItem the first time they become visible. If you don't have too many items in your list, it should not be a problem.

    <ListView>
       ...
       <ListView.ItemsPanel>
          <ItemsPanelTemplate>
             <StackPanel/>
          </ItemsPanelTemplate>
       </ListView.ItemsPanel>
    </ListView>
    
    0 讨论(0)
提交回复
热议问题