我在WPF中已有一个树状视图,我想在其中添加复选框
代码在这里
我有一个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; }
}
在其他一些文章中,我使用ViewModel
PersonViewModel.cs
public class PersonViewModel : INotifyPropertyChanged
{
#region Data
readonly ReadOnlyCollection<PersonViewModel> _children;
readonly PersonViewModel _parent;
readonly Person _person;
bool _isExpanded=true;
bool _isSelected;
#endregion Data
#region Constructors
public PersonViewModel(Person person): this(person, null)
{
}
private PersonViewModel(Person person, PersonViewModel parent)
{
_person = person;
_parent = parent;
_children = new ReadOnlyCollection<PersonViewModel>(
(from child in _person.Children
select new PersonViewModel(child, this))
.ToList<PersonViewModel>());
}
#endregion Constructors
#region Person Properties
public ReadOnlyCollection<PersonViewModel> Children
{
get { return _children; }
}
public string Name
{
get { return _person.Name; }
}
#endregion Person Properties
#region Presentation Members
#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;
OnPropertyChanged("IsExpanded");
}
// Expand all the way up to the root.
if (_isExpanded && _parent != null)
_parent.IsExpanded = true;
}
}
#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;
OnPropertyChanged("IsSelected");
}
}
}
#endregion IsSelected
#region NameContainsText
public bool NameContainsText(string text)
{
if (String.IsNullOrEmpty(text) || String.IsNullOrEmpty(this.Name))
return false;
return Name.IndexOf(text, StringComparison.InvariantCultureIgnoreCase) > -1;
}
#endregion NameContainsText
#region Parent
public PersonViewModel Parent
{
get { return _parent; }
}
#endregion Parent
#endregion Presentation Members
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
#endregion INotifyPropertyChanged Members
}
家谱ViewModel
FamilyTreeViewModel.cs
公共类FamilyTreeViewModel {
#region Data
readonly PersonViewModel _rootPerson;
#endregion Data
#region Constructor
public FamilyTreeViewModel(Person rootPerson)
{
_rootPerson = new PersonViewModel(rootPerson);
FirstGeneration = new ReadOnlyCollection<PersonViewModel>(
new PersonViewModel[]
{
_rootPerson
});
}
#endregion Constructor
#region Properties
#region FirstGeneration
/// <summary>
/// Returns a read-only collection containing the first person
/// in the family tree, to which the TreeView can bind.
/// </summary>
public ReadOnlyCollection<PersonViewModel> FirstGeneration { get; }
#endregion FirstGeneration
#endregion Properties
}
xaml代码 MainWindow.xaml
<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>
MainWindow.xaml.cs
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;
}
}
有人可以帮助我吗?
预先感谢
答案 0 :(得分:1)
我不确定我是否完全理解您的问题,但是您是否尝试过像这样编辑XAML?
<HierarchicalDataTemplate ItemsSource="{Binding Children}">
<StackPanel Orientation=Horizontal>
<TextBlock Text="{Binding Name}" />
<Checkbox IsChecked="{Binding IsSelected} />
</StackPanel>
</HierarchicalDataTemplate>
由于默认情况下wpf的树视图中没有复选框,因此编辑项目模板以添加复选框是一种方法。
由于该复选框已绑定到PersonViewModel的IsSelected属性,因此如果要更新子项的选择,可以执行类似的操作
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;
}
}
答案 1 :(得分:1)
这是我的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;
}
}
有关更多详细信息及其用法示例,请参阅我最近的blog post。