将WPF Listview滚动到特定行

wpf listview scroll

36999 观看

12回复

14360 作者的声誉

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

  ListViewItem item = ItemContainerGenerator.ContainerFromIndex(index) as ListViewItem;
  if (item != null)
    ScrollIntoView(item);

这有效。只要新线在视图中,线就会得到它应该的焦点。

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

但如果没有该项目,如何将该行滚动到视图中?有没有办法滚动到最后一行(或任何地方)而不需要ListViewItem?

作者: Sam 的来源 发布者: 2008 年 10 月 17 日

回应 12


10

1436 作者的声誉

决定

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

所以在这种情况下你可能会得到null这个项目,对吗?(根据你的评论,你这样做)

在MSDN论坛上找到了一个链接,建议直接访问Scrollviewer以便滚动。对我来说,那里提出的解决方案看起来非常像黑客,但你可以自己决定。

以下是上述链接的代码段:

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);
作者: EFrank 发布者: 2008 年 10 月 17 日

3

4924 作者的声誉

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

<ListView>
   ...
   <ListView.ItemsPanel>
      <ItemsPanelTemplate>
         <StackPanel/>
      </ItemsPanelTemplate>
   </ListView.ItemsPanel>
</ListView>
作者: decasteljau 发布者: 2008 年 10 月 17 日

39

14360 作者的声誉

有人告诉我一个更好的方法滚动到一个特定的线,这很容易,就像魅力一样。
简而言之:

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();
}

MSDN论坛中的较长版本:

作者: Sam 发布者: 2008 年 10 月 23 日

2

5800 作者的声誉

谢谢你的最后一个提示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();
}
作者: Echilon 发布者: 2009 年 3 月 26 日

5

2769 作者的声誉

我对Sam的回答做了一些修改。请注意,我想滚动到最后一行。不幸的是,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();
        }
    }

干杯

作者: Joseph jun. Melettukunnel 发布者: 2009 年 8 月 20 日

2

21 作者的声誉

试试这个

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();

}
作者: Murali.S 发布者: 2010 年 4 月 9 日

1

363 作者的声誉

我刚刚遇到了与ItemContainerGenerator.ContainerFromItem()和ItemContainerGenerator.ContainerFromIndex()相同的问题,对列表框中明显存在的项目返回null。Decasteljau是对的,但我不得不做一些挖掘来弄清楚他的意思。这是为了节省下一个人/ gal一些腿部的细分。

长话短说,如果ListBoxItem不在视图中,它们将被销毁。因此,ContainerFromItem()和ContainerFromIndex()返回null,因为ListBoxItems不存在。这显然是一个内存/性能保存功能,详见:http//blogs.msdn.com/b/oren/archive/2010/11/08/wp7-silverlight-perf-demo-1-virtualizingstackpanel-vs-stackpanel-作为一种列表框-itemspanel.aspx

<ListBox.ItemsPanel>代码是关闭虚拟化的原因。为我解决问题的示例代码:

数据模板:

<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>
作者: Ray Ackley 发布者: 2011 年 12 月 7 日

2

47 作者的声誉

如果您只想在创建新数据项后显示并聚焦最后一项,则此方法可能更好。与ScrollIntoView相比,ScrollView的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();
}
作者: Welson Zhong 发布者: 2012 年 11 月 13 日

0

5710 作者的声誉

要克服虚拟化问题但仍然使用ScrollIntoView而不是在ListView的内核中进行攻击,您还可以使用ViewModel对象来确定所选内容。假设您的列表中包含具有IsSelected属性的ViewModel对象。您可以将项目链接到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>

然后,代码隐藏方法可以滚动到第一个选定的项目:

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

如果所选项目远离视图,这也适用。在我的实验中,PersonsListView.SelectedItem属性是null,但当然你的ViewModel IsSelected属性总是在那里。确保在所有绑定和加载完成后调用此方法(右侧DispatcherPriority)。

使用ViewCommand模式,您的ViewModel代码可能如下所示:

PersonVMs.ForEach(vm => vm.IsSelected = false);
PersonVMs.Add(newPersonVM);
newPersonVM.IsSelected = true;
ViewCommandManager.InvokeLoaded("ScrollToSelectedPerson");
作者: ygoe 发布者: 2013 年 3 月 8 日

0

1517 作者的声誉

在我的项目中,我需要将列表视图中选定的索引行显示给用户,因此我将所选项目分配给ListView控件。此代码将滚动滚动条并显示所选项目。

BooleanListView.ScrollIntoView(BooleanListView.SelectedItem);

要么

var listView = BooleanListView; listView.SelectedItem = listView.Items.GetItemAt(BooleanListView.SelectedIndex); listView.ScrollIntoView(listView.Items [0]); listView.ScrollIntoView(listView.SelectedItem);

作者: Jeyavel 发布者: 2015 年 1 月 21 日

0

506 作者的声誉

不确定这是否可行,但这对我来说使用WPF,MVVM Light和.NET 3.5

我为ListBox添加了名为“ lbPossibleError_SelectionChanged ” 的SelectionChanged事件我为ListBox添加了SelectionChanged事件

然后在这个“ lbPossibleError_SelectionChanged ”事件后面,这是代码 在此输入图像描述

对我来说应该是有效的。

作者: Jayson Ragasa 发布者: 2015 年 3 月 16 日

-1

83 作者的声誉

这里给出的答案对我来说很有用,因为我想在xaml中做到这一点。

作者: h.m.i.13 发布者: 2018 年 11 月 15 日
32x32