14

为什么当我在 WPF 中的绑定表达式中使用转换器时,更新数据时值不会更新。

我有一个简单的 Person 数据模型:

class Person : INotifyPropertyChanged
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
}

我的绑定表达式如下所示:

<TextBlock Text="{Binding Converter={StaticResource personNameConverter}" />

我的转换器如下所示:

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

如果我在没有转换器的情况下绑定数据,效果很好:

<TextBlock Text="{Binding Path=FirstName}" />
<TextBlock Text="{Binding Path=LastName}" />

我错过了什么?

编辑:为了澄清一些事情,Joel 和 Alan 对于需要实现的 INotifyPropertyChanged 接口都是正确的。实际上我确实实现了它,但它仍然不起作用。

我不能使用多个 TextBlock 元素,因为我正在尝试将 Window Title 绑定到全名,并且 Window Title 不采用模板。

最后,添加复合属性“FullName”并绑定到它是一个选项,但我仍然想知道为什么当绑定使用转换器时不会发生更新。即使我在转换器代码中放置了一个断点,当对基础数据进行更新时,调试器也不会到达那里:-(

谢谢,乌里

4

4 回答 4

14

您还可以使用 MultiBinding.. 绑定到 Person 对象,FirstName 和 LastName。这样,只要 FirstName 或 LastName 引发属性更改事件,该值就会立即更新。

<MultiBinding Converter="{IMultiValueConverter goes here..}">
    <Binding />
    <Binding Path="FirstName" />
    <Binding Path="LastName" />
</MultiBinding>

或者,如果您只使用 FirstName 和 LastName,则将 Person 对象从绑定中剥离,如下所示:

<MultiBinding Converter="{IMultiValueConverter goes here..}">
    <Binding Path="FirstName" />
    <Binding Path="LastName" />
</MultiBinding>

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

但是,当然,选择的答案也可以,但是 MultiBinding 可以更优雅地工作......

于 2008-10-07T10:36:26.763 回答
12

(见下面的编辑;最新:#2)

它没有更新,因为您的Person对象无法通知任何值FirstNameLastName已更改的内容。看到这个问题

这就是你如何实现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 ) );
            }
        }
    }
}

编辑 1

实际上,由于您在更新名字和姓氏之后,Path=FirstName并且这样的工作正常,我认为您根本不需要转换器。多个TextBlocks同样有效,并且当您本地化为从右到左的语言时实际上可以更好地工作。

编辑 2

我已经想通了。没有通知属性已更新,因为它绑定到对象本身,而不是这些属性之一。即使我做Person了 a DependencyObjectand made FirstNameand LastName DependencyProperties,它也不会更新。

不得不使用一个FullName属性,我已经更新了Person上面类的代码以反映这一点。然后就可以绑定了Title。(注意:我已将Person对象设置为Window's DataContext。)

Title="{Binding Path=FullName, Mode=OneWay}"

如果您正在编辑 a 中的名称TextBox并希望立即更改名称而不是在失去焦点时反映TextBox,您可以这样做:

<TextBox Name="FirstNameEdit"
    Text="{Binding Path=FirstName, UpdateSourceTrigger=PropertyChanged}" />

我知道您不想使用FullName属性,但是任何可以实现您想要的东西都可能有点像 Rube Goldberg 设备。例如实现类本身INotifyPropertyChangedPerson属性,监听事件以触发'事件,并使用如下的相对绑定。当然,您还可以在设置属性之前设置属性或在设置属性之后触发它,以便它显示出来。(否则它将在期间并且需要知道它何时是。)WindowWindowPropertyChangedWindowPropertyChangedPersonInitializeComponent()PropertyChangedPersonnullInitializeComponent()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>
于 2008-10-06T16:29:48.937 回答
1

为了更新绑定,您的人员类需要实现 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
    }
}

您的绑定现在将如下所示:

<TextBlock Text="{Binding Person.FullName}" />
于 2008-10-06T16:47:02.177 回答
0

我还没有检查,但你也可以尝试以下

<TextBlock Text="{Binding Path=/, Converter={StaticResource personNameConverter}}" />
于 2008-10-07T06:20:52.503 回答