高级 WPF 树形视图,多级绑定(代码隐藏 - 无 XAML!)





5.00/5 (2投票s)
演示如何使用 C# 代码隐藏方式,在不使用 XAML 的情况下创建具有多级 HierarchicalDataTemplate 的 WPF 树形视图,并使用 MVVM 进行数据绑定。
引言
通常情况下,您应该**几乎总是使用 XAML** 来创建 WPF UI。但是,本文的目的是展示在不使用 XAML 的情况下,在 WPF 中创建 UI 的代码隐藏方式。在动态 UI 创建和消除复杂的 XAML 绑定方面,这种方式可以灵活而强大。
代码隐藏方法是一种高级概念。即使您可能永远不需要使用它,理解它将帮助您更好地理解 WPF 的工作原理。查尔斯·佩茨奥德写了一本很棒的 WPF 书,书中使用了大量的代码隐藏。
仍然感兴趣吗?让我们继续。
背景
在网上有 WPF TreeView
的示例,但它们大多是基于 XAML 的。例如
我使用 TreeView
+ HierarchicalDataTemplate
与多级绑定,因为它是在 XAML 绑定主题中最容易被误解的部分之一。我在这里不讨论 MVVM,因为网上可以找到大量相关信息。显然,本文的目标是面向中级和高级 WPF 用户。
Using the Code
启动时,应用程序看起来如下。它是一个 4 级 TreeView
,根节点位于第 0
级。每个级别的节点都有自己的 HierarchicalDataTemplate
。
请注意,该项目解决方案中**没有.xaml 文件**
每个级别的节点都有一个 ViewModel
用于数据绑定。例如,C# 类 Level2NodeViewModel
用于数据绑定第 2 级节点,包含:箭头图像 + 文本 + 搜索图像 + 超链接
上述元素被包装在一个以编程方式创建的 StackPanel
中
private FrameworkElementFactory BuildLevel2NodeVisual()
{
var stackPanelFactory = CreateStackPanelFactory(Orientation.Horizontal, new Thickness(4, 4, 5, 2));
AppendImageBinding(stackPanelFactory, "NodeImagePath");
AppendTextBlockBinding(stackPanelFactory, "Name");
// Bind SearchImagePath with Level2NodeViewModel
AppendImageBinding(stackPanelFactory, "SearchImagePath", new Thickness(10, 0, 0, 0));
AppendHyperlinkBinding(stackPanelFactory, "Search with Amazon.com", Colors.MediumOrchid,
"AmazonSearchCommand", "AmazonSearchUrl", new Thickness(5, 0, 0, 0));
return stackPanelFactory;
}
以下函数确定如何为任何级别创建“节点视觉”HierarchicalDataTemplate
private HierarchicalDataTemplate BuildLevelNodeDataTemplate(int level = int.MaxValue)
{
FrameworkElementFactory levelNodeElementFactory;
switch (level)
{
case 0: levelNodeElementFactory = BuildRootNodeVisual(); break;
case 1: levelNodeElementFactory = BuildLevel1NodeVisual(); break;
case 2: levelNodeElementFactory = BuildLevel2NodeVisual(); break;
case 3: levelNodeElementFactory = BuildLevel3NodeVisual(); break;
default: levelNodeElementFactory = BuildLevelNodeBasicVisual(); break;
}
var levelNodeDataTemplate = new HierarchicalDataTemplate
{
ItemsSource = new Binding("ChildNodes"),
VisualTree = levelNodeElementFactory
};
return levelNodeDataTemplate;
}
在运行时,技巧是动态地为它的级别节点选择正确的 ViewModel
public class NodeDataTemplateSelector : DataTemplateSelector
{
....
public override DataTemplate SelectTemplate(object item, DependencyObject container)
{
DataTemplate dataTemplate;
if (item == null)
dataTemplate = base.SelectTemplate(null, container);
else if (item is RootNodeViewModel)
dataTemplate = LevelNodeDateTemplateOrDefault(0);
else if (item is Level1NodeViewModel)
dataTemplate = LevelNodeDateTemplateOrDefault(1);
else if (item is Level2NodeViewModel)
dataTemplate = LevelNodeDateTemplateOrDefault(2);
else if (item is Level3NodeViewModel)
dataTemplate = LevelNodeDateTemplateOrDefault(3);
else
dataTemplate = _defaultLevelNodeDataTemplate;
return dataTemplate;
}
}
结论
我使用了 Visual Studio 2015 和 C# 6.0 语法。如果您使用预 VS2015 编译应用程序,您将需要修复 C# 6.0 语法错误。这很容易做到,并且网上有相关信息。