2009-01-26 9 views
6

Zuerst möchte ich sagen, dass das folgende Beispiel zu stark vereinfacht ist. Angenommen, Sie haben WPF-Steuerelement gebunden.INotifyPropertyChanged Problem

<Window Title="Window1" Height="300" Width="300"> 
<Grid> 
    <StackPanel> 
     <TextBox Text="{Binding Name}" Margin="10"/> 
     <Button HorizontalAlignment="Center" 
     Content="Click Me" Margin="5" 
     Padding="2" Click="OnButtonClick" /> 
    </StackPanel> 
</Grid> 
</Window> 

Fenster wird auf die Klasse Person gebunden, die INotifyPropertyChanged und hat Namen Setter in Form

public string Name 
    { 
     get { return _name; } 
     set 
     { 
      _name = "Some Name"; 
      OnPropertyChanged("Name"); 
     } 
    } 

D. h implementiert _name wird "Some Name" zugewiesen, wenn der Benutzer versucht, ihn von der Benutzeroberfläche zu ändern. Aber dieses Beispiel funktioniert nicht. Ich änderte den Namen in TextBox in einen Wert, um die Schaltfläche zu drücken, um den Fokus auf den Button zu verschieben, und der Wert in TextBox bleibt unverändert, obwohl das PropertyChanged-Ereignis ausgelöst wurde.

Können Sie mir bitte erklären, warum es passiert? Wie ich verstehe PropertyChanged Ereignis zwingt UI, Werte von Eigenschaften neu einzulesen und sie anzuzeigen, aber in meinem Beispiel Wert in datengebundenen Textfeld wird nicht aktualisiert.


Wieder. Ich verstehe, dass dies eine schlechte Umsetzung des Eigentums ist, aber ich möchte wiederholen, dass dies eine zu starke Vereinfachung ist. Es ist nur ein Beispiel. Wie auch immer, PropertyChanged signalisiert, dass die Eigenschaft geändert wurde und sollte aktualisiert werden, tut es aber nicht.

+0

Äh, ich kann falsch sein, aber es sollte nicht sein '{Binding Path = Name}'? –

+1

Nein. Bei Eigenschaftsbindungen können Sie Pfad als Verknüpfung auslassen. –

+1

Dies liegt daran, dass Bindung einen Konstruktor hat, der den Pfad als Parameter verwendet. –

Antwort

8

Das PropertyChanged-Ereignis wird von der TextBox ignoriert, da es der Initiator des Ereignisses ist.

Einige Klarstellung:

Die TextBox (oder die Bindung an die Textbox) weiß, dass es der Initiator ist, weil es das Property Ereignis im gleichen Anruf empfängt. Wenn Sie einen asynchronen Aufruf ausführen, kann das Textfeld (oder die Bindung) nicht wissen, dass es sich um den Initiator handelt. Daher wird das Ereignis so verarbeitet, als ob es von jemand anderem aktualisiert wurde

Wenn Sie der Benutzeroberfläche ein zweites Textfeld hinzufügen, Sie werden sehen, dass sich die 2. TextBox ändert, wenn Sie die erste bearbeiten, und umgekehrt.

+0

Aber warum asynchroner Anruf funktioniert gut? – Oleg

+0

Die TextBox (oder die Bindung in der Textbox) weiß, dass sie der Initiator ist, da sie das PropertyChanged-Ereignis im selben Aufruf empfängt. Durch einen asynchronen Aufruf kann das Textfeld (oder die Bindung) nicht wissen, dass es sich um den Initiator handelt. Daher wird das Ereignis so verarbeitet, als ob es von einem anderen Benutzer aktualisiert wurde – Bubblewrap

0

Der Grund ist, weil Sie den 'Some Name' im Setter fest programmiert haben. Wenn Sie den TextBox-Wert geändert haben, wird der Setter tatsächlich aufgerufen und er setzt wieder "Some Name" als propertyValue, so scheint es nicht in der Benutzeroberfläche geändert zu werden. Setzen Sie _name = value und die Dinge werden nur so funktionieren, wie Sie erwartet haben,

+0

Ich denke OP möchte eigentlich den Wert der Eigenschaft "Some Name" jedes Mal, wenn jemand den Wert ändert, unabhängig davon, was sie eingeben. – matrixugly

0

Wenn ich mich nicht irre, ist das Standard-Bindungsverhalten der Text-Eigenschaft auf der TextBox TwoWay, also sollte dies funktionieren. Sie können ihn zwingen TwoWay in der XAML so zu sein:

<Window Title="Window1" Height="300" Width="300"> 
    <Grid> 
    <StackPanel> 
     <TextBox Text="{Binding Name, Mode=TwoWay}" Margin="10"/> 
     <Button HorizontalAlignment="Center" 
     Content="Click Me" Margin="5" 
     Padding="2" Click="OnButtonClick" /> 
    </StackPanel> 
    </Grid> 
</Window> 

Notiere die Mode=TwoWay in der Binding-Deklaration.

Wenn das nicht funktioniert, vermute ich, dass eine Ausnahme in den Code ausgelöst wird, der das Ereignis auslöst, oder weist die Eigenschaft zu, und Sie sollten danach suchen.

Es scheint eine Möglichkeit zu geben, dass Sie den Aufruf ausführen, um den Wert eines Threads zu ändern, der nicht der UI-Thread ist. Wenn dies der Fall ist, müssen Sie entweder den Aufruf marshalieren, um das Ereignis mit geänderter Eigenschaft im Benutzeroberflächenthread auszulösen, oder den Wert für den Benutzeroberflächenthread ändern.

Wenn ein Objekt an ein UI-Element gebunden ist, müssen Änderungen am Objekt, die sich auf die UI auswirken, im UI-Thread vorgenommen werden.

0
public string MyField 
{ 
    get { return _myField; } 
    set { 
     if (_myField == value) 
      return; 

     _myField = value; 

     OnPropertyChanged("MyField"); 
    } 
} 

Dies ist die ordnungsgemäße Durchführung der Immobilie.

Wenn Sie die Eigenschaft ändern, stellen Sie sicher, dass die EXACT-Instanz des Objekts an ein Steuerelement gebunden ist. Andernfalls wird die Änderung gemeldet, aber das Steuerelement erhält es nie, weil das Steuerelement nicht ordnungsgemäß gebunden ist.

0

Ersetzen Setter in Form

 set 
     { 
      _name = "Some Name"; 
      Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.DataBind, 
       (SendOrPostCallback)delegate { OnPropertyChanged("Name"); }, 
       null); 
     } 

behebt das Problem, aber es ist noch offen. Warum sollte ich einen asynchronen Aufruf anstelle einer synchronen Signalisierung durchführen, dass meine Eigenschaft geändert wurde?

2

Wie Bubblewrap bereits darauf hingewiesen hat, ist dies beabsichtigt - das Textfeld geht davon aus, dass der Setter den Wert nicht ändert, wenn er eine gebundene Eigenschaft auf einen bestimmten Wert setzt. According to Microsoft werden sie dieses Verhalten nicht ändern, da dies den bestehenden Code aufbrechen würde.

Wenn Sie wollen den Wert ändern (ja, es gibt durchaus gute Gründe, dies zu tun), müssen Sie eine Abhilfe verwenden, zum Beispiel durch einen Dummy-Wandler hinzugefügt wird. Es gibt a blog entry (nicht von mir geschrieben), die diese Technik im Detail beschreiben.

4

Die Dummy Abhilfe Konverter von Heinzi vorgeschlagen (beschrieben here) funktioniert nicht, wenn die Bindung Update ist Property. Was aber, wenn wir das brauchen?

Es scheint, dass die Bindung asynchrounous Herstellung der Trick funktioniert, z.B .:

SelectedIndex="{Binding SelectedIndex, IsAsync=True}"