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;

}
Erste Sache, die ich tun würde, ist das ändern 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

Schreibe einen Kommentar