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

时间:2013-02-12 16:08:17

标签: c# wpf

我正在尝试编写一个mutli select treeview行为,但是这样做时我会收到这个神秘的错误“在使用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}}}
                                               }
                               }
                       };
        }
    }

..和我的初始化clode:

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

        }
}

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

1 个答案:

答案 0 :(得分:3)

您错过了<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中的Item,因此在尝试设置其ItemsSource属性时,您会收到错误,因为其中已有一个Item。