Frage

Ich kann nicht scheinen, eine einfache, konkrete Erklärung, wie zu binden Steuerelemente in einer WinForms-Anwendung, um verschachtelte Objekte Bindung unter Verwendung von Daten zu finden. Zum Beispiel:

class MyObject : INotifyPropertyChanged
{
    private string _Name;
    public string Name 
    { 
        get { return _Name; } 
        set 
        { 
            _Name = value; 
            OnPropertyChanged("Name"); 
        }    
    }

    private MyInner _Inner;
    public MyInner Inner 
    { 
       get { return _Inner; } 
       set 
       { 
           _Inner = value; 
           OnPropertyChanged("Inner"); 
       } 
    }

    public event PropertyChangedEventHandler PropertyChanged;
    private void OnPropertyChanged(string propertyName)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

class MyInner : INotifyPropertyChanged
{
    private string _SomeValue;
    public string SomeValue 
    {
        get { return _SomeValue; } 
        set 
        { 
            _SomeValue = value; 
            OnPropertyChanged("SomeValue"); 
        } 
    }

    public event PropertyChangedEventHandler PropertyChanged;
    private void OnPropertyChanged(string propertyName)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

Nun stellen Sie ein Formular mit nur zwei Textfelder, die erste für Name und die zweite für Inner.SomeValue. Ich bin leicht in der Lage zu arbeiten gegen Namen zu bekommen verbindlich, aber Inner.SomeValue ist schuppig. Wenn ich das Objekt und dann gründen die Bindung bevölkern, zeigt es Inner.SomeValue in der Textbox aber ich kann nicht bearbeiten es. Wenn ich aus einem neuen Objekt zu starten, ohne innere Initialisierung, kann ich keine Daten auf Stick scheint in Inner.SomeValue zu erhalten.

Ich habe alle über MSDN, die alle über Stackoverflow, und Dutzende von Suchanfragen mit verschiedenen Keywords geprüft. Jeder will über die Bindung an Datenbanken oder Datagrids und die meisten Beispiele sind geschrieben in XAML.

sprechen

Update: Ich habe Marc Das vollständige Testumgebung ausprobiert und haben teilweise Erfolg. Wenn ich auf den „alles ändern!“ Taste, scheine ich auf die innere Objekt in der Lage sein zu schreiben zurück. Doch mit MyObject.Inner null beginnen, weiß er nicht, wie ein inneres Objekt zu erstellen. Ich denke, für jetzt, ich kann nur sicherstellen, dass meine innere Referenzen um ihn herum arbeiten, sind auf ein gültiges Objekt immer gesetzt. Dennoch kann ich nicht helfen Gefühl, wie ich etwas fehlt:)

War es hilfreich?

Lösung

Hmm - an excellent question; I've done lots of data-binding to objects, and I would have sworn that what you are doing should work; but indeed it is very reluctant to notice the change to the inner object. I've managed to get it working by:

var outer = new BindingSource { DataSource = myObject };
var inner = new BindingSource(outer, "Inner");
txtName.DataBindings.Add("Text", outer, "Name");
txtSomeValue.DataBindings.Add("Text", inner, "SomeValue");

Not ideal, but it works. Btw; you might find the following utility methods useful:

public static class EventUtils {
    public static void SafeInvoke(this EventHandler handler, object sender) {
        if(handler != null) handler(sender, EventArgs.Empty);
    }
    public static void SafeInvoke(this PropertyChangedEventHandler handler,
               object sender, string propertyName) {
        if(handler != null) handler(sender,
               new PropertyChangedEventArgs(propertyName));
    }
}

Then you can have:

class MyObject : INotifyPropertyChanged
{
    private string _Name;
    public string Name { get { return _Name; } set {
        _Name = value; PropertyChanged.SafeInvoke(this,"Name"); } }
    private MyInner _Inner;
    public MyInner Inner { get { return _Inner; } set {
        _Inner = value; PropertyChanged.SafeInvoke(this,"Inner"); } }
    public event PropertyChangedEventHandler PropertyChanged;
}

class MyInner : INotifyPropertyChanged
{
    private string _SomeValue;
    public string SomeValue { get { return _SomeValue; } set {
        _SomeValue = value; PropertyChanged.SafeInvoke(this, "SomeValue"); } }
    public event PropertyChangedEventHandler PropertyChanged;
}

And in the bargain it fixes the (slim) chance of a null-exception (race-condition).


Full test rig, to iron out kinks (from comments):

using System;
using System.ComponentModel;
using System.Windows.Forms;
public static class EventUtils {
    public static void SafeInvoke(this PropertyChangedEventHandler handler, object sender, string propertyName) {
        if(handler != null) handler(sender, new PropertyChangedEventArgs(propertyName));
    }
}
class MyObject : INotifyPropertyChanged
{
    private string _Name;
    public string Name { get { return _Name; } set { _Name = value; PropertyChanged.SafeInvoke(this,"Name"); } }
    private MyInner _Inner;
    public MyInner Inner { get { return _Inner; } set { _Inner = value; PropertyChanged.SafeInvoke(this,"Inner"); } }
    public event PropertyChangedEventHandler PropertyChanged;
}

class MyInner : INotifyPropertyChanged
{
    private string _SomeValue;
    public string SomeValue { get { return _SomeValue; } set { _SomeValue = value; PropertyChanged.SafeInvoke(this, "SomeValue"); } }
    public event PropertyChangedEventHandler PropertyChanged;
}
static class Program
{
    [STAThread]
    public static void Main() {
        var myObject = new MyObject();
        myObject.Name = "old name";
        // optionally start with a default
        //myObject.Inner = new MyInner();
        //myObject.Inner.SomeValue = "old inner value";

        Application.EnableVisualStyles();
        using (Form form = new Form())
        using (TextBox txtName = new TextBox())
        using (TextBox txtSomeValue = new TextBox())
        using (Button btnInit = new Button())
        {
            var outer = new BindingSource { DataSource = myObject };
            var inner = new BindingSource(outer, "Inner");
            txtName.DataBindings.Add("Text", outer, "Name");
            txtSomeValue.DataBindings.Add("Text", inner, "SomeValue");
            btnInit.Text = "all change!";
            btnInit.Click += delegate
            {
                myObject.Name = "new name";
                var newInner = new MyInner();
                newInner.SomeValue = "new inner value";
                myObject.Inner = newInner;
            };
            txtName.Dock = txtSomeValue.Dock = btnInit.Dock = DockStyle.Top;
            form.Controls.AddRange(new Control[] { btnInit, txtSomeValue, txtName });
            Application.Run(form);
        }
    }

}
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top