Domanda

Perché quando uso un convertitore nella mia espressione di associazione in WPF, il valore non viene aggiornato quando i dati vengono aggiornati.

Ho un modello di dati Person semplice:

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

La mia espressione di associazione è simile a questa:

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

Il mio convertitore è simile al seguente:

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

Se associo i dati senza un convertitore, funziona alla grande:

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

Cosa mi sto perdendo?

EDIT: Solo per chiarire alcune cose, sia Joel che Alan hanno ragione riguardo all'interfaccia INotifyPropertyChanged che deve essere implementata. In realtà lo realizzo davvero, ma non funziona ancora.

Non riesco a usare più elementi TextBlock perché sto cercando di associare il titolo della finestra al nome completo e il titolo della finestra non accetta un modello.

Infine, è un'opzione per aggiungere una proprietà composta " FullName " e si legano ad esso, ma mi chiedo ancora perché l'aggiornamento non si verifica quando l'associazione utilizza un convertitore. Anche quando inserisco un punto di interruzione nel codice del convertitore, il debugger non ci arriva quando viene eseguito un aggiornamento dei dati sottostanti :-(

Grazie, Uri

È stato utile?

Soluzione

(vedi le modifiche seguenti; ultime: # 2)

Non si aggiorna perché il tuo oggetto Person non è in grado di notificare nulla che sia cambiato il valore di FirstName o LastName . Vedi questa domanda .

Ed ecco come implementare INotifyPropertyChanged . ( Aggiornato, vedi Modifica 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 ) );
            }
        }
    }
}

Modifica 1

In realtà, dato che stai cercando l'aggiornamento del nome e del cognome, e Path = FirstName e che funziona bene, non penso che avrai bisogno del convertitore. Più TextBlocks sono altrettanto validi e possono effettivamente funzionare meglio quando ti stai localizzando in una lingua da destra a sinistra.

Modifica 2

L'ho capito. Non viene notificato che le proprietà sono state aggiornate perché è vincolante per l'oggetto stesso, non una di quelle proprietà. Anche quando ho fatto Person un DependencyObject e ho creato FirstName e LastName DependencyProperties , non si aggiorna.

Tu dovrai utilizzare una proprietà FullName e ho aggiornato il codice della classe Person per riflettere questo. Quindi puoi associare il Title . ( Nota: ho impostato l'oggetto Person come Window del DataContext .)

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

Se stai modificando i nomi in un TextBox e vuoi che il nome sia cambiato immediatamente invece che quando TextBox perde lo stato attivo, puoi farlo:

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

So che non volevi usare una proprietà FullName , ma qualsiasi cosa che realizzasse ciò che desideri sarebbe probabilmente un po 'un dispositivo Rube Goldberg. Come l'implementazione di INotifyPropertyChanged e una proprietà Person sulla classe Window , con la Window in ascolto sul Evento PropertyChanged per generare l'evento Window del PropertyChanged e utilizzare un relativo binding come il seguente. Avresti anche impostato la proprietà Person prima di InitializeComponent () o attivato PropertyChanged dopo aver impostato la proprietà Person in modo che si presenta, ovviamente. (Altrimenti sarà null durante InitializeComponent () e deve sapere quando si tratta di 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>

Altri suggerimenti

Puoi anche usare un MultiBinding .. Bind all'oggetto Person, FirstName e LastName. In questo modo, il valore viene aggiornato non appena FirstName o LastName generano l'evento modificato dalla proprietà.

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

O se usi solo FirstName e LastName, elimina l'oggetto Person dall'associazione a qualcosa del genere:

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

E MultiValueConverter si presenta così:

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

Ma ovviamente anche la risposta selezionata funziona, ma un MultiBinding funziona in modo più elegante ...

Per aggiornare l'associazione, la classe personale deve implementare INotifyPropertyChanged per far sapere all'associazione che le proprietà dell'oggetto sono state udpate. Puoi anche salvarti dal convertitore aggiuntivo fornendo una proprietà 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
    }
}

Il tuo Binding ora apparirà così:

<TextBlock Text="{Binding Person.FullName}" />

Non l'ho verificato ma puoi anche provare quanto segue

<TextBlock Text="{Binding Path=/, Converter={StaticResource personNameConverter}}" />
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top