c# - c# wpf - 使用ValueConverter时,MVVM不更新UI?
问题描述
我只是在学习 WPF,最终我想要完成的是数据网格中的计算列,其中显示的数字是集合中特定属性的总和。
经过一番谷歌搜索,我决定采用的方法是使用 ValueConverter 进行计算,但似乎该数字从未在 UI 中更新。我所做的阅读表明 PropertyChangedEvent 应该冒泡,这应该只是工作,但它没有。我错过了一些东西,但我不知道是什么。
我写了一个简单的演示应用程序来展示我在下面做什么。第二个TextBlock中的数字在点击按钮之前应该是10(是),但是点击之后是6,但是一直停留在10。
怎么会?我在吠叫错误的树吗?有一个更好的方法吗?任何帮助,将不胜感激。
MainWindow.xaml:
<Window x:Class="TestApp.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:TestApp"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Window.Resources>
<local:BarSumConverter x:Key="BarSumConverter" />
</Window.Resources>
<StackPanel>
<TextBlock Text="{Binding ObjFoo.Bars[0].ANumber, Mode=TwoWay}" />
<TextBlock Text="{Binding ObjFoo.Bars, Converter={StaticResource BarSumConverter}, Mode=TwoWay}" />
<Button Content="Click me!" Click="Button_Click" />
</StackPanel>
</Window>
主窗口.xaml.cs
namespace TestApp
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public Foo ObjFoo { get; set; }
public MainWindow()
{
InitializeComponent();
this.DataContext = this;
ObjFoo = new Foo();
ObjFoo.Bars.Add(new Bar(5));
ObjFoo.Bars.Add(new Bar(5));
}
private void Button_Click(object sender, RoutedEventArgs e)
{
ObjFoo.Bars[0].ANumber = 1;
}
}
}
Foo.cs
public class Foo
{
public Foo()
{
bars = new ObservableCollection<Bar>();
}
ObservableCollection<Bar> bars;
public ObservableCollection<Bar> Bars
{
get
{
return bars;
}
set { bars = value; }
}
}
酒吧.cs
public class Bar : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public Bar(int number)
{
this.ANumber = number;
}
private int aNumber;
public int ANumber
{
get { return aNumber; }
set
{
aNumber = value;
OnPropertyChanged("aNumber");
}
}
protected void OnPropertyChanged([CallerMemberName] string name = null)
{
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
}
BarSumConverter.cs
public class BarSumConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var bars = value as ObservableCollection<Bar>;
if (bars == null) return 0;
decimal total = 0;
foreach (var bar in bars)
{
total += bar.ANumber;
}
return total;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
解决方案
乍一看,您的代码似乎没问题,除了一个细节:要么让反射对name
参数进行评估,要么手动指定它(然后删除属性)。
在后一种情况下,您应该传递属性名称,而不是私有字段。如果名称错误,事件通知将不起作用。绑定机制将仅查找公共属性。只需利用nameof
运算符来防止重构错字。
选项1:
public int ANumber
{
get { return aNumber; }
set
{
aNumber = value;
OnPropertyChanged();
}
}
protected void OnPropertyChanged([CallerMemberName] string name = null)
{
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
选项 2:
public int ANumber
{
get { return aNumber; }
set
{
aNumber = value;
OnPropertyChanged(nameof(ANumber));
}
}
protected void OnPropertyChanged(string name)
{
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
此外,在这两个选项中,我建议在属性上添加相等检查set
。这是为了在替换值与现有值匹配时防止无用的通知:
public int ANumber
{
get { return aNumber; }
set
{
if (aNumber != value)
{
aNumber = value;
OnPropertyChanged( ... );
}
}
}
注意:我没有尝试您的代码,因此它可能隐藏了其他要修补的内容。
更新:我会在Foo
课堂上做出一些根本性的改变,以使事情顺利进行。
public class Foo : INotifyPropertyChanged
{
public Foo()
{
bars = new ObservableCollection<Bar>();
bars.CollectionChanged += OnCollectionChanged;
}
ObservableCollection<Bar> bars;
public ObservableCollection<Bar> Bars
{
get
{
return bars;
}
//set { bars = value; }
}
private decimal total;
public decimal Total
{
get { return total; }
private set {
if (total != value)
{
total = value;
OnPropertyChange();
}
}
}
void OnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
decimal t = 0;
foreach (var bar in bars)
{
t += bar.ANumber;
}
this.Total = t;
}
protected void OnPropertyChanged([CallerMemberName] string name = null)
{
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
}
我将总计算移到了这里:转换器不适用于业务逻辑。
此外,为第二个调整 XAML TextBox
:
<TextBlock Text="{Binding ObjFoo.Total}" />
请注意,没有理由进行TwoWay
此绑定。
推荐阅读
- elasticsearch - 针对嵌套对象的两个属性的 AND 条件的 Elastic Search 查询
- android - 使按钮文本部分粗体不起作用
- artifactory - 更改 docker 存储库类(本地到虚拟)
- django - 如何使用 get_context_data 在基于类的视图中添加分页
- android - 在某些设备上旋转输出的照片
- javascript - raycaster 是否应该始终位于 render() 函数中?
- python - Pandas:向分类数据框添加一列
- regex - 使用shell脚本提取“[和]”之间的值
- c# - 防止重定向到 asp.net webform 中的默认 /Account/Login
- sql - 插入和相应的触发器调用是原子过程吗?