Question

Je ne peux pas sembler trouver une explication simple et concrète de la façon dont les contrôles se lient dans une application WinForms à des objets imbriqués en utilisant la liaison de données. Par exemple:

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));
        }
    }
}

Maintenant, imaginez une forme avec seulement deux zones de texte, la première pour le nom et la seconde pour Inner.SomeValue. Je suis facilement en mesure d'obtenir la liaison au travail contre Nom, mais Inner.SomeValue est squameuse. Si je remplir l'objet, puis mis en place la liaison, il montre Inner.SomeValue dans la zone de texte, mais je ne peux pas le modifier. Si je commence à partir d'un objet frais sans initialisation intérieure, je ne peux pas sembler obtenir des données à coller dans Inner.SomeValue.

J'ai vérifié partout MSDN, partout StackOverflow, et des dizaines de recherches avec des mots-clés. Tout le monde veut parler de se lier à des bases de données ou DataGrid, et la plupart des exemples sont écrits en XAML.

Mise à jour: J'ai essayé harnais de test complet de Marc et ont un succès partiel. Si je frappe le « tout changement! » bouton, je semble être en mesure d'écrire à l'objet intérieur. Cependant, en commençant par null MyObject.Inner, il ne sait pas comment créer un objet intérieur. Je pense que pour l'instant, je peux travailler autour d'elle en faisant juste que mes références internes sont toujours à un objet valide. Pourtant, je ne peux me sentir comme si je manque quelque chose:)

Était-ce utile?

La solution

Hmm - une excellente question; J'ai beaucoup DONE données se liant à des objets, et j'aurais juré que ce que vous faites doit travailler; mais en effet il est très réticent à remarquer le changement de l'objet interne. J'ai réussi à le faire fonctionner par:

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

Pas idéal, mais cela fonctionne. BTW; vous pourriez trouver les méthodes utilitaires suivantes utiles:

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));
    }
}

Ensuite, vous pouvez avoir:

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;
}

Et dans le marché il fixe la chance (mince) d'une exception null (race condition).


banc d'essai complet, de fer à Kinks (des commentaires):

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);
        }
    }

}
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top