WPF - Изменения в свойствах древовидных элементов не отражены в проблеме пользовательского интерфейса - PullRequest
0 голосов
/ 23 мая 2019

У меня есть древовидное представление, построенное с помощью HierarchicalDataTemplates, я хочу иметь возможность добавлять файлы JSON к узлам SegmentInfo - если я это сделаю, данные будут добавлены, но изменение не будет отражено в пользовательском интерфейсе (тем не менее, комментарий говорит: «нет данных»"в красном).

Я сделал список элементов дерева в виде ObservableCollection, переместил его в класс" ViewModel ", который наследует INotifyPropertyChanged, я, кажется, настроил его правильно, я установил 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;
        }
    }
}

Класс ViewModel:

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));
    }
}

Класс TypeInfo:

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 пользовательский интерфейс обновит узел с помощьюданные "комментарий.Теперь я могу убедиться, что данные действительно добавлены в SegmentInfo, но пользовательский интерфейс этого не отражает.

1 Ответ

2 голосов
/ 23 мая 2019

Ваше свойство HasData не обновляет пользовательский интерфейс, поскольку у вас нет механизма для его обновления (INotifyPropertyChanged). SegmentInfo необходимо реализовать INotifyPropertyChanged.

Если вы планируете иметь свойство Bind to UI, оно должно иметь уведомление PropertyChanged для него отдельно. Так на вашем SegmentInfo классе; Name, HasData и JsonContent должны вызывать событие OnPropertyChanged в своем установщике.

Хороший способ думать об этом; все, что напрямую связано с XAML (Text="{Binding Name}"), должно вызывать событие при изменении. Если вы связываете какие-либо свойства, такие как: (Text="{Binding MyThing.Name}"), вы не получите обновления при изменении MyThing.Name. Вы должны разорвать собственность и уведомить об этом напрямую.

...