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