将WPF Listview滚动到特定行

将WPF Listview滚动到特定行,wpf,listview,scroll,Wpf,Listview,Scroll,WPF,类似浏览器的应用程序。 我得到了一个包含ListView的页面。调用PageFunction后,我向ListView添加了一行,并希望将新行滚动到视图中: ListViewItem item = ItemContainerGenerator.ContainerFromIndex(index) as ListViewItem; if (item != null) ScrollIntoView(item); 这很有效。只要新行在视图中,该行就会获得应有的焦点 问题是,当线路

WPF,类似浏览器的应用程序。
我得到了一个包含ListView的页面。调用PageFunction后,我向ListView添加了一行,并希望将新行滚动到视图中:

  ListViewItem item = ItemContainerGenerator.ContainerFromIndex(index) as ListViewItem;
  if (item != null)
    ScrollIntoView(item);
这很有效。只要新行在视图中,该行就会获得应有的焦点

问题是,当线路不可见时,事情就不起作用了。
如果该行不可见,则生成的行没有ListViewItem,因此ItemContainerGenerator.ContainerFromIndex返回null


但是如果没有该项,如何将该行滚动到视图中?有没有办法在不需要ListViewItem的情况下滚动到最后一行(或任何地方?

我认为这里的问题是,如果该行不可见,则ListViewItem尚未创建。WPF创建可视的随需应变

因此,在这种情况下,您可能会得到项目的
null
,是吗? (根据你的评论,你是这样做的)

我已经找到了一个滚动条。对我来说,这里给出的解决方案看起来很像黑客,但你可以自己决定

以下是来自的代码片段:


解决方法之一是更改ListView的ItemsPanel。默认面板是VirtualzingStackPanel,它仅在ListBoxItem第一次可见时创建ListBoxItem。如果你的列表中没有太多的项目,这应该不是问题

<ListView>
   ...
   <ListView.ItemsPanel>
      <ItemsPanelTemplate>
         <StackPanel/>
      </ItemsPanelTemplate>
   </ListView.ItemsPanel>
</ListView>

...

有人告诉我一种更好的滚动到特定行的方法,它很简单,效果很好。
简言之:

public void ScrollToLastItem()
{
  lv.SelectedItem = lv.Items.GetItemAt(rows.Count - 1);
  lv.ScrollIntoView(lv.SelectedItem);
  ListViewItem item = lv.ItemContainerGenerator.ContainerFromItem(lv.SelectedItem) as ListViewItem;
  item.Focus();
}

中的较长版本:

谢谢你最后的提示Sam。我打开了一个对话框,这意味着每次对话框关闭时我的网格都会失去焦点。我用这个:

if(currentRow >= 0 && currentRow < lstGrid.Items.Count) {
    lstGrid.SelectedIndex = currentRow;
    lstGrid.ScrollIntoView(lstGrid.SelectedItem);
    if(shouldFocusGrid) {
        ListViewItem item = lstGrid.ItemContainerGenerator.ContainerFromItem(lstGrid.SelectedItem) as ListViewItem;
        item.Focus();
    }
} else if(shouldFocusGrid) {
    lstGrid.Focus();
}
if(currentRow>=0&¤tRow
我对萨姆的答案做了一些修改。注意,我想滚动到最后一行。不幸的是,ListView部分仅显示了最后一行(即使上面有100行),因此我就是这样修复的:

    public void ScrollToLastItem()
    {
        if (_mainViewModel.DisplayedList.Count > 0)
        {
            var listView = myListView;
            listView.SelectedItem = listView.Items.GetItemAt(_mainViewModel.DisplayedList.Count - 1);
            listView.ScrollIntoView(listView.Items[0]);
            listView.ScrollIntoView(listView.SelectedItem);
            //item.Focus();
        }
    }
干杯,试试这个

private void OnSelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            ScrollViewer scrollViewer = GetScrollViewer(lstVw) as ScrollViewer;
            scrollViewer.ScrollToHorizontalOffset(dataRowToFocus.RowIndex);
            if (dataRowToFocus.RowIndex < 2)
                lstVw.ScrollIntoView((Entity)lstVw.Items[0]);
            else
                lstVw.ScrollIntoView(e.AddedItems[0]);
        } 

 public static DependencyObject GetScrollViewer(DependencyObject o)
        {
            if (o is ScrollViewer)
            { return o; }

            for (int i = 0; i < VisualTreeHelper.GetChildrenCount(o); i++)
            {
                var child = VisualTreeHelper.GetChild(o, i);

                var result = GetScrollViewer(child);
                if (result == null)
                {
                    continue;
                }
                else
                {
                    return result;
                }
            }
            return null;
        } 

private void Focus()
{
 lstVw.SelectedIndex = dataRowToFocus.RowIndex;
 lstVw.SelectedItem = (Entity)dataRowToFocus.Row;

 ListViewItem lvi = (ListViewItem)lstVw.ItemContainerGenerator.ContainerFromItem(lstVw.SelectedItem);
ContentPresenter contentPresenter = FindVisualChild<ContentPresenter>(lvi);
contentPresenter.Focus();
contentPresenter.BringIntoView();

}
SelectionChanged上的私有无效(对象发送者,SelectionChangedEventArgs e)
{
ScrollViewer ScrollViewer=GetScrollViewer(lstVw)作为ScrollViewer;
scrollViewer.ScrollToHorizontalOffset(dataRowToFocus.RowIndex);
if(dataRowToFocus.RowIndex<2)
lstVw.ScrollIntoView((实体)lstVw.Items[0]);
其他的
lstVw.ScrollIntoView(e.AddedItems[0]);
} 
公共静态DependencyObject GetScrollViewer(DependencyObject o)
{
如果(o是ScrollViewer)
{返回o;}
for(int i=0;i
我刚才遇到了同样的问题,ItemContainerGenerator.ContainerFromItem()和ItemContainerGenerator.ContainerFromIndex()对于列表框中明显存在的项返回null。Decasteljau是对的,但我必须做一些挖掘来弄清楚他到底是什么意思。以下是为下一位男士/女士节省一些腿部工作的分解图

长话短说,ListBoxItems如果不在视图中,则会被销毁。因此,ContainerFromItem()和ContainerFromIndex()返回null,因为ListBoxItems不存在。这显然是一项节省内存/性能的功能,详细说明如下:

空的
代码是关闭虚拟化的原因。为我解决问题的示例代码:

数据模板:

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

主体:

<Grid x:Name="ContentPanel">
    <ListBox Name="lbResults" ItemsSource="{Binding SearchResults}" ItemTemplate="{StaticResource StoryViewModelTemplate}">
        <ListBox.ItemsPanel>
             <ItemsPanelTemplate>
                 <StackPanel>
                 </StackPanel>
             </ItemsPanelTemplate>
        </ListBox.ItemsPanel>
    </ListBox>
</Grid>

如果您只想在创建新数据项后显示并聚焦最后一项,此方法可能更好。与ScrollIntoView相比,ScrollViewer的ScrollToEnd在我的测试中更可靠。 在一些测试中,使用上面类似ListView的ScrollIntoView方法失败了,我不知道原因。但是使用ScrollViewer滚动到最后一个可以工作

void FocusLastOne(ListView lsv)
{
   ObservableCollection<object> items= sender as ObservableCollection<object>;

   Decorator d = VisualTreeHelper.GetChild(lsv, 0) as Decorator;
   ScrollViewer v = d.Child as ScrollViewer;
   v.ScrollToEnd();

   lsv.SelectedItem = lsv.Items.GetItemAt(items.Count - 1);
   ListViewItem lvi = lsv.ItemContainerGenerator.ContainerFromIndex(items.Count - 1) as ListViewItem;
   lvi.Focus();
}
void FocusLastOne(ListView lsv)
{
ObservableCollection items=发送方作为ObservableCollection;
Decorator d=VisualTreeHelper.GetChild(lsv,0)作为Decorator;
ScrollViewer v=d。作为ScrollViewer的子级;
v、 ScrollToEnd();
lsv.SelectedItem=lsv.Items.GetItemAt(Items.Count-1);
ListViewItem lvi=lsv.ItemContainerGenerator.ContainerFromIndex(items.Count-1)作为ListViewItem;
lvi.Focus();
}

为了克服虚拟化问题,但仍然使用
ScrollIntoView
,而不是对ListView进行深入研究,您还可以使用ViewModel对象来确定所选内容。假设列表中的ViewModel对象具有
IsSelected
属性。在XAML中将项目链接到ListView,如下所示:

<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>
PersonVMs.ForEach(vm => vm.IsSelected = false);
PersonVMs.Add(newPersonVM);
newPersonVM.IsSelected = true;
ViewCommandManager.InvokeLoaded("ScrollToSelectedPerson");

在我的项目中,我需要从listview向用户显示所选的索引行,因此我将所选项目分配给listview控件。此代码将滚动滚动条并显示sele
var firstSelected = PersonsListView.Items
    .OfType<TreeViewItemViewModel>().FirstOrDefault(x => x.IsSelected);
if (firstSelected != null)
    CoObjectsListView.ScrollIntoView(firstSelected);
PersonVMs.ForEach(vm => vm.IsSelected = false);
PersonVMs.Add(newPersonVM);
newPersonVM.IsSelected = true;
ViewCommandManager.InvokeLoaded("ScrollToSelectedPerson");