C# WPF 数据绑定和 IValueConverter
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/174841/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
WPF Data Binding and IValueConverter
提问by urini
Why is it that when I use a converter in my binding expression in WPF, the value is not updated when the data is updated.
为什么我在 WPF 中的绑定表达式中使用转换器时,更新数据时未更新值。
I have a simple Person data model:
我有一个简单的 Person 数据模型:
class Person : INotifyPropertyChanged
{
public string FirstName { get; set; }
public string LastName { get; set; }
}
My binding expression looks like this:
我的绑定表达式如下所示:
<TextBlock Text="{Binding Converter={StaticResource personNameConverter}" />
My converter looks like this:
我的转换器看起来像这样:
class PersonNameConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
Person p = value as Person;
return p.FirstName + " " + p.LastName;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
If I bind the data without a converter it works great:
如果我在没有转换器的情况下绑定数据,则效果很好:
<TextBlock Text="{Binding Path=FirstName}" />
<TextBlock Text="{Binding Path=LastName}" />
What am I missing?
我错过了什么?
EDIT: Just to clarify a few things, both Joel and Alan are correct regarding the INotifyPropertyChanged interface that needs to be implemented. In reality I do actually implement it but it still doesn't work.
编辑:为了澄清一些事情,乔尔和艾伦关于需要实现的 INotifyPropertyChanged 接口都是正确的。实际上我确实实现了它,但它仍然不起作用。
I can't use multiple TextBlock elements because I'm trying to bind the Window Title to the full name, and the Window Title does not take a template.
我不能使用多个 TextBlock 元素,因为我试图将窗口标题绑定到全名,而窗口标题不采用模板。
Finally, it is an option to add a compound property "FullName" and bind to it, but I'm still wondering why updating does not happen when the binding uses a converter. Even when I put a break point in the converter code, the debugger just doesn't get there when an update is done to the underlying data :-(
最后,添加复合属性“FullName”并绑定到它是一个选项,但我仍然想知道为什么当绑定使用转换器时不会发生更新。即使我在转换器代码中放置了一个断点,当对底层数据进行更新时,调试器也不会到达那里:-(
Thanks, Uri
谢谢,乌里
采纳答案by Joel B Fant
(see edits below; latest: #2)
(见下面的编辑;最新:#2)
It isn't updating because your Person
object is not capable of notifying anything that the value of FirstName
or LastName
has changed. See this Question.
它不会更新,因为您的Person
对象无法通知其值FirstName
或LastName
已更改的任何内容。看到这个问题。
And here's how you implement INotifyPropertyChanged
. (Updated, see Edit 2)
这就是你如何实现INotifyPropertyChanged
. (更新,见编辑 2)
using System.ComponentModel;
class Person : INotifyPropertyChanged {
public event PropertyChangedEventHandler PropertyChanged;
string _firstname;
public string FirstName {
get {
return _firstname;
}
set {
_firstname = value;
onPropertyChanged( "FirstName", "FullName" );
}
}
string _lastname;
public string LastName {
get {
return _lastname;
}
set {
_lastname = value;
onPropertyChanged( "LastName", "FullName" );
}
}
public string FullName {
get {
return _firstname + " " + _lastname;
}
}
void onPropertyChanged( params string[] propertyNames ) {
PropertyChangedEventHandler handler = PropertyChanged;
if ( handler != null ) {
foreach ( var pn in propertyNames ) {
handler( this, new PropertyChangedEventArgs( pn ) );
}
}
}
}
Edit 1
编辑 1
Actually, since you're after the first name and last name updating, and Path=FirstName
and such works just fine, I don't think you'll need the converter at all. Multiple TextBlocks
are just as valid, and can actually work better when you're localizing to a right-to-left language.
实际上,由于您在更新名字和姓氏,Path=FirstName
并且这样工作得很好,我认为您根本不需要转换器。多个TextBlocks
同样有效,当您本地化为从右到左的语言时,实际上可以更好地工作。
Edit 2
编辑 2
I've figured it out. It's not being notified that the properties have updated because it is binding to the object itself, not one of those properties. Even when I made Person
a DependencyObject
and made FirstName
and LastName
DependencyProperties
, it wouldn't update.
我已经想通了。不会通知属性已更新,因为它绑定到对象本身,而不是这些属性之一。甚至当我做了Person
一个DependencyObject
和发FirstName
和LastName
DependencyProperties
,它不会更新。
You willhave to use a FullName
property, and I've update the code of the Person
class above to reflect that. Then you can bind the Title
. (Note:I've set the Person
object as the Window
's DataContext
.)
您将不得不使用一个FullName
属性,我已经更新了Person
上面类的代码以反映这一点。然后就可以绑定了Title
。(注意:我已将Person
对象设置为Window
's DataContext
。)
Title="{Binding Path=FullName, Mode=OneWay}"
If you're editing the names in a TextBox
and want the name changed reflected immediately instead of when the TextBox
loses focus, you can do this:
如果您正在编辑 a 中的名称TextBox
并希望立即反映更改的名称而不是在TextBox
失去焦点时,您可以这样做:
<TextBox Name="FirstNameEdit"
Text="{Binding Path=FirstName, UpdateSourceTrigger=PropertyChanged}" />
I know you didn't want to use a FullName
property, but anything that would accomplish what you want would probably be a bit of a Rube Goldberg device. Such as implementing INotifyPropertyChanged
and a Person
property on the Window
class itself, having the Window
listen on the PropertyChanged
event in order to fire the Window
's PropertyChanged
event, and using a relative binding like the following. You'd also have set the Person
property before InitializeComponent()
or fire PropertyChanged
after setting the Person
property so that it shows up, of course. (Otherwise it will be null
during InitializeComponent()
and needs to know when it's a Person
.)
我知道您不想使用FullName
属性,但是任何可以实现您想要的功能的东西都可能有点像 Rube Goldberg 设备。例如在类本身上实现INotifyPropertyChanged
和Person
属性Window
,Window
监听PropertyChanged
事件以触发Window
'sPropertyChanged
事件,并使用如下所示的相对绑定。当然,您还可以在设置Person
属性之前InitializeComponent()
或PropertyChanged
之后设置Person
属性,以便它显示出来。(否则它将在null
期间InitializeComponent()
并且需要知道它何时是Person
。)
<Window.Resources>
<loc:PersonNameConverter
x:Key="conv" />
</Window.Resources>
<Window.Title>
<Binding
RelativeSource="{RelativeSource Self}"
Converter="{StaticResource conv}"
Path="Person"
Mode="OneWay" />
</Window.Title>
回答by Alan Le
In Order for the binding to be updated, your person class needs to implement INotifyPropertyChanged to let the binding know that the object's properties have been udpated. You can also save yourself from the extra converter by providing a fullName property.
为了更新绑定,您的个人类需要实现 INotifyPropertyChanged 以让绑定知道对象的属性已被更新。您还可以通过提供 fullName 属性来避免额外的转换器。
using System.ComponentModel;
namespace INotifyPropertyChangeSample
{
public class Person : INotifyPropertyChanged
{
private string firstName;
public string FirstName
{
get { return firstName; }
set
{
if (firstName != value)
{
firstName = value;
OnPropertyChanged("FirstName");
OnPropertyChanged("FullName");
}
}
}
private string lastName;
public string LastName
{
get { return lastName; }
set
{
if (lastName != value)
{
lastName = value;
OnPropertyChanged("LastName");
OnPropertyChanged("FullName");
}
}
}
public string FullName
{
get { return firstName + " " + lastName; }
}
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string name)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(name));
}
#endregion
}
}
Your Binding will now look like this:
您的绑定现在将如下所示:
<TextBlock Text="{Binding Person.FullName}" />
回答by rudigrobler
I haven't check it but can you also try the following
我还没有检查过,但你也可以试试下面的
<TextBlock Text="{Binding Path=/, Converter={StaticResource personNameConverter}}" />
回答by Arcturus
You can also use a MultiBinding.. Bind to the Person object, the FirstName and LastName. That way, the value gets updated as soon as FirstName or LastName throws the property changed event.
您还可以使用 MultiBinding.. 绑定到 Person 对象的 FirstName 和 LastName。这样,一旦 FirstName 或 LastName 引发属性更改事件,值就会更新。
<MultiBinding Converter="{IMultiValueConverter goes here..}">
<Binding />
<Binding Path="FirstName" />
<Binding Path="LastName" />
</MultiBinding>
Or if you only use the FirstName and LastName, strip the Person object from the binding to something like this:
或者,如果您只使用 FirstName 和 LastName,则将 Person 对象从绑定中剥离为如下所示的内容:
<MultiBinding Converter="{IMultiValueConverter goes here..}">
<Binding Path="FirstName" />
<Binding Path="LastName" />
</MultiBinding>
And the MultiValueConverter looks like this:
MultiValueConverter 看起来像这样:
class PersonNameConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
return values[0].ToString() + " " + values[1].ToString();
}
public object ConvertBack(object[] values, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
But of course, the selected answer works as well, but a MultiBinding works more elegantly...
但是当然,选定的答案也有效,但 MultiBinding 更优雅地工作......