C# 在MVVM(WPF)应用程序中更改ListView上的筛选器后,如何滚动到View?
我在VM中有一个ObservableCollection,它显示在ListView的视图中。当所选项目更改时,SelectionChanged事件会很好地触发。下面是我如何配置ListView的:C# 在MVVM(WPF)应用程序中更改ListView上的筛选器后,如何滚动到View?,c#,wpf,listview,mvvm,filter,C#,Wpf,Listview,Mvvm,Filter,我在VM中有一个ObservableCollection,它显示在ListView的视图中。当所选项目更改时,SelectionChanged事件会很好地触发。下面是我如何配置ListView的: <ListView Grid.Row="3" Margin="5" AlternationCount="2" Name="_lvSettings" IsSynchronizedWithCurrentItem="True" ItemsSource="{Bi
<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从第一个项目更改为显示,并且通常所选项目不在视图中(但仍然是所选项目)
在VM中,我有一个属性“SelectedSettingIndex”,它在属性发生变化时抛出PropertyChanged事件。即使在筛选器更改时我自己从VM手动引发事件(base.OnPropertyChanged(“SelectedSettingIndex”);),该事件似乎也不会真正引发,因为该属性没有真正更改。在这个场景中,必须有一种方法来调用ScrollIntoView或类似的东西,但我无法找出正确的事件或触发器来执行此操作。我错过了什么
编辑
下面是我所关心的问题的一个更好的描述:
1) 我正在VM中使用CollectionViewSource来过滤数据
2) 有一个按钮供用户在过滤器之间切换
3) 让我们假设ListView在任何给定时间都有空间显示多达10个项目
4) 用户在过滤视图中选择项目“A”,该项目位于listview的索引50处
5) 然后,用户单击该按钮以关闭过滤
预期结果:ListView中填充了未过滤的列表,项目“A”保持选中状态,并且ListView被“滚动”以便项目“A”仍然可见
实际结果:ListView填充了未过滤的列表,项目“A”保持选中状态,ListView“滚动”到顶部并显示前10个项目。项目“A”不在视图中。将ListView的SelectedItem保留在属性中:
public MyTypeOfObject SelectedItem { get; set; }
将绑定分配给XAML:
<ListView Name="MyListView" SelectedItem="{Binding SelectedItem}"...></ListView>
编辑:
要在用户控件中执行此操作,为了使视图模型与控件引用(ListView)保持一致,在那里捕获一个标准的
CollectionView
事件,或者定义您自己的事件,该事件将在筛选器或其他作业发生后被触发。如果您使用的是MVVM,那么您需要确保您已经在viewModel中设置了所选项目的绑定,并且也使用模式=双向。。。对于选择上的滚动,我们必须使用ListView上的行为(避免代码隐藏)
您必须添加对System.Windows.Interactivity
的引用才能使用行为类
行为
public类ScrollIntoViewForListView:行为
{
///
///当连接Beahvior时
///
受保护的覆盖无效附加()
{
base.onatached();
this.AssociatedObject.SelectionChanged+=AssociatedObject\u SelectionChanged;
}
///
///在选择上有所改变
///
///
///
无效关联对象\u选择已更改(对象发送方,
选择ChangedEventArgs(e)
{
如果(发件人是ListView)
{
ListView ListView=(发送方为ListView);
如果(listview.SelectedItem!=null)
{
listview.Dispatcher.BeginInvoke(
(行动)(()=>
{
UpdateLayout();
如果(listview.SelectedItem=
空)
listview.ScrollIntoView(
listview.SelectedItem);
}));
}
}
}
///
///当行为被分离时
///
附加时受保护的覆盖无效()
{
base.OnDetaching();
this.AssociatedObject.SelectionChanged-=
关联对象\u选择已更改;
}
}
用法
将别名添加到XAML
asxmlns:i=“clr命名空间:System.Windows.Interactivity;assembly=System.Windows.Interactivity”
然后在控件中
DisplayMemberBinding=“{Binding Path=Setting}”/>
现在,当在ViewModel
中设置“MySelectedItem”属性时,当选择更改时,列表将滚动
变更通知
在viewModel中,应在绑定到xaml的属性设置器中调用INotifyProperty changed,以便可以重新选择viewModel中的更改以查看
在MVVM中使用SelectionChanged事件
此外,在MVVM中,您不必使用“SelectionChnaged事件”,因为您可以在MySelectedItem属性的Setter中调用函数,也可以使用EventToCommand
类进行显式事件调用
过滤
Google使用CollectionViewSource进行排序、过滤等功能
希望它能帮上忙…好的-所以我找到了两个可行的解决方案,但不是100%满意:
1) 使用ViewModel的中介模式通知视图过滤器已更改。然后,视图将调用当前所选项目的ScrollToView。虽然我喜欢VM-to-VM通知的中介,但在ViewModel及其匹配视图之间使用它会让人感觉脏兮兮的
2) 对处理程序中当前选定的项调用ScrollToView以调用ListView的LayoutUpdated事件。笨手笨脚,效率低下——只是我不喜欢这样
我不打算为了寻求更好的解决方案而对此进行标记。把这个放在这里是为了好奇的人或其他可能正在看类似问题的人。Fo
if (SelectedItem != null)
MyListView.ScrollIntoView(SelectedItem);
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;
}
}
public class SelectingItemAttachedProperty
{
public static readonly DependencyProperty SelectingItemProperty = DependencyProperty.RegisterAttached(
"SelectingItem",
typeof(int),
typeof(SelectingItemAttachedProperty),
new PropertyMetadata(default(int), OnSelectingItemChanged));
public static int GetSelectingItem(DependencyObject target)
{
return (int)target.GetValue(SelectingItemProperty);
}
public static void SetSelectingItem(DependencyObject target, int value)
{
target.SetValue(SelectingItemProperty, value);
}
static void OnSelectingItemChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
var lb = sender as ListBox;
if (lb?.SelectedItem == null)
return;
lb.Dispatcher.InvokeAsync(() =>
{
lb.UpdateLayout();
lb.ScrollIntoView(lb.SelectedItem);
});
}
}
<Listbox
design:SelectingItemAttachedProperty.SelectingItem="{Binding CollectionViewFromVM.Count}"
...>