将复选框添加到WPF中的现有Treeview

将复选框添加到WPF中的现有Treeview,wpf,checkbox,treeview,Wpf,Checkbox,Treeview,我在WPF中有一个现有的treeview,我想在其中添加复选框 这是密码 我有一个包含所有结构的类Person Person.cs public class Person { readonly List<Person> _children = new List<Person>(); public IList<Person> Children { get { return _chil

我在WPF中有一个现有的treeview,我想在其中添加复选框

这是密码

我有一个包含所有结构的类Person

Person.cs

public class Person
    {
        readonly List<Person> _children = new List<Person>();
        public IList<Person> Children
        {
            get { return _children; }
        }

        public string Name { get; set; }
    }
有人能帮我吗


提前感谢

我不确定我是否完全理解您的问题,但您是否尝试过像这样编辑XAML

    <HierarchicalDataTemplate ItemsSource="{Binding Children}">
      <StackPanel Orientation=Horizontal>
        <TextBlock Text="{Binding Name}" />
        <Checkbox IsChecked="{Binding IsSelected} />
      </StackPanel>
    </HierarchicalDataTemplate>

这是我的TreeView项的基本ViewModel类,其中包括级联检查

public class perTreeViewItemViewModelBase : perViewModelBase
{
    // a dummy item used in lazy loading mode, ensuring that each node has at least one child so that the expand button is shown
    private static perTreeViewItemViewModelBase LoadingDataItem { get; }

    static perTreeViewItemViewModelBase()
    {
        LoadingDataItem = new perTreeViewItemViewModelBase { Caption = "Loading Data ..." };
    }

    private readonly perObservableCollection<perTreeViewItemViewModelBase> _childrenList = new perObservableCollection<perTreeViewItemViewModelBase>();

    public perTreeViewItemViewModelBase(bool addLoadingDataItem = false)
    {
        if (addLoadingDataItem)
            _childrenList.Add(LoadingDataItem);
    }

    private string _caption;

    public string Caption
    {
        get { return _caption; }
        set { Set(nameof(Caption), ref _caption, value); }
    }

    public void ClearChildren()
    {
        _childrenList.Clear();
    }

    public void AddChild(perTreeViewItemViewModelBase child)
    {
        if (_childrenList.Any() && _childrenList.First() == LoadingDataItem)
            ClearChildren();

        _childrenList.Add(child);
        SetChildPropertiesFromParent(child);
    }

    protected void SetChildPropertiesFromParent(perTreeViewItemViewModelBase child)
    {
        child.Parent = this;

        if (IsChecked.GetValueOrDefault())
            child.IsChecked = true;
    }

    public void AddChildren(IEnumerable<perTreeViewItemViewModelBase> children)
    {
        foreach (var child in children)
            AddChild(child);
    }

    protected perTreeViewItemViewModelBase Parent { get; private set; }

    private bool? _isChecked = false;

    public bool? IsChecked
    {
        get { return _isChecked; }
        set
        {
            if (Set(nameof(IsChecked), ref _isChecked, value))
            {
                foreach (var child in Children)
                    if (child.IsEnabled)
                        child.SetIsCheckedIncludingChildren(value);

                SetParentIsChecked();
            }
        }
    }

    private bool _isExpanded;

    public bool IsExpanded
    {
        get { return _isExpanded; }
        set
        {
            if (!Set(nameof(IsExpanded), ref _isExpanded, value) || IsInitialised || IsInitialising)
                return;

            var unused = InitialiseAsync();
        }
    }

    private bool _isEnabled = true;

    public bool IsEnabled
    {
        get { return _isEnabled; }
        set { Set(nameof(IsEnabled), ref _isEnabled, value); }
    }

    public bool IsInitialising { get; private set; }
    public bool IsInitialised { get; private set; }

    public async Task InitialiseAsync()
    {
        if (IsInitialised || IsInitialising)
            return;

        IsInitialising = true;
        await InitialiseChildrenAsync().ConfigureAwait(false);
        foreach (var child in InitialisedChildren)
            SetChildPropertiesFromParent(child);
        IsInitialised = true;
        RaisePropertyChanged(nameof(Children));
    }

    protected virtual Task InitialiseChildrenAsync()
    {
        return Task.CompletedTask;
    }

    public IEnumerable<perTreeViewItemViewModelBase> Children => IsInitialised
        ? InitialisedChildren
        : _childrenList;

    // override this as required in descendent classes
    // e.g. if Children is a union of multiple child item collections which are populated in InitialiseChildrenAsync()
    protected virtual IEnumerable<perTreeViewItemViewModelBase> InitialisedChildren => _childrenList;

    private bool _isSelected;

    public bool IsSelected
    {
        get { return _isSelected; }
        set
        {
            // ensure that all ancestor items are expanded, so this item will be visible
            if (value)
            {
                var parent = Parent;
                while (parent != null)
                {
                    parent.IsExpanded = true;
                    parent = parent.Parent;
                }
            }

            if (_isSelected == value)
                return;

            // use DispatcherPriority.ContextIdle so that we wait for any children of newly expanded items to be fully created in the
            // parent TreeView, before setting IsSelected for this item (which will scroll it into view - see perTreeViewItemHelper)
            perDispatcherHelper.CheckBeginInvokeOnUI(() => Set(nameof(IsSelected), ref _isSelected, value), DispatcherPriority.ContextIdle);

            // note that by rule, a TreeView can only have one selected item, but this is handled automatically by 
            // the control - we aren't required to manually unselect the previously selected item.
        }
    }

    private void SetIsCheckedIncludingChildren(bool? value)
    {
        _isChecked = value;
        RaisePropertyChanged(nameof(IsChecked));

        foreach (var child in Children)
            if (child.IsEnabled)
                child.SetIsCheckedIncludingChildren(value);
    }

    private void SetIsCheckedThisItemOnly(bool? value)
    {
        _isChecked = value;
        RaisePropertyChanged(nameof(IsChecked));
    }

    private void SetParentIsChecked()
    {
        var parent = Parent;

        while (parent != null)
        {
            var hasIndeterminateChild = parent.Children.Any(c => c.IsEnabled && !c.IsChecked.HasValue);

            if (hasIndeterminateChild)
                parent.SetIsCheckedThisItemOnly(null);
            else
            {
                var hasSelectedChild = parent.Children.Any(c => c.IsEnabled && c.IsChecked.GetValueOrDefault());
                var hasUnselectedChild = parent.Children.Any(c => c.IsEnabled && !c.IsChecked.GetValueOrDefault());

                if (hasUnselectedChild && hasSelectedChild)
                    parent.SetIsCheckedThisItemOnly(null);
                else
                    parent.SetIsCheckedThisItemOnly(hasSelectedChild);
            }

            parent = parent.Parent;
        }
    }

    public override string ToString()
    {
        return Caption;
    }
}

有关详细信息及其用法示例,请参阅我最近的。

谢谢您的帮助。这有效地允许我添加复选框。现在在代码级别,我们如何管理选择。选择父项,选中所有子项。只检查一个孩子会使家长处于不确定状态。修改了我的答案!
<TreeView ItemsSource="{Binding FirstGeneration}">
    <TreeView.ItemContainerStyle>
        <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.ItemTemplate>
        <HierarchicalDataTemplate ItemsSource="{Binding Children}">
            <TextBlock Text="{Binding Name}" />

        </HierarchicalDataTemplate>
    </TreeView.ItemTemplate>
</TreeView>
  public partial class MainWindow : Window
    {
        readonly FamilyTreeViewModel _familyTree;

        public MainWindow()
        {
            InitializeComponent();
            Person rootPerson = new Person
            {
                Name="Application Architect Right",
                Children =
                {
                    new Person
                    {
                        Name="Generate"
                    },
                    new Person
                    {
                        Name="Instances rights",
                        Children =
                        {
                            new Person
                            {
                                Name = "Create"
                            },
                            new Person
                            {
                                Name = "Modify"
                            },
                            new Person
                            {
                                Name = "Delete"
                            },
                            new Person
                            {
                                Name = "Exceptions Management"
                            }
                        }

                    },
                    new Person
                    {
                        Name="Templates rights",
                        Children =
                        {
                            new Person
                            {
                                Name = "Create"
                            },
                            new Person
                            {
                                Name = "Modify"
                            },
                            new Person
                            {
                                Name = "Delete"
                            }
                        }
                    },
                    new Person
                    {
                        Name="Parameters rights",
                        Children =
                        {
                            new Person
                            {
                                Name = "Create"
                            },
                            new Person
                            {
                                Name = "Modify"
                            },
                            new Person
                            {
                                Name = "Delete"
                            }
                        }
                    },
                }
            };


            // Create UI-friendly wrappers around the 
            // raw data objects (i.e. the view-model).
            _familyTree = new FamilyTreeViewModel(rootPerson);


            // Let the UI bind to the view-model.
            DataContext = _familyTree;

        }
    }
    <HierarchicalDataTemplate ItemsSource="{Binding Children}">
      <StackPanel Orientation=Horizontal>
        <TextBlock Text="{Binding Name}" />
        <Checkbox IsChecked="{Binding IsSelected} />
      </StackPanel>
    </HierarchicalDataTemplate>
public bool IsSelected
{
   ...
    set
    {
        if (value != _isSelected)
        {
            _isSelected = value;
            OnPropertyChanged("IsSelected");
            this.UpdateChildSelection();
        }
    }
}


private void UpdateChildSelection()
{
    foreach(var child in Children)
    {
        child.IsSelected = this.IsSelected;
    }
}
public class perTreeViewItemViewModelBase : perViewModelBase
{
    // a dummy item used in lazy loading mode, ensuring that each node has at least one child so that the expand button is shown
    private static perTreeViewItemViewModelBase LoadingDataItem { get; }

    static perTreeViewItemViewModelBase()
    {
        LoadingDataItem = new perTreeViewItemViewModelBase { Caption = "Loading Data ..." };
    }

    private readonly perObservableCollection<perTreeViewItemViewModelBase> _childrenList = new perObservableCollection<perTreeViewItemViewModelBase>();

    public perTreeViewItemViewModelBase(bool addLoadingDataItem = false)
    {
        if (addLoadingDataItem)
            _childrenList.Add(LoadingDataItem);
    }

    private string _caption;

    public string Caption
    {
        get { return _caption; }
        set { Set(nameof(Caption), ref _caption, value); }
    }

    public void ClearChildren()
    {
        _childrenList.Clear();
    }

    public void AddChild(perTreeViewItemViewModelBase child)
    {
        if (_childrenList.Any() && _childrenList.First() == LoadingDataItem)
            ClearChildren();

        _childrenList.Add(child);
        SetChildPropertiesFromParent(child);
    }

    protected void SetChildPropertiesFromParent(perTreeViewItemViewModelBase child)
    {
        child.Parent = this;

        if (IsChecked.GetValueOrDefault())
            child.IsChecked = true;
    }

    public void AddChildren(IEnumerable<perTreeViewItemViewModelBase> children)
    {
        foreach (var child in children)
            AddChild(child);
    }

    protected perTreeViewItemViewModelBase Parent { get; private set; }

    private bool? _isChecked = false;

    public bool? IsChecked
    {
        get { return _isChecked; }
        set
        {
            if (Set(nameof(IsChecked), ref _isChecked, value))
            {
                foreach (var child in Children)
                    if (child.IsEnabled)
                        child.SetIsCheckedIncludingChildren(value);

                SetParentIsChecked();
            }
        }
    }

    private bool _isExpanded;

    public bool IsExpanded
    {
        get { return _isExpanded; }
        set
        {
            if (!Set(nameof(IsExpanded), ref _isExpanded, value) || IsInitialised || IsInitialising)
                return;

            var unused = InitialiseAsync();
        }
    }

    private bool _isEnabled = true;

    public bool IsEnabled
    {
        get { return _isEnabled; }
        set { Set(nameof(IsEnabled), ref _isEnabled, value); }
    }

    public bool IsInitialising { get; private set; }
    public bool IsInitialised { get; private set; }

    public async Task InitialiseAsync()
    {
        if (IsInitialised || IsInitialising)
            return;

        IsInitialising = true;
        await InitialiseChildrenAsync().ConfigureAwait(false);
        foreach (var child in InitialisedChildren)
            SetChildPropertiesFromParent(child);
        IsInitialised = true;
        RaisePropertyChanged(nameof(Children));
    }

    protected virtual Task InitialiseChildrenAsync()
    {
        return Task.CompletedTask;
    }

    public IEnumerable<perTreeViewItemViewModelBase> Children => IsInitialised
        ? InitialisedChildren
        : _childrenList;

    // override this as required in descendent classes
    // e.g. if Children is a union of multiple child item collections which are populated in InitialiseChildrenAsync()
    protected virtual IEnumerable<perTreeViewItemViewModelBase> InitialisedChildren => _childrenList;

    private bool _isSelected;

    public bool IsSelected
    {
        get { return _isSelected; }
        set
        {
            // ensure that all ancestor items are expanded, so this item will be visible
            if (value)
            {
                var parent = Parent;
                while (parent != null)
                {
                    parent.IsExpanded = true;
                    parent = parent.Parent;
                }
            }

            if (_isSelected == value)
                return;

            // use DispatcherPriority.ContextIdle so that we wait for any children of newly expanded items to be fully created in the
            // parent TreeView, before setting IsSelected for this item (which will scroll it into view - see perTreeViewItemHelper)
            perDispatcherHelper.CheckBeginInvokeOnUI(() => Set(nameof(IsSelected), ref _isSelected, value), DispatcherPriority.ContextIdle);

            // note that by rule, a TreeView can only have one selected item, but this is handled automatically by 
            // the control - we aren't required to manually unselect the previously selected item.
        }
    }

    private void SetIsCheckedIncludingChildren(bool? value)
    {
        _isChecked = value;
        RaisePropertyChanged(nameof(IsChecked));

        foreach (var child in Children)
            if (child.IsEnabled)
                child.SetIsCheckedIncludingChildren(value);
    }

    private void SetIsCheckedThisItemOnly(bool? value)
    {
        _isChecked = value;
        RaisePropertyChanged(nameof(IsChecked));
    }

    private void SetParentIsChecked()
    {
        var parent = Parent;

        while (parent != null)
        {
            var hasIndeterminateChild = parent.Children.Any(c => c.IsEnabled && !c.IsChecked.HasValue);

            if (hasIndeterminateChild)
                parent.SetIsCheckedThisItemOnly(null);
            else
            {
                var hasSelectedChild = parent.Children.Any(c => c.IsEnabled && c.IsChecked.GetValueOrDefault());
                var hasUnselectedChild = parent.Children.Any(c => c.IsEnabled && !c.IsChecked.GetValueOrDefault());

                if (hasUnselectedChild && hasSelectedChild)
                    parent.SetIsCheckedThisItemOnly(null);
                else
                    parent.SetIsCheckedThisItemOnly(hasSelectedChild);
            }

            parent = parent.Parent;
        }
    }

    public override string ToString()
    {
        return Caption;
    }
}