Ich habe einen Datenkontext (UserPreferences
), der meinem Hauptfenster zugeordnet ist, und ein Textfeld, das im Kontext eine bidirektionale Bindung an eine Eigenschaft innerhalb einer der Datenkontexteigenschaften (CollectionDevice
) herstellt.WPF-Zweiwege-Bindung funktioniert nicht
Wenn das Fenster geladen wird, binden die Textfelder nicht an die Eigenschaften in meinem Modell. Ich verifiziere innerhalb des Debuggers, dass der Datenkontext auf das Modellobjekt eingestellt ist und die Eigenschaften des Modells korrekt zugewiesen sind. Alles, was ich bekomme, sind jedoch eine Reihe von Textfeldern mit Nullen.
Wenn ich die Daten in die Textfelder eingabe, werden die Daten im Modell aktualisiert. Das Problem tritt nur auf, wenn ich die Daten lade und sie auf den Datenkontext anwende. Das Textfeld wird nicht aktualisiert.
Wenn ich das Modell in der Datenbank speichern, werden die richtigen Daten aus dem Textfeld gespeichert. Wenn ich das Modell von der Datenbank wiederherstelle, werden die richtigen Daten angewendet. Wenn das Modell auf den Datenkontext in meinem Konstruktor angewendet wird, enthält der Datenkontext des Textfelds die korrekten Daten und die Eigenschaften werden so zugewiesen, wie sie sein sollten. Das Problem ist, dass die Benutzeroberfläche dies nicht widerspiegelt.
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 Textfeld bindet an.
/// <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 in die richtige Richtung zeigen? Ich nehme an, das Problem ist meine Bindung in XAML; Ich kann es jedoch nicht finden. Ich benötige eine bidirektionale Bindung, da sich die Daten jederzeit während der Lebensdauer der App im Modell ändern können (die Datenbank wird über Synchronisationen aktualisiert), und die Benutzeroberfläche muss diese Änderungen widerspiegeln. Der Benutzer kann jedoch Änderungen am Modell vornehmen die Benutzeroberfläche.
Update 1
ich das Textfeld databind zu zwingen versucht, aktualisiert werden, aber das hat nicht so gut funktionieren.
BindingExpression be = this.BaudRateTextBox.GetBindingExpression(TextBox.TextProperty);
be.UpdateSource();
Ich habe auch versucht die UpdateSourceTrigger
zu PropertyChanged
Einstellung und das schien nicht das Problem zu lösen.
<TextBox Name="BaudRateTextBox" Text="{Binding Path=SelectedCollectionDevice.BaudRate, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Margin="10, 10, 0, 0" MinWidth="80" ></TextBox>
Update 2
habe ich versucht, mit einigen documentation from Microsoft zu folgen und es scheint nicht das Problem zu beheben. Die Werte bleiben weiterhin 0, wenn das Fenster geladen wird. Die Bindung wird nicht aktualisiert, nachdem ich den Status des Objekts aus der Datenbank wiederhergestellt habe. Die Bindung ist jedoch verdrahtet, weil, wenn ich Daten eingabe, der Datenkontext aktualisiert wird. Aus irgendeinem Grund verhält es sich wie One-Way, wenn ich es auf Two-Way eingestellt habe.
Update 3
Ich habe versucht, den Code in das Fenster geladen Ereignis und aus dem Konstruktor zu bewegen, aber das schien nicht zu helfen.Interessant ist, dass das PropertyChanged-Ereignis während des Deserialisierungsprozesses nicht ausgelöst wird. Ich denke nicht, dass es in diesem Fall wichtig ist, weil das Objekt vollständig wiederhergestellt ist und ich es dann einfach dem Datenkontext zuordne. Ich habe den Datenkontext aus dem XAML in den WindowLoaded verschoben, um zu testen, ob XAML das Problem war. 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 das Problem identifiziert haben, müssen aber noch eine Auflösung. Der entscheidende Punkt der Datenbindung ist, dass ich diese manuelle Zuweisung nicht durchführen muss. Stimmt etwas nicht mit meinen 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;
}
Der erste, was ich tun würde, ist die Veränderung 'List' auf 'ObservableCollection ' –
EkoostikMartin
Es gibt sogar eine eigene Klasse I verwendet werden, die Updates Multi-Thread unterstützt: https://code.google.com /p/mutinyirc/source/browse/MvvmFoundation.Wpf/MTObservableCollection.cs Falls Sie es brauchen. –
Dank @Ekoostik, der Grund, warum ich es nicht als beobachtbar gemacht habe, ist, weil es nicht beobachtet werden muss. Die Sammlung wird innerhalb des Objektkonstruktors eingerichtet und ändert sich nie. Es ist mit vorbestimmten Sammlungsgeräten gefüllt. Der letzte Eintrag ist ein Gerät, bei dem Name auf "Andere" gesetzt ist und der Benutzer dann benutzerdefinierte Daten eingeben kann. Aus diesem Grund wird das SelectedCollectionDevice beobachtet, nicht jedoch das AvailableCollectionDevice. –