Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/wpf/13.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 在MVVM(WPF)应用程序中更改ListView上的筛选器后,如何滚动到View?_C#_Wpf_Listview_Mvvm_Filter - Fatal编程技术网

C# 在MVVM(WPF)应用程序中更改ListView上的筛选器后,如何滚动到View?

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

我在VM中有一个ObservableCollection,它显示在ListView的视图中。当所选项目更改时,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从第一个项目更改为显示,并且通常所选项目不在视图中(但仍然是所选项目)

在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
as
xmlns: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}"
   ...>