WPF数据绑定和IValueConverter

uri*_*ini 13 c# data-binding wpf converter

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

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

class Person : INotifyPropertyChanged
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

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

<TextBlock Text="{Binding Converter={StaticResource personNameConverter}" />
Run Code Online (Sandbox Code Playgroud)

我的转换器看起来像这样:

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();
    }
}
Run Code Online (Sandbox Code Playgroud)

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

<TextBlock Text="{Binding Path=FirstName}" />
<TextBlock Text="{Binding Path=LastName}" />
Run Code Online (Sandbox Code Playgroud)

我错过了什么?

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

我不能使用多个TextBlock元素,因为我正在尝试将Window Title绑定到全名,而Window Title不会使用模板.

最后,可以选择添加复合属性"FullName"并绑定到它,但我仍然想知道为什么绑定使用转换器时不会发生更新.即使我在转换器代码中设置了一个断点,当对底层数据进行更新时,调试器也无法实现:-(

谢谢,乌里

Arc*_*rus 14

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

<MultiBinding Converter="{IMultiValueConverter goes here..}">
    <Binding />
    <Binding Path="FirstName" />
    <Binding Path="LastName" />
</MultiBinding>
Run Code Online (Sandbox Code Playgroud)

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

<MultiBinding Converter="{IMultiValueConverter goes here..}">
    <Binding Path="FirstName" />
    <Binding Path="LastName" />
</MultiBinding>
Run Code Online (Sandbox Code Playgroud)

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();
    }
}
Run Code Online (Sandbox Code Playgroud)

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


Joe*_*ant 11

(见下面的编辑;最新:#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 ) );
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

编辑1

实际上,由于你是在更名的第一个名字和名字之后,Path=FirstName并且这样的工作很好,我认为你根本不需要转换器.多个TextBlocks同样有效,并且当您使用从右到左的语言本地化时,实际上可以更好地工作.

编辑2

我已经明白了.没有通知属性已更新,因为它绑定到对象本身,而不是其中一个属性.即使我做Person了一个DependencyObject和制作FirstNameLastName DependencyProperties,它不会更新.

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

Title="{Binding Path=FullName, Mode=OneWay}"
Run Code Online (Sandbox Code Playgroud)

如果您正在编辑a中的名称TextBox并希望立即反映名称而不是TextBox失去焦点,则可以执行以下操作:

<TextBox Name="FirstNameEdit"
    Text="{Binding Path=FirstName, UpdateSourceTrigger=PropertyChanged}" />
Run Code Online (Sandbox Code Playgroud)

我知道你不想使用一个FullName属性,但任何可以实现你想要的东西都可能是一个Rube Goldberg设备.如实施INotifyPropertyChangedPerson对房地产Window类本身,具有Window侦听PropertyChanged,以触发事件WindowPropertyChanged事件,并使用相对类似下面结合.您也可以在设置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>
Run Code Online (Sandbox Code Playgroud)