首页 > 解决方案 > WPF - treeviewitems 属性的更改未反映在 UI 问题中

问题描述

我有一个使用 HierarchicalDataTemplates 构建的树视图,我希望能够将 JSON 文件添加到 SegmentInfo 节点 - 如果我这样做了,则会添加数据,但更改不会反映在 UI 中(仍然评论以红色显示“无数据” )。

我已将树视图项列表设置为 ObservableCollection,将其移至继承 INotifyPropertyChanged 的​​“ViewModel”类,我似乎设置正确,我已将 DataContext 设置为我窗口中的 ViewModel 对象。在 xaml 中,我将绑定和模式设置为 TwoWay。仍然没有任何帮助

XAML:

<Window.Resources>
        <local:BoolToStringConverter x:Key="BoolToStringConverter" FalseValue="no data" TrueValue="has data" />
    </Window.Resources>
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="auto" MinHeight="384.8"/>
            <RowDefinition Height="35.2"/>
        </Grid.RowDefinitions>
        <TreeView Name="trvTypeInfos" Margin="5" Grid.Row="0" ItemsSource="{Binding Path=TypeInfoList, Mode=TwoWay}">
            <TreeView.Resources>
                <Style TargetType="{x:Type TreeViewItem}">
                    <EventSetter Event="ListBoxItem.PreviewMouseUp" 
                            Handler="ListBoxItem_PreviewMouseUp"/>
                    <Setter Property="IsExpanded" Value="True"/>
                </Style>
                <HierarchicalDataTemplate DataType="{x:Type data:TypeInfo}" ItemsSource="{Binding SegmentInfos, Mode=TwoWay}">
                    <StackPanel Orientation="Horizontal">
                        <TextBlock Text="{Binding Name}" />
                        <TextBlock Text=" [" Foreground="Blue" />
                        <TextBlock Text="{Binding SegmentInfos.Count}" Foreground="Blue"/>
                        <TextBlock Text="]" Foreground="Blue" />
                    </StackPanel>
                </HierarchicalDataTemplate>
                <DataTemplate DataType="{x:Type data:SegmentInfo}">
                    <StackPanel Orientation="Horizontal">
                        <TextBlock Text="{Binding Name}" />
                        <TextBlock Text=" ("/>
                        <TextBlock Text="{Binding Path=HasData, Mode=TwoWay, Converter={StaticResource BoolToStringConverter}}">
                            <TextBlock.Style>
                                <Style TargetType="TextBlock">
                                    <Style.Triggers>
                                        <Trigger Property="Text" Value="no data">
                                            <Setter Property="Foreground" Value="Red"/>
                                        </Trigger>
                                        <Trigger Property="Text" Value="has data">
                                            <Setter Property="Foreground" Value="Green"/>
                                        </Trigger>
                                    </Style.Triggers>
                                </Style>
                            </TextBlock.Style>
                        </TextBlock>
                        <TextBlock Text=")"/>
                    </StackPanel>
                </DataTemplate>
            </TreeView.Resources>
        </TreeView>
        <StackPanel Grid.Row="1" Orientation="Horizontal" HorizontalAlignment="Right">
            <Button  Width="80" Height="20" Content="OK" Margin="5,0, 5, 5" IsDefault="True" Click="OK_Click"/>
            <Button  Width="80" Height="20" Content="Cancel" Margin="5,0, 5, 5" Click="Cancel_Click" />
        </StackPanel>

    </Grid>

窗口类:

public SegmentDataUpdaterDialog(SegmentDataUpdater segmentDataUpdater, List<TypeInfo> typeInfoList)
{
    ViewModel = new ViewModel(typeInfoList);
    DataContext = ViewModel;
    SegmentDataUpdater = segmentDataUpdater;
    InitializeComponent();
}

private void ListBoxItem_PreviewMouseUp(object sender, MouseButtonEventArgs e)
{
    TreeViewItem item = sender as TreeViewItem;
    SegmentInfo segInfo = item.Header as SegmentInfo;
    if (segInfo != null)
    {
        MessageBox.Show(segInfo.JsonContents);
        var filePath = AskForFile();
        bool success = SegmentDataUpdater.TryStoreJson(segInfo, filePath, out string json);
        if (success)
        {
            segInfo.JsonContents = json;
            segInfo.HasData = true;
        }
    }
}

视图模型类:

public class ViewModel : INotifyPropertyChanged
{
    private ObservableCollection<TypeInfo> _typeInfoList;
    public ObservableCollection<TypeInfo> TypeInfoList
    {
        get { return _typeInfoList; }
        set
        {
            if (_typeInfoList==null || !value.All(_typeInfoList.Contains))
            {
                _typeInfoList = value;
                OnPropertyChanged(nameof(TypeInfoList));
            }
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    public ViewModel(List<TypeInfo> typeInfos)
    {
        TypeInfoList = new ObservableCollection<TypeInfo>(typeInfos);
    }

    private void OnPropertyChanged(string propertyName)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

类型信息类:

public class TypeInfo
{
    public string Name { get; set; }
    public ObservableCollection<SegmentInfo> SegmentInfos { get; set; }
    public int ElementId { get; set; }

    public TypeInfo()
    {
        SegmentInfos = new ObservableCollection<SegmentInfo>();
    }
}

SegmentInfo 类:

public class SegmentInfo
{
    public string Name { get; set; }
    public bool HasData { get; set; }
    public string JsonContents { get; set; }
    public int ElementId { get; set; }
}

转换器类:

public class BoolToValueConverter<T> : IValueConverter
{
    public T FalseValue { get; set; }
    public T TrueValue { get; set; }

    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        if (value == null)
            return FalseValue;
        else
            return (bool)value ? TrueValue : FalseValue;
    }

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        return value != null ? value.Equals(TrueValue) : false;
    }
}

public class BoolToStringConverter : BoolToValueConverter<String> { }

我希望在成功将 json 文件添加到 SegmentInfo 后,UI 将使用“有数据”注释更新节点。现在我可以检查数据是否确实添加到 SegmentInfo 但 UI 没有反映这一点。

标签: c#wpfdata-bindingtreeview

解决方案


您的HasData属性不会更新 UI,因为您没有更新它的机制 (INotifyPropertyChanged)。SegmentInfo需要实现 INotifyPropertyChanged。

如果您计划将一个属性绑定到 UI,则需要为它单独发出一个 PropertyChanged 通知。所以在你的SegmentInfo课上;Name, HasData, 并且JsonContent每个人都应该在他们的 setter 中引发一个OnPropertyChanged事件。

一个很好的思考方式;XAML ( ) 中直接绑定的任何内容Text="{Binding Name}"在更改时都应引发事件。如果您绑定任何属性,例如: ( Text="{Binding MyThing.Name}") 您将不会在更改时获得更新MyThing.Name。您需要拆分该属性并直接对其进行通知。


推荐阅读