AttachedProperty 与在 ViewModel 中获取 SelectedItems 的行为

AttachedProperty vs behavior to get SelectedItems in the ViewModel

我必须选择在 ViewModel 中获取 SelectedItems。

附上属性这样的:

public class ListBoxSelectedItemsAttachedProperty
    {
        #region SelectedItems
        ///
        /// SelectedItems Attached Dependency Property
        ///
        public static readonly DependencyProperty SelectedItemsProperty =
        DependencyProperty.RegisterAttached("SelectedItems", typeof(IList),
        typeof(ListBoxSelectedItemsAttachedProperty),
        new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault,
        new PropertyChangedCallback(OnSelectedItemsChanged)));

        public static IList GetSelectedItems(DependencyObject d)
        {
            return (IList)d.GetValue(SelectedItemsProperty);
        }

        public static void SetSelectedItems(DependencyObject d, IList value)
        {
            d.SetValue(SelectedItemsProperty, value);
        }

        private static void OnSelectedItemsChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            ListBox miLb = (ListBox)d;
            miLb.SelectionChanged += listBox_SelectionChanged;
        }

        private static void listBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            ListBox miLg = (ListBox)sender;
            //Get list box's selected items.
            IEnumerable miDgSelectedItems = miLg.SelectedItems;
            //Get list from model
            IList ModelSelectedItems = GetSelectedItems(miLg);

            //Update the model
            ModelSelectedItems.Clear();

            if (miLg.SelectedItems != null)
            {
                foreach (var item in miLg.SelectedItems)
                    ModelSelectedItems.Add(item);
            }
            SetSelectedItems(miLg, ModelSelectedItems);
        }
        #endregion
    }

并且在axml中是这样使用的,例如在一个Listbox中:

Behaviors:ListBoxSelectedItemsAttachedProperty.SelectedItems="{Binding MyPropertyInViewModel}"

附加行为:

public class SelectedItemsBehavior : Behavior<MultiSelector>
    {
        protected override void OnAttached()
        {
            AssociatedObject.SelectionChanged += AssociatedObjectSelectionChanged;
        }
        protected override void OnDetaching()
        {
            AssociatedObject.SelectionChanged -= AssociatedObjectSelectionChanged;
        }

        void AssociatedObjectSelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            List<object> selectedItemList = AssociatedObject.SelectedItems.Cast<object>().ToList();
            ObservableCollection<object> selectedItems = new ObservableCollection<object>(selectedItemList);
            SelectedItems = selectedItems;
        }
        public ObservableCollection<object> SelectedItems
        {
            get { return (ObservableCollection<object>)GetValue(SelectedItemsProperty); }
            set { SetValue(SelectedItemsProperty, value); }
        }
        public static readonly DependencyProperty SelectedItemsProperty =
            DependencyProperty.Register("SelectedItems"
                , typeof(ObservableCollection<object>)
                , typeof(SelectedItemsBehavior)
                ,
            new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));
    }

而在axml中就是这样使用的,比如在一个DataGrid中:

<i:Interaction.Behaviors>
    <Behaviors:SelectedItemsBehavior SelectedItems="{Binding MyPropertyInViewModel}" />
</i:Interaction.Behaviors>

但我真的不知道附加 属性 和附加行为之间的区别,以及在 ViewModel 中获取 SelectedItems 的最佳选择是什么。

谢谢。

在 WPF 中实现附加行为基本上有两种不同的方法。您可以创建附加的 属性 并对其应用 PropertyChangedCallback 以在依赖项 属性 的值时对其附加的 DependencyObject 执行某些操作或扩展它所附加的 DependencyObject变化。

另一种方法是创建一个从 System.Windows.Interactivity.Behavior<T> 派生的 class。这通常称为 "Blend" 行为,与创建带有回调的附加 属性 相比,它提供了一种更好的封装行为功能的方法。 Blend 行为也更容易设计友好,因为它们可以通过 Blend 中的拖放功能轻松附加到 UI 中的视觉元素,并且它们还提供了一种使用 OnAttachedOnDetaching 方法。主要缺点是您不能在样式设置器中应用这些行为。

因此,如果您需要能够在 Style 中附加您的行为,请使用附加的 属性。否则我宁愿使用混合行为。如果您有兴趣,这里有一个如何绑定到可用的只读属性的示例 on this blog