Der WPF-Datenbindung und IValueConverter
-
05-07-2019 - |
Frage
Warum ist es so, dass wenn ich mit einem Konverter in mein verbindliches Ausdruck in WPF, wird der Wert nicht aktualisiert, wenn die Daten aktualisiert werden.
Ich habe eine einfache Person Daten Modell:
class Person : INotifyPropertyChanged
{
public string FirstName { get; set; }
public string LastName { get; set; }
}
Meine Bindung zum Ausdruck sieht wie folgt aus:
<TextBlock Text="{Binding Converter={StaticResource personNameConverter}" />
Mein Konverter sieht wie folgt aus:
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();
}
}
Wenn ich die Daten gebunden werden, ohne einen Konverter, es funktioniert Super:
<TextBlock Text="{Binding Path=FirstName}" />
<TextBlock Text="{Binding Path=LastName}" />
Was vermisse ich?
EDIT:Nur um zu klären ein paar Dinge, beide, Joel und Alan sind richtig in Bezug auf die INotifyPropertyChanged-interface, die muss umgesetzt werden.In Wirklichkeit habe ich tatsächlich implementieren, aber es funktioniert immer noch nicht.
Ich kann nicht mehrere TextBlock-Elemente, weil ich versuche, mich zu binden, Fenster-Titel, um den vollständigen Namen und den Titel des Fensters nicht nehmen eine Vorlage aus.
Schließlich ist es eine option für das hinzufügen eine compound-Eigenschaft "FullName" und zu binden, aber ich bin immer noch Fragen, warum die Aktualisierung geschieht nicht, wenn die Bindung verwendet ein Konverter.Sogar, wenn ich einen Haltepunkt in den code converter, debugger einfach nicht bekommen es, wenn ein update durchgeführt, um die zugrunde liegenden Daten :-(
Vielen Dank, Uri
Lösung
(siehe änderungen unten;neueste:#2)
Es ist nicht aktualisieren, weil Ihre Person
Objekt ist nicht in der Lage, Benachrichtigen alles, was der Wert von FirstName
oder LastName
hat sich geändert. Siehe diese Frage.
Und hier ist, wie Sie umzusetzen INotifyPropertyChanged
. (Aktualisiert, finden Sie unter Bearbeiten 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 ) );
}
}
}
}
Bearbeiten 1
Tatsächlich, da Sie nach dem Vornamen und Nachnamen aktualisieren, und Path=FirstName
und so funktioniert Prima, ich glaube nicht, benötigen Sie den Konverter an alle.Mehrere TextBlocks
nur als gültig, und kann tatsächlich besser funktionieren, wenn Sie die Lokalisierung zu einer rechts-nach-Links-Sprache.
Bearbeiten 2
Ich habe es herausgefunden.Es ist nicht darüber informiert, dass die Eigenschaften aktualisiert, da es die Bindung an das Objekt selbst, nicht eine von diesen Eigenschaften.Auch wenn ich aus Person
ein DependencyObject
und aus FirstName
und LastName
DependencyProperties
, wäre es nicht aktualisieren.
Sie wird die Verwendung einer FullName
Eigentum, und ich habe den code aktualisieren, der Person
Klasse oben zu reflektieren.Dann können Sie die binden Title
. (Hinweis: Ich habe den Person
Objekt als Window
's DataContext
.)
Title="{Binding Path=FullName, Mode=OneWay}"
Wenn Sie das Bearbeiten der Namen in einer TextBox
und der name änderte sich sofort statt, wenn die TextBox
den Fokus verliert, können Sie dies tun:
<TextBox Name="FirstNameEdit"
Text="{Binding Path=FirstName, UpdateSourceTrigger=PropertyChanged}" />
Ich kenne Sie nicht wollen, zu verwenden eine FullName
Eigenschaft, sondern etwas, dass würde erreichen, was Sie wollen, wäre wahrscheinlich ein bisschen von einer Rube-Goldberg-Gerät.Wie der Umsetzung INotifyPropertyChanged
und eine Person
Eigenschaft der Window
Klasse selbst, mit der Window
hören Sie auf die PropertyChanged
Ereignis, um das Feuer Window
's PropertyChanged
Ereignis, und verwenden eine relative Bindung wie die folgenden.Hättest du auch haben die Person
Eigenschaft vor InitializeComponent()
oder Feuer PropertyChanged
nach der Einstellung der Person
Immobilie, so dass es sich zeigt, natürlich.(Sonst wird es werden null
während InitializeComponent()
und muss wissen wenn es ein 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>
Andere Tipps
Sie können auch eine Multibinding .. binden an das Objekt Person, die Vor- und den Nachnamen verwenden. Auf diese Weise wird der Wert, sobald Vorname oder Nachname wirft die Eigenschaft geänderte Ereignis aktualisiert.
<MultiBinding Converter="{IMultiValueConverter goes here..}">
<Binding />
<Binding Path="FirstName" />
<Binding Path="LastName" />
</MultiBinding>
Oder wenn Sie nur das Vorname verwenden und Nachname, Streifen der Person-Objekt aus der Bindung an etwas wie folgt aus:
<MultiBinding Converter="{IMultiValueConverter goes here..}">
<Binding Path="FirstName" />
<Binding Path="LastName" />
</MultiBinding>
Und die MultiValueConverter sieht wie folgt aus:
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();
}
}
Aber natürlich die gewählte Antwort funktioniert auch, aber ein Multibinding arbeitet elegante ...
Damit die Bindung aktualisiert werden, muss Ihre Person Klasse INotifyPropertyChanged implementieren die Bindung zu können, dass die Eigenschaften des Objekts haben udpated worden. Sie können auch Sie sich von den zusätzlichen Konverter sparen, indem ein Fullname-Eigenschaft bereitstellt.
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
}
}
Ihre Bindung wird nun wie folgt aussehen:
<TextBlock Text="{Binding Person.FullName}" />
Ich habe überprüfen Sie es nicht, aber Sie können auch versuchen, die folgende
<TextBlock Text="{Binding Path=/, Converter={StaticResource personNameConverter}}" />