我在 VM 中有一个 ObservableCollection,它显示在 ListView 的 View 中。当所选项目更改时,SelectionChanged 事件会很好地触发。下面是我如何配置 ListView:
<ListView Grid.Row="3" Margin="5" AlternationCount="2" Name="_lvSettings"
IsSynchronizedWithCurrentItem="True"
ItemsSource="{Binding Path=CollectionView}"
SelectedIndex="{Binding Path=SelectedSettingIndex}"
SelectionChanged="OnSelectionChanged" >
<ListView.View>
<GridView>
<GridViewColumn Width="170"
Header="{Binding Path=ShowAllDisplay}"
x:Name="_colSettings"
DisplayMemberBinding="{Binding Path=Setting}"/>
<GridViewColumn Header="Old Value" Width="150"
DisplayMemberBinding="{Binding Path=OldVal}"/>
<GridViewColumn Header="New Value"
DisplayMemberBinding="{Binding Path=NewVal}" />
</GridView>
</ListView.View>
</ListView>
我遇到的问题是当我更改集合上的过滤器时。所选项目保持不变,这很好,但是 ListView 更改为从第一个项目显示,并且经常选择项目不在 View 中(但仍然是所选项目)。
在 VM 中,我有属性“SelectedSettingIndex”,它在更改时抛出 PropertyChanged 事件。即使我自己在过滤器更改时从 VM 手动引发事件(base.OnPropertyChanged("SelectedSettingIndex");),事件似乎也没有真正引发,因为属性并没有真正改变。在这种情况下,必须有一种方法可以调用 ScrollIntoView 或类似的东西,但我无法找出正确的事件或触发器来这样做。我缺少什么?
编辑
这是对我所关心的问题的描述,希望更好:
1) 我在 VM 中使用 CollectionViewSource 来过滤数据。
2) 有一个按钮供用户在过滤器之间切换。
3) 假设 ListView 在任何给定时间都有空间最多显示 10 个项目。
4) 用户在 ListView 中索引为 50 的筛选 View 中选择项目“A”。
5) 然后用户单击按钮关闭过滤。
预期结果:ListView 填充了未过滤的列表,项目“A”保持选中状态,ListView 被“滚动”,使得项目“A”仍然可见。
实际结果:ListView 中填充了未过滤的列表,项目“A”保持选中状态,ListView“滚动”到顶部并显示前 10 个项目。项目“A”不在 View 中。
最佳答案
如果您使用的是 MVVM,那么您需要确保已将绑定(bind)设置为 viewModel 中的选定项,并且还使用 Mode=TwoWay
设置了绑定(bind)。 ...为了在 Selection 上滚动,我们必须在 ListView 上使用一个行为(避免代码隐藏)
您必须添加对 System.Windows.Interactivity
的引用使用 Behavior<T> class
行为
public class ScrollIntoViewForListView : Behavior<ListView>
{
/// <summary>
/// When Beahvior is attached
/// </summary>
protected override void OnAttached()
{
base.OnAttached();
this.AssociatedObject.SelectionChanged += AssociatedObject_SelectionChanged;
}
/// <summary>
/// On Selection Changed
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
void AssociatedObject_SelectionChanged(object sender,
SelectionChangedEventArgs e)
{
if (sender is ListView)
{
ListView listview = (sender as ListView);
if (listview.SelectedItem != null)
{
listview.Dispatcher.BeginInvoke(
(Action) (() =>
{
listview.UpdateLayout();
if (listview.SelectedItem !=
null)
listview.ScrollIntoView(
listview.SelectedItem);
}));
}
}
}
/// <summary>
/// When behavior is detached
/// </summary>
protected override void OnDetaching()
{
base.OnDetaching();
this.AssociatedObject.SelectionChanged -=
AssociatedObject_SelectionChanged;
}
}
用法
将别名添加到 XAML
作为xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
然后在你的Control
DisplayMemberBinding="{Binding Path=Setting}"/>
现在当在 ViewModel
中设置“MySelectedItem”属性时反射(reflect)更改时,列表将滚动。
变更通知
在 viewModel 中应该调用 INotifyProperty 在你已经绑定(bind)到你的 xaml 的属性的 setter 中更改,以便 viewModel 中的更改可以反射(reflect)到 View ...
在 MVVM 中使用 SelectionChanged 事件
同样在 MVVM 中,您不必使用“SelectionChnaged 事件”,因为您可以调用 MySelectedItem 属性的 Setter 中的函数,或者您可以使用 EventToCommand
显式事件调用类..
过滤
Google 使用 ColletionViewSource 来实现排序、过滤等功能
希望对你有帮助
关于c# - 在 MVVM (WPF) 应用程序中更改 ListView 上的筛选器后如何滚动到 View ?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10135850/