WPF TreeView绑定二级ObservableCollection点击提交按钮无法显示层级数据如何解决
修复方案
你代码里存在多处绑定错误、逻辑错误和实例重复问题,按以下步骤修改即可正常运行:
1. 修复XAML代码
<UserControl x:Class="MCTv2.UI.LeftTreeView" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:local="clr-namespace:MCTv2.UI" xmlns:UIC="clr-namespace:MCTv2.UIController" xmlns:vm ="clr-namespace:MCTv2.Modules.ActionDefinition" xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity" mc:Ignorable="d" VerticalContentAlignment="Stretch" HorizontalContentAlignment="Stretch"> <UserControl.DataContext> <UIC:AddChannelViewModel/> </UserControl.DataContext> <!-- 删掉重复的ViewModel实例声明,否则会出现两个独立的ViewModel实例数据不互通 --> <StackPanel> <TreeView ItemsSource="{Binding View}"> <i:Interaction.Behaviors> <UIC:TreeViewBehavior/> </i:Interaction.Behaviors> <TreeView.Resources> <!-- 修复绑定路径:ItemsSource绑定DeviceObjects,Content绑定正确大小写的ChannelName --> <HierarchicalDataTemplate DataType="{x:Type vm:ChannelObject}" ItemsSource="{Binding DeviceObjects}"> <Label Content="{Binding ChannelName}"/> </HierarchicalDataTemplate> <!-- 新增DeviceObject的DataTemplate,否则设备节点不会显示正确名称 --> <HierarchicalDataTemplate DataType="{x:Type vm:DeviceObject}"> <Label Content="{Binding DeviceName}"/> </HierarchicalDataTemplate> </TreeView.Resources> </TreeView> </StackPanel> </UserControl>
2. 修复Model层属性通知
你用了MvvmLight的ViewModelBase,自动属性不会触发UI变更通知,修改为标准通知属性:
public class ChannelObject : ViewModelBase { private string _channelName; public string ChannelName { get => _channelName; set { _channelName = value; RaisePropertyChanged(); } } private ObservableCollection<DeviceObject> _deviceObjects = new ObservableCollection<DeviceObject>(); public ObservableCollection<DeviceObject> DeviceObjects { get => _deviceObjects; set { _deviceObjects = value; RaisePropertyChanged(); } } } public class DeviceObject : ViewModelBase { private string _deviceName; public string DeviceName { get => _deviceName; set { _deviceName = value; RaisePropertyChanged(); } } public UserTest userTests { get; set; } = new UserTest(); public ConformanceTest confTests { get; set; } = new ConformanceTest(); }
3. 修复ViewModel逻辑错误
public class AddChannelViewModel : ViewModelBase { private string channelName; private int numberofDev; // 改为int类型避免字符串拼接错误 // 将treelist改为属性,WPF绑定不支持字段 private ObservableCollection<ChannelObject> _treelist = new ObservableCollection<ChannelObject>(); public ObservableCollection<ChannelObject> View { get => _treelist; set { _treelist = value; RaisePropertyChanged(); } } // SelectedItem改为通知属性 private ChannelObject _selectedItem; public ChannelObject SelectedItem { get => _selectedItem; set { _selectedItem = value; RaisePropertyChanged(); } } private ChannelObject Channel { get => new ChannelObject() { ChannelName = channelName, DeviceObjects = new ObservableCollection<DeviceObject>() { new DeviceObject() { DeviceName = $"device {numberofDev}", confTests = new ConformanceTest(), userTests = new UserTest() }, new DeviceObject() { DeviceName = $"device {numberofDev+1}", confTests = new ConformanceTest(), userTests = new UserTest() } } }; } public AddChannelViewModel() { AddChannelCommand = new RelayCommand<object[]>(AddTreeNode); ClosePopupCommand = new RelayCommand<object>(ClosePopup); } private void AddTreeNode(object[] parameter) { channelName = parameter[0].ToString(); if (string.IsNullOrWhiteSpace(channelName)) return; // 把输入的设备数量转为int if(!int.TryParse(parameter[1].ToString(), out numberofDev)) return; if (parameter[2] is Window win) { win.Close(); } if (SelectedItem == null) View.Add(Channel); else // 删掉没用的devs属性,直接new DeviceObject添加 SelectedItem.DeviceObjects.Add(new DeviceObject() { DeviceName = $"device {numberofDev}" }); } private void ClosePopup(object parameter) { if (parameter is Window win) { win.Close(); } } public ICommand AddChannelCommand { get; set; } public ICommand ClosePopupCommand { get; set; } }
4. 优化TreeViewBehavior容错
public class TreeViewBehavior : Behavior<TreeView> { protected override void OnAttached() { AssociatedObject.SelectedItemChanged += AssociatedObject_SelectedItemChanged; } private void AssociatedObject_SelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> e) { if (sender is not TreeView tv || tv.DataContext is not AddChannelViewModel vm) return; // 仅当选中的是通道节点时赋值,避免选中设备节点时SelectedItem为null vm.SelectedItem = tv.SelectedItem as ChannelObject; } protected override void OnDetaching() { AssociatedObject.SelectedItemChanged -= AssociatedObject_SelectedItemChanged; base.OnDetaching(); } }
修改完成后即可实现:未选中节点时点击提交新增根通道节点,选中通道节点时点击提交为该通道新增子设备节点,层级结构正常展示。
内容的提问来源于stack exchange,提问作者h1d3r00t
相关产品推荐
相关产品推荐

