WPF-zwei-Wege-Bindung nicht funktioniert
Habe ich eine Daten-Kontext (UserPreferences
) zugewiesen ist mein Hauptfenster, und ein Textfeld, das bindet zwei-Weg, um eine Eigenschaft in den Daten-Kontext die Eigenschaften (CollectionDevice
) im Rahmen.
Wenn das Fenster geladen wird, wird die textbox nicht binden, um die Eigenschaften in meinem Modell. Ich überprüfen Sie in der debugger, dass die Daten Zusammenhang ist auf das Modell-Objekt und das Modell die Eigenschaften sind richtig zugeordnet. Alles was ich bekomme sind jedoch eine Reihe von textbox ' s mit 0 in.
Wenn ich geben Sie die Daten in der textbox ist, die Daten werden in das Modell. Das Problem passiert nur wenn ich die Daten laden und für die Daten-Rahmen, die das Textfeld nicht aktualisiert.
Wenn ich das Modell speichern, um die Datenbank, die die Daten richtig gespeichert wird aus dem Textfeld. Wenn ich wieder das Modell aus der Datenbank die richtigen Daten angewendet wird. Wenn sich das Modell auf die Daten angewendet Kontext, in meinen Konstruktor wird das Textfeld des datacontext enthält die richtigen Daten und die Eigenschaften zugewiesen werden, wie Sie sein sollten. Das Problem ist die Benutzeroberfläche spiegelt sich das nicht wider.
XAML
<Window.DataContext>
<models:UserPreferences />
</Window.DataContext>
<!-- Wrap pannel used to store the manual settings for a collection device. -->
<StackPanel Name="OtherCollectionDevicePanel">
<StackPanel Orientation="Horizontal">
<TextBlock VerticalAlignment="Center" Margin="10, 10, 0, 0" Text="Baud Rate" />
<TextBox Name="BaudRateTextBox" Text="{Binding Path=SelectedCollectionDevice.BaudRate, Mode=TwoWay}" Margin="10, 10, 0, 0" MinWidth="80" ></TextBox>
</StackPanel>
<WrapPanel>
<TextBlock VerticalAlignment="Center" Margin="10, 10, 0, 0" Text="Com Port" />
<TextBox Text="{Binding Path=SelectedCollectionDevice.ComPort, Mode=TwoWay}" Margin="10, 10, 0, 0" MinWidth="80" ></TextBox>
</WrapPanel>
<WrapPanel>
<TextBlock VerticalAlignment="Center" Margin="10, 10, 0, 0" Text="Data Points" />
<TextBox Text="{Binding Path=SelectedCollectionDevice.DataPoints, Mode=TwoWay}" Margin="10, 10, 0, 0" MinWidth="80" ></TextBox>
</WrapPanel>
<WrapPanel Orientation="Horizontal">
<TextBlock VerticalAlignment="Center" Margin="10, 10, 0, 0" Text="WAAS" />
<CheckBox IsChecked="{Binding Path=SelectedCollectionDevice.WAAS, Mode=TwoWay}" Content="Enabled" Margin="20, 0, 0, 0" VerticalAlignment="Bottom"></CheckBox>
</WrapPanel>
</StackPanel>
Modell <-- Datacontext.
///<summary>
///Provides a series of user preferences.
///</summary>
[Serializable]
public class UserPreferences : INotifyPropertyChanged
{
private CollectionDevice selectedCollectionDevice;
public UserPreferences()
{
this.AvailableCollectionDevices = new List<CollectionDevice>();
var yuma1 = new CollectionDevice
{
BaudRate = 4800,
ComPort = 31,
DataPoints = 1,
Name = "Trimble Yuma 1",
WAAS = true
};
var yuma2 = new CollectionDevice
{
BaudRate = 4800,
ComPort = 3,
DataPoints = 1,
Name = "Trimble Yuma 2",
WAAS = true
};
var toughbook = new CollectionDevice
{
BaudRate = 4800,
ComPort = 3,
DataPoints = 1,
Name = "Panasonic Toughbook",
WAAS = true
};
var other = new CollectionDevice
{
BaudRate = 0,
ComPort = 0,
DataPoints = 0,
Name = "Other",
WAAS = false
};
this.AvailableCollectionDevices.Add(yuma1);
this.AvailableCollectionDevices.Add(yuma2);
this.AvailableCollectionDevices.Add(toughbook);
this.AvailableCollectionDevices.Add(other);
this.SelectedCollectionDevice = this.AvailableCollectionDevices.First();
}
///<summary>
///Gets or sets the GPS collection device.
///</summary>
public CollectionDevice SelectedCollectionDevice
{
get
{
return selectedCollectionDevice;
}
set
{
selectedCollectionDevice = value;
this.OnPropertyChanged("SelectedCollectionDevice");
}
}
///<summary>
///Gets or sets a collection of devices that can be used for collecting GPS data.
///</summary>
[Ignore]
[XmlIgnore]
public List<CollectionDevice> AvailableCollectionDevices { get; set; }
public event PropertyChangedEventHandler PropertyChanged;
///<summary>
///Notifies objects registered to receive this event that a property value has changed.
///</summary>
///<param name="propertyName">The name of the property that was changed.</param>
protected virtual void OnPropertyChanged(string propertyName)
{
if (this.PropertyChanged != null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
CollectionDevice <-- Wo der text box bindet.
///<summary>
///CollectionDevice model
///</summary>
[Serializable]
public class CollectionDevice : INotifyPropertyChanged
{
///<summary>
///Gets or sets the COM port.
///</summary>
private int comPort;
///<summary>
///Gets or sets a value indicating whether [waas].
///</summary>
private bool waas;
///<summary>
///Gets or sets the data points.
///</summary>
private int dataPoints;
///<summary>
///Gets or sets the baud rate.
///</summary>
private int baudRate;
///<summary>
///Gets or sets the name.
///</summary>
public string Name { get; set; }
///<summary>
///Gets or sets the COM port.
///</summary>
public int ComPort
{
get
{
return this.comPort;
}
set
{
this.comPort= value;
this.OnPropertyChanged("ComPort");
}
}
///<summary>
///Gets or sets the COM port.
///</summary>
public bool WAAS
{
get
{
return this.waas;
}
set
{
this.waas = value;
this.OnPropertyChanged("WAAS");
}
}
///<summary>
///Gets or sets the COM port.
///</summary>
public int DataPoints
{
get
{
return this.dataPoints;
}
set
{
this.dataPoints = value;
this.OnPropertyChanged("DataPoints");
}
}
///<summary>
///Gets or sets the COM port.
///</summary>
public int BaudRate
{
get
{
return this.baudRate;
}
set
{
this.baudRate = value;
this.OnPropertyChanged("BaudRate");
}
}
public event PropertyChangedEventHandler PropertyChanged;
///<summary>
///Notifies objects registered to receive this event that a property value has changed.
///</summary>
///<param name="propertyName">The name of the property that was changed.</param>
protected virtual void OnPropertyChanged(string propertyName)
{
if (this.PropertyChanged != null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
public override string ToString()
{
return this.Name;
}
}
Kann mir jemand zeigen in die richtige Richtung? Ich nehme an das Problem ist meine Bindung in XAML; ich kann es nicht finden obwohl. Ich brauche es, um zwei-Wege gebunden, weil die Daten jederzeit ändern können, während die apps, die Leben in das Modell (Datenbank wird aktualisiert durch Synchronisierung) und das UI muss, um diese änderungen wiederzugeben, doch kann der Benutzer die änderungen auf das Modell anzuwenden, die über die Benutzeroberfläche.
Update 1
Habe ich versucht zu zwingen das Textfeld databind-Methode aktualisiert werden, aber das funktionierte nicht so gut.
BindingExpression be = this.BaudRateTextBox.GetBindingExpression(TextBox.TextProperty);
be.UpdateSource();
Ich habe auch versucht die Einstellung der UpdateSourceTrigger
zu PropertyChanged
und das schien nicht das problem zu beheben entweder.
<TextBox Name="BaudRateTextBox" Text="{Binding Path=SelectedCollectionDevice.BaudRate, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Margin="10, 10, 0, 0" MinWidth="80" ></TextBox>
Update 2
Versuchte ich zu Folgen zusammen mit einigen - Dokumentation von Microsoft und es scheint nicht das Problem zu beheben. Die Werte bleiben 0, wenn das Fenster geladen wird. Die Bindung wird nicht aktualisiert, nachdem ich die Wiederherstellung des Zustandes des Objekts aus der Datenbank. Verbindlich ist verkabelt, aber, weil, wie ich Daten eingeben, die Daten-Kontext wird aktualisiert. Für einige Grund, es wirkt wie Eine Weg, wenn ich habe es auf Zwei-Wege.
Update 3
Ich versuchte, verschieben Sie den code in das Fenster geladen-Ereignis und aus dem Konstruktor, aber das schien nicht zu helfen. Etwas, was ich interessant fand ist, dass das PropertyChanged-event nicht gefeuert, die während der Deserialisierung. Ich glaube nicht, dass es darauf ankommt, in diesem Fall, weil das Objekt vollständig wiederhergestellt ist richtig und ich habe dann einfach ordnen Sie die Daten, die Kontext sowieso. Ich zog den Daten-Kontext aus der XAML-und in der WindowLoaded, um zu testen, ob die XAML-war das problem. Das Ergebnis war das gleiche.
private void WindowLoaded(object sender, RoutedEventArgs e)
{
//Restore our preferences state.
var preferences = new UserPreferenceCommands();
Models.UserPreferences viewModel = new Models.UserPreferences();
//Set up the event handler before we deserialize.
viewModel.PropertyChanged += viewModel_PropertyChanged;
preferences.LoadPreferencesCommand.Execute(viewModel);
//At this point, viewModel is a valid object. All properties are set correctly.
viewModel = preferences.Results;
//After this step, the UI still shows 0's in all of the text boxs. Even though the values are not zero.
this.DataContext = viewModel;
}
//NEVER gets fired from within the WindowLoaded event.
void viewModel_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
MessageBox.Show("Property changed!");
}
//This changes the model properties and is immediately reflected in the UI. Why does this not happen within the WindowLoaded event?
private void TestButtonClickEvent(object sender, RoutedEventArgs e)
{
var context = this.DataContext as Models.UserPreferences;
context.SelectedCollectionDevice.ComPort = 1536;
}
Update 4 - Problem identifiziert
Ich habe das problem erkannt, aber dennoch eine Lösung. Der ganze Sinn der Datenbindung ist, so dass ich nicht haben, führen Sie diese manuelle Zuordnung. Gibt es etwas falsch mit meinem INotify-Implementierungen?
private void WindowLoaded(object sender, RoutedEventArgs e)
{
//Restore our preferences state.
var preferences = new UserPreferenceCommands();
Models.UserPreferences viewModel = new Models.UserPreferences();
//Set up the event handler before we deserialize.
viewModel.PropertyChanged += viewModel_PropertyChanged;
preferences.LoadPreferencesCommand.Execute(viewModel);
//At this point, viewModel is a valid object. All properties are set correctly.
viewModel = preferences.Results;
//After this step, the UI still shows 0's in all of the text boxs. Even though the values are not zero.
this.DataContext = viewModel;
//SOLUTION: - Setting the actual property causes the UI to be reflected when the window is initialized; setting the actual data context does not. Why? Also note that I set this property and my PropertyChanged event handler still does not fire.
((Models.UserPreferences) DataContext).SelectedCollectionDevice = viewModel.SelectedCollectionDevice;
}
List<CollectionDevice>
zu ObservableCollection<CollectionDevice>
Es gibt sogar eine eigene Klasse, die ich verwenden, die unterstützt die multi-threaded-updates: code.google.com/p/mutinyirc/source/browse/MvvmFoundation.Wpf/..., wenn Sie es brauchen.
Danke @Ekoostik, der Grund, warum ich es nicht als beobachtbar ist, weil es nicht beachtet werden müssen. Die Sammlung ist innerhalb der Objekte Konstruktor und es wird sich nie ändern. Es ist gefüllt mit vorbestimmten Sammlung von Geräten. Der Letzte Eintrag ist ein Gerät mit dem Namen "Anderen", die dann ermöglicht dem Benutzer das eingeben von benutzerdefinierten Daten. Das ist, warum die SelectedCollectionDevice beobachtet wird, aber nicht die AvailableCollectionDevice.
Ich copy / Paste den code mit keine änderungen und es funktioniert alles Prima. Der einzige Unterschied... ich halt manuell zugewiesen datacontext im Fenster-Konstruktor in der code-behind. Ich auch Hinzugefügt eine Schaltfläche, die updates eine Eigenschaft, auf die SelectedCollectionDevice und der Blick spiegelt die Veränderung. Ich weiß nicht, was das problem ist, aber ich hoffe, dies kann Ihnen helfen, zu Debuggen.
Hi @Kevin danke für den Tipp. Ich habe versucht, das hinzufügen einer Schaltfläche auf der Benutzeroberfläche und dass es einen Wert zuweisen eine der Eigenschaften innerhalb
SelectedCollectionDevice
und es aktualisiert das UI richtig. Ich zog auch den Daten-Kontext aus dem XAML und in den Konstruktor. Das Endergebnis ist, dass das Objekt noch nicht richtig gebunden, wenn das Fenster geladen wird. Das Textfeld leer sind. Alle updates, nachdem das Fenster geladen ist, wird sofort dargestellt, die auf das Textfeld, nur nicht während der Belastung. Irgendwelche anderen Ideen?InformationsquelleAutor Johnathon Sullinger | 2014-03-07
Du musst angemeldet sein, um einen Kommentar abzugeben.
Standardmäßig die
Text
- Eigenschaft der TextBox wird nur aktualisiert, wenn der Fokus verloren gegangen ist. Dies ist das standardmäßige Verhalten. Haben Sie überprüft es mit Ihrem DataContext?Wenn Sie möchten, überschreiben Sie dieses Verhalten, Sie müssen die Eigenschaft
UpdateSourceTrigger
in dieser Weise:Einstellung
UpdateSourceTrigger
's WertPropertyChanged
, die änderung in der TextBox, wenn Sie ändern Sie den Wert Ihres gebundene Eigenschaft, sobald sich der text ändert.Ein nützliches tutorial über die Verwendung von
UpdateSourceTrigger
Eigenschaft ist hier.Sorry für meine späte Antwort! Ich bin froh, dass Sie festen!
+1 Die Einfachheit und hohe Verallgemeinerung dieser Antwort hat mir geholfen. Ich wollte das Standardverhalten und hatte einfach
{Binding MyProp}
hatte aber explizit beideMode
zuTwoWay
undUpdateSourceTrigger
zuLostFocus
Mein Szenario ist, bin ich auch Bindung oben Bindung wie selectedRow. Aber ich habe ein Problem, wenn klar meine textbox, und ich werde klicken Sie auf Abbrechen. Sie werden weitergeleitet auf main usercontrol. danach werde ich Doppel-klicken Sie in derselben Zeile das Textfeld leer ist. hier, ich bin nicht der Datensatz gespeichert wird. es müssen zum abrufen des Werts aus dem Modell ausgewählt row-Objekt und binden Sie an die textbox. aber, es ist zu scheitern. Was muss hier getan werden? kann u bitte klären Sie mich. Danke!
Beim Lesen der ersten Zeile war genug 😀
InformationsquelleAutor Alberto Solano
Okay, ich war in der Lage, um zu bestimmen, das problem und bekommen es behoben. Es stellte sich heraus, dass eine Zusammenstellung der Dinge, die dies verursacht.
Erste, meinem Modell.
UserPreferences <-- MainWindow Daten gebunden.
In der Set-Methode für die
SelectedCollectionDevice
ich war nicht auf der Suche, um zu sehen, wenn das ausgewählte Gerät wurde anderen. Alle anderen Geräte (yuma1, panasonic etc.) haben vorher festgelegten Eigenschaftswerte, die sich nie geändert. Wenn der Benutzer wählt "Andere" die textbox ' s angezeigt werden und Sie können manuell die Daten eingeben. Das problem war, dass, wenn die manuell eingegebenen Daten aus der Datenbank wiederhergestellt wird, während das Fenster laden, ich war nicht die Zuordnung der benutzerdefinierten Daten inSelectedCollectionDevice
zu dem entsprechenden Objekt in der Sammlung.Während der laden-Fenster, die
Combobox.SelectedItem
wurde auf den index derSelectedCollectionDevice
. DieCombobox.ItemsSource
wurde eingestellt, um dieAvailableCollectionDevices
Sammlung.Wenn der obige code ausgeführt wird, wird die combo-box zieht den Standard-Objekt aus der zugehörigen Datenquelle, der hat alle Werte auf null gesetzt. In der combo-box
SelectionChanged
event, an dem ich die Zuordnung der Daten KontextSelectedCollectionDevice
auf die null würde out-Element im Kombinationsfeld zugeordnet.Also lange Geschichte kurz, ich habe den oben stehenden code in die Set-Methode für die
SelectedCollectionDevice
anwenden der Wert derAvailableCollectionDevices
Liste<>. Auf diese Weise, wenn die combo-box hat den "Anderen" Wert " ausgewählt wird, zieht er den Wert aus der Erhebung mit den richtigen Daten. Bei der Deserialisierung, ich bin nur Deserialisieren derSelectedCollectionDevice
und nicht in der Liste<> was ist der Grund, warum die Daten immer überschrieben, wenn das Fenster zum ersten mal geladen wird.Dies erklärt auch, warum re-zuweisen der data-Kontext
SelectedCollectionDevice
Eigenschaft mit der lokalenviewModel.SelectedCollectionDevice
tätig war. Ich war anstelle der zero-D-Objekt im Zusammenhang mit der combo-box, die hatte den Daten-Kontext während derSelectionChanged
Veranstaltung. Ich bin nicht in der Lage, den DataContext im XAML-Code und entfernen Sie die manuelle Zuordnung.Dank für all die Hilfe, es hat mir geholfen, verengen meine Debuggen, bis ich endlich das Problem gelöst. Sehr geschätzt!
InformationsquelleAutor Johnathon Sullinger
Nicht als Antwort, sondern wollte nach den code, das funktioniert auf meiner Maschine zu helfen, OP...
Komplette xaml-Seite...
Vollständige code hinter...
Ich zog auch meinen restore code und Wert-Zuweisungen aus dem Konstruktor und in das Loaded-Ereignis für das Fenster, um zu sehen, ob es einfach nur ein Problem mit dem timing. Am Ende der Geladenen Veranstaltung, die ich zuweisen das gültige Objekt, um die Daten Kontext und die Benutzeroberfläche zeigt nur noch Nullen.
Ich sehe... ich habe nur erwähnt, dass Sie könnten versuchen, die änderung der Eigenschaften, die außerhalb der Seite zu laden... aber dann sah dieser Kommentar gelöscht und mein Kommentar oben.
Das Objekt deserialisiert wird aus einem gespeicherten Zustand befindet, und dann Daten zugeordnet Kontext. An der Stelle, wo ich die Aufgabe, das Objekt ist vollständig deserialisiert und auf gültige Werte. Ich bin ziemlich ratlos.
Wenn ich die Deserialisierung, meine PropertyChanged-Ereignis nicht gefeuert. Würde das etwas zu tun mit es? Ich würde denken, dass wäre es nicht, wenn man bedenkt, dass ich das Objekt zuweisen, wie meine Daten-Kontext nachdem das Objekt korrekt deserialisiert und die Eigenschaften sind richtig eingestellt. Ich dachte, ich würde erwähnen, das sowieso in den Fall könnte es haben einige Auswirkungen.
InformationsquelleAutor Kevin
Ich hatte das gleiche Problem. Mein problem war die Bindung Eigenschaft name war falsch. Wenn Sie einen Blick auf das Ausgabe-Fenster können Sie sehen, alle verbindliche Fehler während der Laufzeit.
InformationsquelleAutor user1546570