stacklayout data content calculated bindingcontext c# wpf data-binding converter

c# - content - WPF Data Binding y IValueConverter



xamarin ivalueconverter (4)

¿Por qué es que cuando uso un convertidor en mi expresión de enlace en WPF, el valor no se actualiza cuando los datos se actualizan?

Tengo un modelo de datos de persona simple:

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

Mi expresión vinculante se ve así:

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

Mi convertidor se ve así:

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

Si enlace los datos sin un convertidor, funciona de maravilla:

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

¿Qué me estoy perdiendo?

EDITAR: solo para aclarar algunas cosas, tanto Joel como Alan tienen razón con respecto a la interfaz INotifyPropertyChanged que debe implementarse. En realidad, realmente lo implemento, pero todavía no funciona.

No puedo usar varios elementos de TextBlock porque estoy intentando vincular el título de la ventana con el nombre completo, y el título de la ventana no toma una plantilla.

Finalmente, es una opción agregar una propiedad compuesta "FullName" y vincularla, pero todavía me pregunto por qué no ocurre la actualización cuando el enlace usa un convertidor. Incluso cuando pongo un punto de interrupción en el código del convertidor, el depurador simplemente no llega cuando se realiza una actualización a los datos subyacentes :-(

Gracias, Uri


Para que el enlace se actualice, su clase personal debe implementar INotifyPropertyChanged para que el enlace sepa que las propiedades del objeto se han omitido. También puede salvarse del convertidor adicional proporcionando una propiedad 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 } }

Su enlace ahora se verá así:

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


No lo he comprobado pero también puedes probar lo siguiente

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


También puede usar un enlace múltiple. Enlace al objeto Person, FirstName y LastName. De esta forma, el valor se actualiza tan pronto como FirstName o LastName arrojen el evento de propiedad modificada.

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

O si solo usa FirstName y LastName, quite el objeto Person del enlace a algo como esto:

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

Y el MultiValueConverter se ve así:

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

Pero, por supuesto, la respuesta seleccionada también funciona, pero un MultiBinding funciona de manera más elegante ...


(ver ediciones a continuación; último: n.º 2)

No se está actualizando porque su objeto Person no puede notificar nada de que el valor de FirstName o LastName haya cambiado. Ver esta pregunta .

Y así es cómo implementa INotifyPropertyChanged . ( Actualizado, vea Edición 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 ) ); } } } }

Editar 1

En realidad, dado que buscas el primer nombre y la actualización del apellido, y Path=FirstName funciona bien, no creo que necesites el convertidor. Múltiples TextBlocks son igual de válidos y, de hecho, pueden funcionar mejor cuando se localiza en un idioma de derecha a izquierda.

Editar 2

Lo he descubierto. No se está notificando que las propiedades se hayan actualizado porque es vinculante para el objeto en sí, no como una de esas propiedades. Incluso cuando hice Person a DependencyObject e hice FirstName y LastName DependencyProperties , no se actualizaría.

Tendrá que usar una propiedad FullName , y he actualizado el código de la clase Person anterior para reflejar eso. Entonces puedes vincular el Title . ( Nota: configuré el objeto Person como el DataContext la Window ).

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

Si está editando los nombres en un TextBox y desea que el nombre cambiado se refleje inmediatamente en lugar de cuando el TextBox pierde el foco, puede hacer esto:

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

Sé que no quería usar una propiedad FullName , pero cualquier cosa que logre lo que desea probablemente sea un dispositivo Rube Goldberg. Como implementar INotifyPropertyChanged y una propiedad Person en la clase Window , hacer que Window escuche en el evento PropertyChanged para INotifyPropertyChanged el evento PropertyChanged la Window y usar un enlace relativo como el siguiente. También habría establecido la propiedad Person antes de InitializeComponent() o activa PropertyChanged después de configurar la propiedad Person para que aparezca, por supuesto. (De lo contrario, será null durante InitializeComponent() y necesita saber cuándo es una 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>