我在C#中使用WPF TreeView遇到一个奇怪的问题。运行我的应用程序时,我想展开和折叠所选TreeView节点中的所有节点。当我选择根节点时,IsSelected属性不会更新。当我手动展开根节点并单击子节点时,IsSelected属性将更新。问题是,当我使用上下文菜单来展开和折叠根节点上的子节点时,根节点不会被展开。使用上下文菜单后,我展开根节点时,可以看到所有子节点均已正确展开。
我在互联网上进行了很多搜索,但找不到类似的问题。此外,我在C#WPF TreeViews上阅读了很多教程和howtos。到目前为止,我的实现对我而言似乎是正确的,这就是为什么我不理解为什么在单击节点时未设置根属性IsSelected的原因。
有人可以帮助我吗?我已经实现了TreeView,ViewModel和Model如下。在此先感谢
BR
Michael
<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)));
}
}
}
ViewModel
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.IsSelected
的UpdateSourceTrigger
设置为PropertyChanged
。这样,您可以减少代码并提高可读性。