如何在C#后台代码中动态创建数据模板并绑定TreeView层级数据
Great start with your DataTemplate code! The key missing pieces here are using a HierarchicalDataTemplate instead of a regular DataTemplate (since TreeView handles nested/hierarchical data) and properly binding the child items collection for each node. Let's break this down step by step:
1. 替换为HierarchicalDataTemplate
TreeView items depend on HierarchicalDataTemplate to manage nested child nodes—this template includes an ItemsSource property that lets you bind to the child collection of each parent node. Here's how to adjust your template creation code:
private HierarchicalDataTemplate GetHierarchicalTreeTemplate() { // Use HierarchicalDataTemplate instead of basic DataTemplate var hierarchicalTemplate = new HierarchicalDataTemplate(); // Create root StackPanel (same as your original setup) var stackPanel = new FrameworkElementFactory(typeof(StackPanel)); stackPanel.SetValue(StackPanel.OrientationProperty, Orientation.Horizontal); // --- Fix CheckBox Binding --- // Bind IsChecked to a boolean property on your data model (e.g., IsSelected) var checkBox = new FrameworkElementFactory(typeof(CheckBox)); checkBox.SetValue(CheckBox.MarginProperty, new Thickness(2)); // Use TwoWay binding so UI changes update your data model and vice versa checkBox.SetBinding(CheckBox.IsCheckedProperty, new Binding("IsSelected") { Mode = BindingMode.TwoWay }); // Optional: Bind Tag to the node's Name for reference in logic checkBox.SetValue(CheckBox.TagProperty, new Binding("Name")); stackPanel.AppendChild(checkBox); // --- Image Binding (your original code works well) --- var image = new FrameworkElementFactory(typeof(Image)); image.SetValue(Image.MarginProperty, new Thickness(2)); image.SetBinding(Image.SourceProperty, new Binding("ImageUrl")); stackPanel.AppendChild(image); // --- TextBlock with ToolTip Binding --- var textBlock = new FrameworkElementFactory(typeof(TextBlock)); textBlock.SetBinding(TextBlock.TextProperty, new Binding("Name")); // Bind ToolTip to the node's Name (or another property like Description if needed) textBlock.SetBinding(TextBlock.ToolTipProperty, new Binding("Name")); stackPanel.AppendChild(textBlock); // Assign the visual tree to the template hierarchicalTemplate.VisualTree = stackPanel; // --- Critical: Bind child items collection --- // Replace "Children" with the name of the child collection property on your data model hierarchicalTemplate.ItemsSource = new Binding("Children"); return hierarchicalTemplate; }
2. 定义你的数据模型
You need a data model class that represents each TreeView node, including a collection for child nodes. Make sure it implements INotifyPropertyChanged so UI updates reflect data changes:
public class TreeNodeModel : INotifyPropertyChanged { private string _name; private string _imageUrl; private bool _isSelected; private ObservableCollection<TreeNodeModel> _children; public string Name { get => _name; set { _name = value; OnPropertyChanged(); } } public string ImageUrl { get => _imageUrl; set { _imageUrl = value; OnPropertyChanged(); } } public bool IsSelected { get => _isSelected; set { _isSelected = value; OnPropertyChanged(); } } public ObservableCollection<TreeNodeModel> Children { get => _children; set { _children = value; OnPropertyChanged(); } } public event PropertyChangedEventHandler PropertyChanged; protected void OnPropertyChanged([CallerMemberName] string propertyName = null) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } }
3. 绑定TreeView数据源并应用模板
Finally, connect the dynamic template to your TreeView and set its ItemsSource to your root node collection:
private void InitializeTreeView() { // Create sample hierarchical data var rootNodes = new ObservableCollection<TreeNodeModel> { new TreeNodeModel { Name = "Root Folder 1", ImageUrl = "/Icons/folder.png", IsSelected = false, Children = new ObservableCollection<TreeNodeModel> { new TreeNodeModel { Name = "Document 1.1", ImageUrl = "/Icons/file.png" }, new TreeNodeModel { Name = "Document 1.2", ImageUrl = "/Icons/file.png" } } }, new TreeNodeModel { Name = "Root Folder 2", ImageUrl = "/Icons/folder.png" } }; // Apply the dynamic template to the TreeView BimTreeView.ItemTemplate = GetHierarchicalTreeTemplate(); // Bind the root data collection to the TreeView BimTreeView.ItemsSource = rootNodes; }
额外小贴士
- XamlReader Alternative: For newer .NET versions, Microsoft recommends using
XamlReader.Parseto create templates—it's more readable thanFrameworkElementFactory. Here's a quick example:private HierarchicalDataTemplate GetTemplateViaXamlReader() { var xaml = @" <HierarchicalDataTemplate xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation' ItemsSource='{Binding Children}'> <StackPanel Orientation='Horizontal'> <CheckBox Margin='2' IsChecked='{Binding IsSelected, Mode=TwoWay}' Tag='{Binding Name}'/> <Image Margin='2' Source='{Binding ImageUrl}'/> <TextBlock Text='{Binding Name}' ToolTip='{Binding Name}'/> </StackPanel> </HierarchicalDataTemplate>"; return (HierarchicalDataTemplate)XamlReader.Parse(xaml); }
内容的提问来源于stack exchange,提问作者Ali Asad

