将复选框添加到WPF中的现有Treeview
我在WPF中有一个现有的treeview,我想在其中添加复选框 这是密码 我有一个包含所有结构的类Person Person.cs将复选框添加到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
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;
}
}