2012-04-13 18 views
3

我有一个ListView中的视图中显示的虚拟机中的ObservableCollection。当选定的项目更改时,SelectionChanged事件触发很好。下面是我如何配置ListView:如何在MVVM(WPF)应用程序中更改ListView上的筛选器后使用ScrollIntoView?

<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从第一个项目更改为显示,并且通常所选项目不在视图中(但仍为选定项目)。

在虚拟机,我的财产“SelectedSettingIndex”抛出PropertyChanged事件时,它的变化。即使我在过滤器更改时从VM手动提取事件(base.OnPropertyChanged(“SelectedSettingIndex”);),事件看起来并没有真正引发,因为属性没有真正改变。在这种情况下,必须有一种方法来调用ScrollIntoView或类似的东西,但我无法弄清楚正确的事件或触发器。我错过了什么?

编辑

这是我所关心的问题的,希望更好,说明:

1)我使用VM中的CollectionViewSource过滤数据。

2)是为用户的过滤器之间切换的按钮。

3)让我们假设ListView中有空间来显示多达10个项目在任何给定的时间。

4)用户在过滤的视图,其是在列表视图索引50选择项“A”。

5)然后用户点击按钮关闭过滤。

预期结果:ListView控件填充了未经过滤的名单“A”保持选中状态,项目和ListView的是“滚动”,使得项目“A”仍是可见的。

实际结果:ListView填充未过滤列表,项目“A”保持选中状态,ListView“滚动”到顶部并显示前10项。项目“A”不在视野中。

回答

1

在属性让你的ListView的的SelectedItem:

public MyTypeOfObject SelectedItem { get; set; } 

分配结合XAML:

<ListView Name="MyListView" SelectedItem="{Binding SelectedItem}"...></ListView> 

现在,只要你改变滤波器做:

if (SelectedItem != null) 
    MyListView.ScrollIntoView(SelectedItem); 

编辑:

要在用户控件中执行此操作,为了让您从控件引用(ListView)中查看模型清理,请捕获标准的CollectionView事件或定义您自己的事件,该事件将在过滤器或其他工作发生后触发。

+0

我几乎已经doin g你在第一篇文章中提出的建议,除了使用SelectedIndex而不是SelectedItem,并且我没有在我的虚拟机中引用ListView。无论哪种方式我的结果是相同的 - 改变过滤器重置视图“滚动”到开始。 对于第二个建议 - 我使用CollectionView(在VM中)进行过滤。我找不到适当的事件来回应,或者如何回应。你能提供更多细节吗? – 2012-04-13 17:29:10

+0

糟糕 - 我说的不完全正确 - 我使用ListCollectionView来进行排序。不确定是否会有所作为... – 2012-04-13 17:36:17

+0

@KJ在应用滤波器之前,将SelectedItem保持为临时变量并在之后再次设置。为了在你的ListView中生效,你必须使用OnPropertyChanged(“SelectedItem”)来使用nofity UI进行手动属性更改。考虑到你以前的SelectedItem不再在你的视图中,因为过滤器。在这种情况下,做CollectionView.MoveCurrentToFirst()。 – Dummy01 2012-04-14 05:49:10

9

如果您使用的MVVM,那么你需要确保你已经设置了绑定到视图模型所选择的项目和太有Mode=TwoWay ...和滚动的选择,我们必须使用上的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 = “{绑定路径=设置}”/>

现在,当过 “MySelectedItem” 属性在ViewModel设置更改时refelected列表将滚动。

变更通知

在视图模型应该调用INotifyProperty在你已经绑定到你的XAML,以便在视图模型的变化可以refelected查看属性的制定者改变...

在MVVM使用SelectionChanged事件

此外,在MVVM你唐太斯必须使用“SelectionChnaged事件”,你可以调用一个函数在MySelectedItem setter方法,也可以使用EventToCommand类明确的事件调用..

过滤

ColletionViewSource的谷歌使用像排序功能,filtering..etc

希望它可以帮助...

+0

感谢帖子 - 这非常有趣。然而,我认为我说这个问题做得不好。 “滚动选择”在我的代码中工作正常。请参阅我上面的编辑,希望有更好的描述。此外 - 我尝试了“双向”绑定,但它没有区别,因为选择更改已经正常工作。我需要回应的是过滤器的改变以及ListView的重新填充,以便将当前选择(仍然正确选择)返回到视图中。 – 2012-04-13 18:06:38

0

OK - 所以我发现2个解决方案的工作,但并不满足于100% :

1)使用ViewModel的Mediator模式通知视图过滤器已更改。该视图然后将调用当前选定项目上的ScrollToView。虽然我喜欢虚拟机到虚拟机通知的Mediator,但它在ViewModel和它的匹配View之间使用它不知何故感觉很脏。

2)的处理程序ListView的LayoutUpdated事件中当前选定的项目叫ScrollToView。沉重的,低效的 - 只是简单的不喜欢这个。

我不会以纪念这个回答在一个更好的解决方案的希望。只是为了好奇或者其他可能正在寻找类似问题的人在这里提出这个问题。

1

找到一些其他职位(Credit)的解决方案,附加属性绑定到的CollectionView过滤的计数:

附加属性:

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}" 
    ...> 

这似乎对大多数情况下工作正常=)

+0

ICollectionView有一个事件CollectionChanged,它在更改过滤器时触发。你可以在后面的代码中钩入它并滚动到ListView控件的SelectedItem。 – FreddyFlares 2017-03-15 23:32:36

相关问题