哦,上帝是星期一,我觉得我对wpf的所有知识都被删除了。
我想当在Property ModeChanged模式下绑定时,只有在Target属性发生变化时才会更新Source,而不是所有时间。
这是一个示例,即使Target属性尚未更改,Binding也会不断更新Source。为什么?
顺便说一下,我在.NET 4.0中
<StackPanel>
<TextBox x:Name="tbx1" Text="{Binding Txt, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
<Button Content="Change Text" Click="OnClick" />
</StackPanel>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.DataContext = new ViewModel();
}
private void OnClick(object sender, RoutedEventArgs e)
{
tbx1.Text = "hello";
}
}
public class ViewModel
{
private string txt;
public string Txt
{
get { return txt; }
set { txt = value; Console.WriteLine("Txt Setter Called!");}
}
}
每次我点击按钮时,都会调用Txt的设置器。为什么?价值没有改变。
GetHashCode()方法返回相同的结果。
我缺少什么? : - )
答案 0 :(得分:2)
嗯,这就是它的工作方式,但名称可能有点误导。
在设置目标属性时,源值正在更新,不一定在值已更改时,顾名思义。
您可以使用fx观察相同的行为。一个CheckBox
。即使目标值没有改变,反复将IsChecked
属性设置为true也会触发源更新。
因此绑定系统在触发更新之前不会比较实际值,只关心目标属性是否已设置。
您的示例扩展为CheckBox
:
XAML:
<StackPanel>
<TextBox x:Name="tbx1" Text="{Binding Txt, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
<CheckBox x:Name="chk1" IsChecked="{Binding IsChecked, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
<Button Content="Change Text" Click="OnClick" />
</StackPanel>
代码隐藏:
public partial class MainWindow
{
public MainWindow()
{
InitializeComponent();
this.DataContext = new ViewModel();
}
private void OnClick(object sender, RoutedEventArgs e)
{
tbx1.Text = "hello";
chk1.IsChecked = true;
}
}
public class ViewModel
{
private string txt;
public string Txt
{
get { return txt; }
set { txt = value; Console.WriteLine("Txt Setter Called!"); }
}
private bool isChecked;
public bool IsChecked
{
get { return isChecked; }
set { isChecked = value; Console.WriteLine("IsChecked Setter Called!"); }
}
}
答案 1 :(得分:1)
如您所料,没有发生实际的“财产变更”,可以通过以下方式确认:
using System.ComponentModel;
var descriptor = DependencyPropertyDescriptor.FromProperty(TextBox.TextProperty, typeof(TextBox));
descriptor.AddValueChanged(tbx1, (s, e) => Console.WriteLine("tbx1 changed"));
“tbx1已更改”只会出现一次。
如果源是依赖属性,则它也不会更改。尝试添加另一个文本框并将其用作源而不是viewmodel:
<TextBox Name="tbx2" />
<TextBox Name="tbx1" Text="{Binding ElementName=tbx2, Path=Text, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
处理程序:
descriptor.AddValueChanged(tbx2, (s, e) => Console.WriteLine("tbx2 changed"));
同样,只有一次改变。
所以是的,触发器没有准确命名。此行为的原因可能是确保属性集始终触发绑定转换器,因为即使使用相同的输入,ConvertBack 也可以返回不同的值。
无论如何,目标和来源都需要承担一些责任来确定“变化”是什么。毕竟,如果它是一个真正的双向绑定,那么应允许以这种方式实现OnClick
,效果完全相同:
tbx1.DataContext.Txt = "hello";
因此,请确保您的属性设置器在继续之前始终检查实际更改(作为依赖项属性)。
答案 2 :(得分:0)
将您的BindingMode
更改为OneWay
或OneWayToSource
..您的问题将得到解决......
<TextBox x:Name="tbx1" Text="{Binding Txt, Mode=OneWayToSource, UpdateSourceTrigger=PropertyChanged}"/>
答案 3 :(得分:0)
您可以更改setter以检查新值是否与旧值不同。
public string Txt
{
get { return txt; }
set {
if (txt == value) return;
txt = value;
Console.WriteLine("Txt Setter Called!");
}
}