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}" />

私は何が欠けていますか?

編集:いくつかのことを明確にするために、実装する必要のあるINotifyPropertyChangedインターフェイスに関してJoelとAlanの両方が正しいです。実際には実際に実装していますが、それでも機能しません。

ウィンドウタイトルをフルネームにバインドしようとしていて、ウィンドウタイトルがテンプレートを受け取らないため、複数のTextBlock要素を使用できません。

最後に、複合プロパティ "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

私はそれを理解しました。これらのプロパティの 1 つではなく、オブジェクト自体にバインドされているため、プロパティが更新されたことは通知されません。と を作っPersonDependencyObjectとを作っFirstNameLastName DependencyPropertiesも更新されませんでした。

プロパティを使用する必要があり、それを反映するために上記のクラスFullNameのコードを更新しました。Personその後、バインドできますTitle。(注:Personオブジェクトを として設定しましたWindowDataContext)

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

で名前を編集していて、 がフォーカスを失っTextBoxたときではなく、名前の変更をすぐに反映させたい場合は、次のTextBoxようにします。

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

あなたがプロパティを使いたくないのはわかっていますが、FullNameあなたが望むことを達成するものは、おそらくルーブ・ゴールドバーグのデバイスのようなものになるでしょう. クラス自体にINotifyPropertyChangedPersonプロパティを実装し、のイベントを発生させるためにイベントをリッスンし、次のような相対バインディングを使用します。もちろん、プロパティを設定する前にプロパティを設定するか、プロパティを設定した後に起動して、それが表示されるようにします。(それ以外の場合はduringであり、いつが であるかを知る必要があります。)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

バインディングを更新するには、personクラスが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 に答える