我的treeView填充了一个奇怪的孩子:WPF MVVM

时间:2015-10-08 23:10:46

标签: c# wpf xaml mvvm treeview

我在这个非常好的article的帮助下构建了一个treeView WPF MVVM 然后我为某个节点创建了一个contextMenu,允许我从选定的父节点添加子节点。

问题是如果我点击"添加"如果不手动扩展所选节点(父节点),除了在点击"添加"时预期生成的节点之外,还会自动创建一个奇怪的子节点。

我尝试检测到问题,因此我更改了以下代码:

<Setter Property="IsExpanded" Value="{Binding IsExpanded, Mode=TwoWay}" />

为:

<Setter Property="IsExpanded" Value="True" />

下面的图1显示了此测试的结果,或者图2显示了我的treeView必须显示的内容。

  • image1的 enter image description here

  • IMAGE2

enter image description here

Rq:我使用了我所谈到的article中的图片。另外,我使用了本文中描述的相同方法(包括类TreeViewItemViewModel.cs)

  • 所有ViewModel的基类

    公共类TreeViewItemViewModel:INotifyPropertyChanged {     #region数据

    static readonly TreeViewItemViewModel DummyChild = new TreeViewItemViewModel();
    
    readonly ObservableCollection<TreeViewItemViewModel> _children;
    readonly TreeViewItemViewModel _parent;
    
    bool _isExpanded;
    bool _isSelected;
    
    #endregion // Data
    
    #region Constructors
    
    protected TreeViewItemViewModel(TreeViewItemViewModel parent, bool lazyLoadChildren)
    {
        _parent = parent;
    
        _children = new ObservableCollection<TreeViewItemViewModel>();
    
        if (lazyLoadChildren)
            _children.Add(DummyChild);
    }
    
    // This is used to create the DummyChild instance.
    private TreeViewItemViewModel()
    {
    }
    
    #endregion // Constructors
    
    #region Presentation Members
    
    #region Children
    
    /// <summary>
    /// Returns the logical child items of this object.
    /// </summary>
    public ObservableCollection<TreeViewItemViewModel> Children
    {
        get { return _children; }
    }
    
    #endregion // Children
    
    #region HasLoadedChildren
    
    /// <summary>
    /// Returns true if this object's Children have not yet been populated.
    /// </summary>
    public bool HasDummyChild
    {
        get { return this.Children.Count == 1 && this.Children[0] == DummyChild; }
    }
    
    #endregion // HasLoadedChildren
    
    #region IsExpanded
    
    /// <summary>
    /// Gets/sets whether the TreeViewItem 
    /// associated with this object is expanded.
    /// </summary>
    public bool IsExpanded
    {
        get { return _isExpanded; }
        set
        {
            if (value != _isExpanded)
            {
                _isExpanded = value;
                this.OnPropertyChanged("IsExpanded");
            }
    
            // Expand all the way up to the root.
            if (_isExpanded && _parent != null)
                _parent.IsExpanded = true;
    
            // Lazy load the child items, if necessary.
            if (this.HasDummyChild)
            {
                this.Children.Remove(DummyChild);
                this.LoadChildren();
            }
        }
    }
    
    #endregion // IsExpanded
    
    #region IsSelected
    
    /// <summary>
    /// Gets/sets whether the TreeViewItem 
    /// associated with this object is selected.
    /// </summary>
    public bool IsSelected
    {
        get { return _isSelected; }
        set
        {
            if (value != _isSelected)
            {
                _isSelected = value;
                this.OnPropertyChanged("IsSelected");
            }
        }
    }
    
    #endregion // IsSelected
    
    #region LoadChildren
    
    /// <summary>
    /// Invoked when the child items need to be loaded on demand.
    /// Subclasses can override this to populate the Children collection.
    /// </summary>
    protected virtual void LoadChildren()
    {
    }
    
    #endregion // LoadChildren
    
    #region Parent
    
    public TreeViewItemViewModel Parent
    {
        get { return _parent; }
    }
    
    #endregion // Parent
    
    #endregion // Presentation Members
    
    #region INotifyPropertyChanged Members
    
    public event PropertyChangedEventHandler PropertyChanged;
    
    protected virtual void OnPropertyChanged(string propertyName)
    {
        if (this.PropertyChanged != null)
            this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
    }
    
    #endregion // INotifyPropertyChanged Members
    

    }

  • Myxml:

<TreeView ItemsSource="{Binding Regions}" IsEnabled="{Binding EnableTree}" > <TreeView.ItemContainerStyle> <!-- This Style binds a TreeViewItem to a TreeViewItemViewModel. --> <Style TargetType="{x:Type TreeViewItem}"> <Setter Property="IsExpanded" Value="{Binding IsExpanded, Mode=TwoWay}" /> <Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}" /> <Setter Property="FontWeight" Value="Normal" /> <Style.Triggers> <Trigger Property="IsSelected" Value="True"> <Setter Property="FontWeight" Value="Bold" /> </Trigger> </Style.Triggers> </Style> </TreeView.ItemContainerStyle> <TreeView.Resources> <ContextMenu x:Key="AddCity" ItemsSource="{Binding AddCityItems}"/> <HierarchicalDataTemplate DataType="{x:Type local:StateViewModel}" ItemsSource="{Binding Children}" > <StackPanel Orientation="Horizontal" ContextMenu="{StaticResource AddCity}"> <Image Width="16" Height="16" Margin="3,0" Source="Images\Region.png" /> <TextBlock Text="{Binding RegionName}" /> </StackPanel> </HierarchicalDataTemplate> </TreeView.Resources>

  • RegionViewModel:

`public class StateViewModel:TreeViewItemViewModel     {

   readonly State _state;
   public ICommand AddCityCommand { get; private set; }
   public List<MenuItem> AddCityItems { get; set; }

    public StateViewModel(State state, RegionViewModel parentRegion)
        : base(parentRegion, true)
    {
        _state = state;
        AddCityItems = new List<MenuItem>();
        AddCityCommand = new DelegateCommand<CancelEventArgs>(OnAddCityCommandExecute, OnAddCityCommandCanExecute);
        AddCityItems.Add(new MenuItem() { Header = "Add City", Command = AddCityCommand });
    }

    public string StateName
    {
        get { return _state.StateName; }
    }

    protected override void LoadChildren()
    {
        foreach (City city in Database.GetCities(_state))
            base.Children.Add(new CityViewModel(city, this));
    }


    bool OnAddCityCommandCanExecute(CancelEventArgs parameter)
    {
        return true;
    }

    public void OnAddCityCommandExecute(CancelEventArgs parameter)
    {
        var myNewCity = new city();
        Children.Add(new CityViewModel(myNewCity, this));
    }
}`
顺便说一句,如果我展开我的父节点,然后点击添加城市,我得到了预期的结果但是如果我不扩展父节点并且我点击contextMenu我除了孩子之外还创建了另一个孩子我想创建

修改 我将下面的statemnt添加到我的add()方法中,现在我没有任何问题:

public void OnAddCityCommandExecute(CancelEventArgs parameter)
    {
        var myNewCity = new city();
        Children.Add(new CityViewModel(myNewCity, this));
        //the modif
        this.Children.Remove(DummyChild);
    }

1 个答案:

答案 0 :(得分:1)

我可以在你的代码中看到错误。

以下是重现的步骤:

  1. 在状态节点(从不首先展开它)
  2. 未预先扩展子项,StateViewModel的子项包含DummyChild。
  3. 在列表中添加了1个新城市,导致HasDummyChild无效,因为儿童列表中的计数现在为2
  4. 然后,当您尝试展开节点以检查结果时。您的树状人员将拥有DummyChild,这是一个搞砸了所有内容的基类
  5. 所以,基本上这就是为什么“扩展”首先是你的问题的关键,因为当时HasDummyChild仍在工作,因为它比较.Count == 1.如果添加,树将不会从你的子列表中删除DummyChild列表中的额外子项使.Count == 2.

    根据要求提供其他信息

    只需将HasDummyChild更改为以下

    即可
        public bool HasDummyChild
        {
            //get { return this.Children.Count == 1 && this.Children[0] == DummyChild; }
            get { return Children.Any() && Children.Contains(DummyChild); }
        }
    

    enter image description here