gpt4 book ai didi

.net - 将多选 ListBox 与 MVVM 同步

转载 作者:行者123 更新时间:2023-12-03 13:29:01 24 4
gpt4 key购买 nike

我有一些数据的两个 View :一个 ListView (现在是 ListBox,但我一直想切换到 ListView)和 map 上的精美图形表示。在任一 View 中,用户都可以单击一个对象,它将在两个 View 中被选中。多选也是可能的,所以每个 ViewModel实例有自己的IsSelected属性(property)。

目前我正在绑定(bind)ListBoxItem.IsSelectedViewModel.IsSelected ,但这只有在 ListBox不是虚拟化(see here)。不幸的是,禁用虚拟化会损害性能,并且我的应用程序变得太慢了。

所以我必须再次启用虚拟化。为了维护ViewModel.IsSelected屏幕外项目的属性,我注意到 ListBoxListView有一个SelectionChanged我可以(大概)用来从 ListBox/ListView 传播选择状态的事件到ViewModel .

我的问题是,如何反向传播选择状态? SelectedItems ListBox/ListView 的属性(property)是只读的!假设用户单击图形表示中的一个项目,但它在屏幕外 w.r.t.名单。如果我只是设置 ViewModel.IsSelected然后 ListBox/ListView将不知道新的选择,因此如果用户单击列表中的其他项目,它将无法取消选择该项目。我可以调用ListBox.ScrollIntoView来自 ViewModel ,但有几个问题:

  • 在我的 UI 中,如果它们以图形方式位于同一位置,实际上可以一键选择两个项目,尽管它们可能位于 ListBox/ListView 中完全不同的位置。 .
  • 它打破了 ViewModel 的隔离(我的 ViewModel 完全不知道 WPF,我想保持这种状态。)

  • 那么,亲爱的 WPF 专家,有什么想法吗?

    编辑:我最终切换到 Infragistics 控件并使用了一个丑陋且相当缓慢的解决方案。关键是,我不再需要答案了。

    最佳答案

    您可以创建 Behavior同步 ListBox.SelectedItems在 ViewModel 中有一个集合:

    public class MultiSelectionBehavior : Behavior<ListBox>
    {
    protected override void OnAttached()
    {
    base.OnAttached();
    if (SelectedItems != null)
    {
    AssociatedObject.SelectedItems.Clear();
    foreach (var item in SelectedItems)
    {
    AssociatedObject.SelectedItems.Add(item);
    }
    }
    }

    public IList SelectedItems
    {
    get { return (IList)GetValue(SelectedItemsProperty); }
    set { SetValue(SelectedItemsProperty, value); }
    }

    public static readonly DependencyProperty SelectedItemsProperty =
    DependencyProperty.Register("SelectedItems", typeof(IList), typeof(MultiSelectionBehavior), new UIPropertyMetadata(null, SelectedItemsChanged));

    private static void SelectedItemsChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
    {
    var behavior = o as MultiSelectionBehavior;
    if (behavior == null)
    return;

    var oldValue = e.OldValue as INotifyCollectionChanged;
    var newValue = e.NewValue as INotifyCollectionChanged;

    if (oldValue != null)
    {
    oldValue.CollectionChanged -= behavior.SourceCollectionChanged;
    behavior.AssociatedObject.SelectionChanged -= behavior.ListBoxSelectionChanged;
    }
    if (newValue != null)
    {
    behavior.AssociatedObject.SelectedItems.Clear();
    foreach (var item in (IEnumerable)newValue)
    {
    behavior.AssociatedObject.SelectedItems.Add(item);
    }

    behavior.AssociatedObject.SelectionChanged += behavior.ListBoxSelectionChanged;
    newValue.CollectionChanged += behavior.SourceCollectionChanged;
    }
    }

    private bool _isUpdatingTarget;
    private bool _isUpdatingSource;

    void SourceCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
    {
    if (_isUpdatingSource)
    return;

    try
    {
    _isUpdatingTarget = true;

    if (e.OldItems != null)
    {
    foreach (var item in e.OldItems)
    {
    AssociatedObject.SelectedItems.Remove(item);
    }
    }

    if (e.NewItems != null)
    {
    foreach (var item in e.NewItems)
    {
    AssociatedObject.SelectedItems.Add(item);
    }
    }

    if (e.Action == NotifyCollectionChangedAction.Reset)
    {
    AssociatedObject.SelectedItems.Clear();
    }
    }
    finally
    {
    _isUpdatingTarget = false;
    }
    }

    private void ListBoxSelectionChanged(object sender, SelectionChangedEventArgs e)
    {
    if (_isUpdatingTarget)
    return;

    var selectedItems = this.SelectedItems;
    if (selectedItems == null)
    return;

    try
    {
    _isUpdatingSource = true;

    foreach (var item in e.RemovedItems)
    {
    selectedItems.Remove(item);
    }

    foreach (var item in e.AddedItems)
    {
    selectedItems.Add(item);
    }
    }
    finally
    {
    _isUpdatingSource = false;
    }
    }

    }

    可以使用此行为,如下所示:
            <ListBox ItemsSource="{Binding Items}"
    DisplayMemberPath="Name"
    SelectionMode="Extended">
    <i:Interaction.Behaviors>
    <local:MultiSelectionBehavior SelectedItems="{Binding SelectedItems}" />
    </i:Interaction.Behaviors>
    </ListBox>

    (注意你的 ViewModel 中的 SelectedItems 集合必须被初始化;行为不会设置它,它只会改变它的内容)

    关于.net - 将多选 ListBox 与 MVVM 同步,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8088595/

    24 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com