c# - c# wpf TreeView 选择根项目的问题
问题描述
我对 C# 中的 WPF TreeView 有一个奇怪的问题。运行我的应用程序时,我想从选定的 TreeView 节点展开和折叠所有节点。当我选择一个根节点时, IsSelected 属性不会更新。当我手动展开根节点并单击子节点时,将更新 IsSelected 属性。问题是当我使用上下文菜单展开和折叠根节点上的子节点时,根节点没有展开。当我在使用上下文菜单后展开根节点时,我可以看到所有子节点都已正确展开。
我在互联网上搜索了很多,但找不到类似的问题。此外,我阅读了很多关于 C# WPF TreeViews 的教程和操作指南。到目前为止,我的实现在我看来是正确的,这就是为什么我不明白为什么单击节点时未设置根属性 IsSelected。
谁能帮我?我已经实现了 TreeView、ViewModel 和 Model,如下所示。提前致谢
BR
迈克尔
<TreeView
Grid.Row="1"
Grid.Column="0"
Margin="5"
ContextMenuOpening="FrameworkElement_OnContextMenuOpening"
ItemsSource="{Binding HierarchialTestObjects}"
SelectedItemChanged="TreeView_OnSelectedItemChanged">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate DataType="{x:Type vm:HierarchialTestObjectViewModel}" ItemsSource="{Binding Childs}">
<HierarchicalDataTemplate.ItemContainerStyle>
<Style TargetType="{x:Type TreeViewItem}">
<Setter Property="IsSelected" Value="{Binding Path=IsSelected, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
<Setter Property="IsExpanded" Value="{Binding Path=IsExpanded, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
</Style>
</HierarchicalDataTemplate.ItemContainerStyle>
<StackPanel Orientation="Horizontal">
<Label Content="{Binding Path=Name}" />
</StackPanel>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
<TreeView.ContextMenu>
<ContextMenu>
<MenuItem Command="{Binding ExpandAllTreeItemsCommand}" Header="Expand All" />
<MenuItem Command="{Binding CollapseAllTreeItemsCommand}" Header="Collapse All" />
</ContextMenu>
</TreeView.ContextMenu>
namespace mynamespace.subspace.report.data
{
// ... usings removed
public class HierarchialTestObject
{
public string Name { get; set; }
public List<HierarchialTestObject> Childs { get; set; } = new List<HierarchialTestObject>();
public List<string> Path { get; set; } = new List<string>();
public List<string> ParentPath => Path?.AsEnumerable()?.Reverse()?.Skip(1)?.Reverse()?.ToList();
public IHierarchialItem Item { get; set; }
public HierarchialTestObject(IHierarchialItem item)
{
Item = item;
Name = item.ShortDescription;
}
public void SetChilds(IEnumerable<IHierarchialItem> childs)
{
childs.ToList().ForEach(c => Childs?.Add(new HierarchialTestObject(c)));
}
}
}
视图模型
namespace mynamespace.subspace.report.viewmodel
{
// ... usings removed
public class HierarchialTestObjectViewModel : ViewModelBase
{
public delegate void NotifyTreeItemSelected(HierarchialTestObjectViewModel item);
public event NotifyTreeItemSelected OnTreeItemSelected;
private bool _isSelected;
public bool IsSelected
{
get { return _isSelected; }
set
{
if (_isSelected != value)
{
SetProperty(ref _isSelected, value, nameof(IsSelected));
if (value)
{
// this is a hack-ish workaround because i cannot use PRISM or other similar libs
OnTreeItemSelected?.Invoke(this);
}
}
}
}
private bool _isExpanded;
public bool IsExpanded
{
get { return _isExpanded; }
set
{
if (_isExpanded != value)
{
SetProperty(ref _isExpanded, value, nameof(IsExpanded));
RaisePropertyChanged(nameof(IconType));
}
}
}
private string _name;
public string Name
{
get { return _name; }
set
{
if (_name != value)
{
SetProperty(ref _name, value, nameof(Name));
}
}
}
private IHierarchialItem _item;
public IHierarchialItem Item
{
get { return _item; }
set
{
if (_item != value)
{
SetProperty(ref _item, value, nameof(Item));
}
}
}
private List<string> _path;
public List<string> Path
{
get { return _path; }
set
{
if (_path != value)
{
SetProperty(ref _path, value, nameof(Path));
}
}
}
private ObservableCollection<HierarchialTestObjectViewModel> _childs;
public ObservableCollection<HierarchialTestObjectViewModel> Childs
{
get { return _childs; }
set
{
if (_childs != value)
{
SetProperty(ref _childs, value, nameof(Childs));
}
}
}
public HierarchialTestObjectViewModel(HierarchialTestObject obj)
{
var childs = obj.Childs.Select(c => new HierarchialTestObjectViewModel(c)).ToList();
Path = obj.Path ?? new List<string>();
Childs = new ObservableCollection<HierarchialTestObjectViewModel>(childs);
Item = obj.Item;
Name = obj.Name;
}
public void ExpandRecursively()
{
IsExpanded = true;
Childs?.ToList().ForEach(c => c.ExpandRecursively());
}
public void CollapseRecursively()
{
IsExpanded = false;
Childs?.ToList().ForEach(c => c.CollapseRecursively());
}
}
}
解决方案
问题是您从未设置ItemContainerStyle
根项目的 。仅适用于子项。因此,根数据模型的IsExpanded
和IsSelected
属性没有连接到它们的TreeViewItem
数据容器。
只需设置TreeView.ItemContainerStyle
即可解决问题:
<TreeView.ItemContainerStyle>
<Style TargetType="{x:Type TreeViewItem}">
<Setter Property="IsSelected" Value="{Binding IsSelected}" />
<Setter Property="IsExpanded" Value="{Binding IsExpanded, Mode=TwoWay}" />
</Style>
</TreeView.ItemContainerStyle>
评论
TreeViewItem.IsSelected
默认配置为BindingMode.TwoWay
。
TreeViewItem.IsSelected
并将TreeViewItem.IsExpanded
它们Binding.UpdateSourceTrigger
设置为UpdateSourceTrigger.PropertyChanged
默认值,这是所有 Dependency
Property
的默认值,除非它们被显式设置为不同的触发器,只有少数属性是这种情况(例如默认TextBox.Text
设置为UpdateSourceTrigger.LostFocus
)。
这样可以减少代码并提高可读性。