我有以下视图模型类:
public class ViewModel : INotifyPropertyChanged {
public event PropertyChangedEventHandler PropertyChanged;
private bool _isSelected;
public bool IsSelected {
get => _isSelected;
set {
if (value == _isSelected) { return; }
_isSelected = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(IsSelected)));
}
}
public int Data { get; }
public ViewModel(int data) => Data = data;
}
和以下视图:
<Window x:Class="MVVMScrollIntoView.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<DataGrid Name="dg">
<DataGrid.RowStyle>
<Style TargetType="DataGridRow">
<Setter Property="IsSelected" Value="{Binding IsSelected}"/>
</Style>
</DataGrid.RowStyle>
</DataGrid>
</Window>
我在下面的代码中设置了DataGrid的ItemsSource
,如下所示:
var data = Enumerable.Range(1, 100).Select(x => new ViewModel(x)).ToList();
dg.ItemsSource = data;
将数据网格中的选择/取消选择行传播到视图模型实例,并将从代码到视图模型的IsSelected
属性的更改传播回数据网格。
但是我希望通过代码设置IsSelected
属性时:
data[79].IsSelected = true;
选定的数据网格行也应该滚动到视图中,大概使用数据网格的ScrollIntoView
方法。
我最初的想法是在后面的视图代码中侦听SelectionChanged
事件:
dg.SelectionChanged += (s, e) => dg.ScrollIntoView(dg.SelectedItem);
但是这不起作用,因为SelectionChanged
仅在启用虚拟化时才在可见项目上触发。
关闭虚拟化是一个成功的解决方法:
<DataGrid Name="dg" EnableRowVirtualization="False">
...
但是我担心大型列表(超过2万个项目)的性能影响,所以我不希望这样做。
MVVM执行此操作的方式是什么?
如果您不想放弃虚拟化,则可以使用行索引:
private void Button_Click(object sender, RoutedEventArgs e)
{
var idxToScroll = 4;//e.g. scroll to 4
var firstItem = listView.ItemContainerGenerator.ContainerFromIndex(0);
ScrollContentPresenter presenter = null;
for (var vis = firstItem as UIElement; vis != null; vis = VisualTreeHelper.GetParent(vis) as UIElement)
if ((presenter = vis as ScrollContentPresenter) != null)
break;
if (presenter == null)
throw new Exception();
var virtStackPnl = FirstVisualChild(presenter.Content as ItemsPresenter);//VirtualizingStackPanel
(virtStackPnl as VirtualizingPanel)?.BringIndexIntoViewPublic(idxToScroll);
}
private static DependencyObject FirstVisualChild(UIElement visual)
{
if (visual == null)
return null;
if (VisualTreeHelper.GetChildrenCount(visual) == 0)
return null;
return VisualTreeHelper.GetChild(visual, 0);
}