WPF TreeView 不显示箭头,除非我在运行时修改样式
WPF TreeView doesn't display arrows unless I modify styling during runtime
我有一个在运行时填充的 ObservableCollection,它绑定到 TreeView。更新集合时,集合的根 object 出现在 TreeView 中,无法展开它(见第一张图片)。
我认为这意味着绑定存在问题,但是,删除 TreeView.ItemCotainerStyle 标记会使箭头出现并且一切都按预期工作(参见第二张图片)。此行为也反向工作,如果样式标签不在视图中并且我在更新集合后添加它,那么箭头将出现。
样式标签对于我的项目中的任何功能来说都不是必需的,它们只是我正在处理的示例中遗留下来的。
<TreeView ItemsSource="{Binding CompileMessages}">
<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>
</TreeView.ItemContainerStyle>
<TreeView.Resources>
<HierarchicalDataTemplate DataType="{x:Type models:CompileMessagesDto}"
ItemsSource="{Binding Path=Children}">
<StackPanel Orientation="Horizontal">
<materialDesign:PackIcon Kind="{Binding Path=State}"
Margin="3"
Foreground="White" />
<TextBlock Text="{Binding Path=Path}"
FontWeight="Normal"
Foreground="White"
FontSize="12"
Margin="3" />
<TextBlock Text="{Binding Path=Description}"
FontWeight="Normal"
FontSize="12"
Foreground="#ff8000"
Margin="3" />
</StackPanel>
</HierarchicalDataTemplate>
</TreeView.Resources>
</TreeView>
public class CompileMessagesDto
{
public string State { get; set; } = "";
public string Path { get; set; } = "";
public string Description { get; set; } = "";
public string Parent { get; set; }
public ObservableCollection<CompileMessagesDto> Children { get; set; } = new ObservableCollection<CompileMessagesDto>();
}
TreeView before modification
TreeView after removing the style tag
用测试值初始化集合会显示箭头,只有在运行时修改集合后,TreeView 才会表现得像这样。如果有帮助,我会使用 MaterialDesignInXaml。
编辑(集合的更新方式)
这绝对不是最好的方法,但我遍历了编译器消息的集合并将它们添加到集合中。我只剩下每个 object 和对他们 parent.
的引用
foreach (CompilerResultMessage message in messages)
{
CompileMessagesDto compileMessage = new CompileMessagesDto { State = message.State.ToString(), Path = message.Path, Description = message.Description, Parent = parent };
MessageCrawl(message.Messages, message.Path);
CompileMessages.Add(compileMessage);
}
然后我设置每个 object 的 children,并从集合中删除不是根 object 的每个 object。留下带有 children 树的根。
List<CompileMessagesDto> removeItems = new List<CompileMessagesDto>();
foreach (CompileMessagesDto message in CompileMessages)
{
message.Children = new ObservableCollection<CompileMessagesDto>(CompileMessages.Where(c => c.Parent == message.Path));
if (message.Parent != "Root") { removeItems.Add(message); }
}
foreach (CompileMessagesDto message in removeItems)
{
CompileMessages.Remove(message);
}
您正在覆盖绑定源 CompileMessagesDto.Children
:
message.Children = new ObservableCollection<CompileMessagesDto>(...);
由于 CompileMessagesDto
未实现 INotifyProeprtyChanged
,Binding
将无法识别 属性 更改。
数据绑定的一般规则:如果源是 DependencyObject
的子类,则绑定源必须始终将其属性实现为 DependencyProperty
。否则源对象必须实现 INotifyPropertyChanged
.
如果不这样做,您将造成潜在的内存泄漏。
要解决您的问题,请让 CompileMessagesDto
实施 INotifyPropertyChanged
并从 CompileMessagesDto.Children
属性 引发 PropertyChanged
事件(推荐的解决方案)。
或者保留当前的 ObservableCollection
实例并使用 Add
和 Remove
对其进行修改。
// Using for-statement allows to get rid of the 'removedItems' collection and the second foreach-statement (improve performance)
for (int index = CompileMessages.Count - 1; index >= 0; index--)
{
CompileMessages
.Where(c => c.Parent == message.Path)
.ToList()
.ForEach(message.Children.Add);
if (message.Parent != "Root")
{
CompileMessages.Remove(message);
}
}
另请注意,您的 Style
确实会引发错误。 CompileMessagesDto
既没有 IsExpanded
也没有 IsSelected
属性.
我有一个在运行时填充的 ObservableCollection,它绑定到 TreeView。更新集合时,集合的根 object 出现在 TreeView 中,无法展开它(见第一张图片)。
我认为这意味着绑定存在问题,但是,删除 TreeView.ItemCotainerStyle 标记会使箭头出现并且一切都按预期工作(参见第二张图片)。此行为也反向工作,如果样式标签不在视图中并且我在更新集合后添加它,那么箭头将出现。
样式标签对于我的项目中的任何功能来说都不是必需的,它们只是我正在处理的示例中遗留下来的。
<TreeView ItemsSource="{Binding CompileMessages}">
<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>
</TreeView.ItemContainerStyle>
<TreeView.Resources>
<HierarchicalDataTemplate DataType="{x:Type models:CompileMessagesDto}"
ItemsSource="{Binding Path=Children}">
<StackPanel Orientation="Horizontal">
<materialDesign:PackIcon Kind="{Binding Path=State}"
Margin="3"
Foreground="White" />
<TextBlock Text="{Binding Path=Path}"
FontWeight="Normal"
Foreground="White"
FontSize="12"
Margin="3" />
<TextBlock Text="{Binding Path=Description}"
FontWeight="Normal"
FontSize="12"
Foreground="#ff8000"
Margin="3" />
</StackPanel>
</HierarchicalDataTemplate>
</TreeView.Resources>
</TreeView>
public class CompileMessagesDto
{
public string State { get; set; } = "";
public string Path { get; set; } = "";
public string Description { get; set; } = "";
public string Parent { get; set; }
public ObservableCollection<CompileMessagesDto> Children { get; set; } = new ObservableCollection<CompileMessagesDto>();
}
TreeView before modification
TreeView after removing the style tag
用测试值初始化集合会显示箭头,只有在运行时修改集合后,TreeView 才会表现得像这样。如果有帮助,我会使用 MaterialDesignInXaml。
编辑(集合的更新方式)
这绝对不是最好的方法,但我遍历了编译器消息的集合并将它们添加到集合中。我只剩下每个 object 和对他们 parent.
的引用foreach (CompilerResultMessage message in messages)
{
CompileMessagesDto compileMessage = new CompileMessagesDto { State = message.State.ToString(), Path = message.Path, Description = message.Description, Parent = parent };
MessageCrawl(message.Messages, message.Path);
CompileMessages.Add(compileMessage);
}
然后我设置每个 object 的 children,并从集合中删除不是根 object 的每个 object。留下带有 children 树的根。
List<CompileMessagesDto> removeItems = new List<CompileMessagesDto>();
foreach (CompileMessagesDto message in CompileMessages)
{
message.Children = new ObservableCollection<CompileMessagesDto>(CompileMessages.Where(c => c.Parent == message.Path));
if (message.Parent != "Root") { removeItems.Add(message); }
}
foreach (CompileMessagesDto message in removeItems)
{
CompileMessages.Remove(message);
}
您正在覆盖绑定源 CompileMessagesDto.Children
:
message.Children = new ObservableCollection<CompileMessagesDto>(...);
由于 CompileMessagesDto
未实现 INotifyProeprtyChanged
,Binding
将无法识别 属性 更改。
数据绑定的一般规则:如果源是 DependencyObject
的子类,则绑定源必须始终将其属性实现为 DependencyProperty
。否则源对象必须实现 INotifyPropertyChanged
.
如果不这样做,您将造成潜在的内存泄漏。
要解决您的问题,请让 CompileMessagesDto
实施 INotifyPropertyChanged
并从 CompileMessagesDto.Children
属性 引发 PropertyChanged
事件(推荐的解决方案)。
或者保留当前的 ObservableCollection
实例并使用 Add
和 Remove
对其进行修改。
// Using for-statement allows to get rid of the 'removedItems' collection and the second foreach-statement (improve performance)
for (int index = CompileMessages.Count - 1; index >= 0; index--)
{
CompileMessages
.Where(c => c.Parent == message.Path)
.ToList()
.ForEach(message.Children.Add);
if (message.Parent != "Root")
{
CompileMessages.Remove(message);
}
}
另请注意,您的 Style
确实会引发错误。 CompileMessagesDto
既没有 IsExpanded
也没有 IsSelected
属性.