Synchronizing multi-select ListBox with MVVM

后端 未结 1 1805
故里飘歌
故里飘歌 2020-12-13 10:23

I have two views of some data: a list view (a ListBox now, but I\'ve been meaning to switch to ListView) and a fancy graphical representation on a

1条回答
  •  时光取名叫无心
    2020-12-13 10:53

    You can create a Behavior that synchronizes ListBox.SelectedItems with a collection in your ViewModel:

    public class MultiSelectionBehavior : Behavior
    {
        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;
            }
        }
    
    }
    

    This behavior can be used as shown below:

            
                
                    
                
            
    

    (note that the SelectedItems collection in your ViewModel has to be initialized; the behavior won't set it, it will only change its content)

    0 讨论(0)
提交回复
热议问题