c# - 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 没有反映这一点。
解决方案
您的HasData
属性不会更新 UI,因为您没有更新它的机制 (INotifyPropertyChanged)。SegmentInfo
需要实现 INotifyPropertyChanged。
如果您计划将一个属性绑定到 UI,则需要为它单独发出一个 PropertyChanged 通知。所以在你的SegmentInfo
课上;Name
, HasData
, 并且JsonContent
每个人都应该在他们的 setter 中引发一个OnPropertyChanged
事件。
一个很好的思考方式;XAML ( ) 中直接绑定的任何内容Text="{Binding Name}"
在更改时都应引发事件。如果您绑定任何属性,例如: ( Text="{Binding MyThing.Name}"
) 您将不会在更改时获得更新MyThing.Name
。您需要拆分该属性并直接对其进行通知。
推荐阅读
- android - 使用 SAF 从 Uri 获取文件夹名称
- reactjs - 反应警告useEffect缺少依赖
- php - 在 PHP 中转义特殊字符
- azure - 备份 Azure Analysis Services 表格模型
- java - Spring Boot 实体和 OneToMany 连接:如何获取所需信息?
- python - 列表中 Python 中的 Sumifs 函数
- c++ - 标准整数类型前面的命名空间
- flutter - 在接受后请求对颤动的许可会创建新的页面实例
- asp.net-mvc - 在 LINQ 中使用 group by 排序并加入
- c# - 不使用 Web API 的主动通知