WPF附加的行为错误|在使用ItemsSource之前,Items集合必须为空

本文关键字:Items 之前 ItemsSource 集合 错误 WPF | 更新日期: 2023-09-27 17:58:53

我正试图编写一个mutli select树视图行为,但在这样做的时候,我得到了一个神秘的错误"在使用ItemsSource之前,Items集合必须为空。"

以下是我的xaml代码:

<Window x:Class="TreeView.Spike.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:Spike="clr-namespace:TreeView.Spike" 
         Title="MainWindow" Height="350" Width="525">
    <Window.Resources>
    </Window.Resources>
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width=".5*"/>
            <ColumnDefinition Width=".5*"/>
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition Height="*"></RowDefinition>
            <RowDefinition Height="40"></RowDefinition>
        </Grid.RowDefinitions>
        <TreeView ItemsSource="{Binding Nodes}" Grid.Row="0" x:Name="treeView" Grid.Column="0">
            <TreeView.ItemTemplate>
                <HierarchicalDataTemplate ItemsSource="{Binding Nodes}">
                    <StackPanel Orientation="Horizontal">
                        <TextBlock Text="{Binding Name}">                  
                    </TextBlock>
                    </StackPanel>
                </HierarchicalDataTemplate>
            </TreeView.ItemTemplate>
            <TreeView.ContextMenu>
                <ContextMenu>
                    <MenuItem Header="Add"></MenuItem>
                    <MenuItem Header="Delete"></MenuItem>
                </ContextMenu>
            </TreeView.ContextMenu>
            <Spike:MultipleItemSelectionAttachedBehavior AllSelectedItems="{Binding Path=AllSelectedNodes}"/>
        </TreeView>
    </Grid>
</Window>

我的附加行为:

 public class MultipleItemSelectionAttachedBehavior:Behavior<System.Windows.Controls.TreeView>
    {
        public static DependencyProperty AllSelectedItemsProperty =
        DependencyProperty.RegisterAttached("AllSelectedItems", typeof(object), typeof(MultipleItemSelectionAttachedBehavior),
    new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault | FrameworkPropertyMetadataOptions.Inherits));
        private static readonly PropertyInfo IsSelectionChangeActiveProperty = typeof(System.Windows.Controls.TreeView).GetProperty("IsSelectionChangeActive",
          BindingFlags.NonPublic | BindingFlags.Instance);
        public object AllSelectedItems
        {
            get
            {
                return (object)GetValue(AllSelectedItemsProperty);
            }
            set
            {
                SetValue(AllSelectedItemsProperty, value);
            }
        }
        public static bool GetAllSelectedItems(DependencyObject obj)
        {
            return (bool)obj.GetValue(AllSelectedItemsProperty);
        }
        public static void SetAllSelectedItems(DependencyObject obj, bool value)
        {
            obj.SetValue(AllSelectedItemsProperty, value);
        }
        protected override void OnAttached()
        {
            base.OnAttached();
            AssociatedObject.SelectedItemChanged += AssociatedObject_SelectedItemChanged;
        }
        protected override void OnDetaching()
        {
            base.OnDetaching();
            AssociatedObject.SelectedItemChanged -= AssociatedObject_SelectedItemChanged;
        }
        void AssociatedObject_SelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
        {
            if (IsSelectionChangeActiveProperty == null) return;
            var selectedItems = new List<Node>();
            var treeViewItem = AssociatedObject.SelectedItem as Node;
            if (treeViewItem == null) return;
            // allow multiple selection
            // when control key is pressed
            if (Keyboard.IsKeyDown(Key.LeftCtrl) || Keyboard.IsKeyDown(Key.RightCtrl))
            {
                var isSelectionChangeActive = IsSelectionChangeActiveProperty.GetValue(AssociatedObject, null);
                IsSelectionChangeActiveProperty.SetValue(AssociatedObject, true, null);
                selectedItems.ForEach(item => item.IsSelected = true);
                IsSelectionChangeActiveProperty.SetValue(AssociatedObject, isSelectionChangeActive, null);
            }
            else
            {
                // deselect all selected items except the current one
                selectedItems.ForEach(item => item.IsSelected = (item == treeViewItem));
                selectedItems.Clear();
            }
            if (!selectedItems.Contains(treeViewItem))
            {
                selectedItems.Add(treeViewItem);
            }
            else
            {
                // deselect if already selected
                treeViewItem.IsSelected = false;
                selectedItems.Remove(treeViewItem);
            }
            AllSelectedItems = selectedItems;
        }
    }

和我的ViewModel

public class ViewModel :NotificationObject
    {
        public ViewModel()
        {
            AllSelectedNodes = new ObservableCollection<Node>();
        }
 private ObservableCollection<Node> _allSelectedNodes;
        public ObservableCollection<Node> AllSelectedNodes
        {
            get { return _allSelectedNodes; }
            set
            {
                _allSelectedNodes = value;
                RaisePropertyChanged(() => AllSelectedNodes);   
            }
        }
}

我的型号:

public class Node:NotificationObject
    {
        private string _name;
        public string Name
        {
            get { return _name; }
            set
            {
                _name = value;
                RaisePropertyChanged(() => Name);   
            }
        }


        private bool _isExpanded = true;
        public bool IsExpanded
        {
            get { return _isExpanded; }
            set
            {
                _isExpanded = value;
                RaisePropertyChanged(() => IsExpanded); 
            }
        }
        private bool _isSelected;
        public bool IsSelected
        {
            get { return _isSelected; }
            set
            {
                _isSelected = value;
                RaisePropertyChanged(() => IsSelected);
            }
        }
        private ObservableCollection<Node> _nodes;
        public ObservableCollection<Node> Nodes
        {
            get { return _nodes; }
            set
            {
                _nodes = value;
                RaisePropertyChanged(() => Nodes);  
            }
        }
        public static IList<Node> Create()
        {
            return new List<Node>()
                       {
                           new Node()
                               {
                                   Name = "Activity",
                                   Nodes = new ObservableCollection<Node>()
                                               {
                                                   new Node() {Name = "Company",Nodes = new ObservableCollection<Node>(){  new Node() {Name = "Company1",Existing = false}}},
                                                     new Node() {Name = "Strategy",Nodes = new ObservableCollection<Node>(){  new Node() {Name = "Strategy1"}}},
                                                        new Node() {Name = "Vehicle",Nodes = new ObservableCollection<Node>(){  new Node() {Name = "Vehicle1",Existing = false}}}
                                               }
                               }
                       };
        }
    }

和我的初始化块:

   public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            var viewModel = new ViewModel();
            this.DataContext = viewModel;
            viewModel.Nodes = new ObservableCollection<Node>(Node.Create());
        }
}

我不知道这里出了什么问题,你能帮忙吗?

WPF附加的行为错误|在使用ItemsSource之前,Items集合必须为空

您缺少<i:Interaction.Behaviors>元素:

替换:

<Spike:MultipleItemSelectionAttachedBehavior AllSelectedItems="{Binding Path=AllSelectedNodes}"/>

用于:

<i:Interaction.Behaviors>
    <Spike:MultipleItemSelectionAttachedBehavior AllSelectedItems="{Binding Path=AllSelectedNodes}"/>
</i:Interaction.Behaviors>

问题是TreeView的默认内容属性是它的Items属性,因此,将行为的XAML元素放在TreeView元素内部而不指定<i:Interaction.Behaviors> Attached属性,是在告诉WPF,你希望你的行为作为TreeView中的项,因此,当试图设置其ItemsSource属性时,你会收到错误,因为里面已经有一个Item了。