Frage

Wenn ich möchte eine Kontrolle auf eine Eigenschaft meiner Aufgabe binden, ich habe den Namen der Eigenschaft als String zu liefern. Dies ist nicht sehr gut, weil:

  1. Ist das Objekt entfernt oder Ich umbenannt, nicht bekommen, einen Compiler Warnung.
  2. Wenn ein Umbenennungs Eigentum mit einem Refactoring Werkzeug ist es wahrscheinlich Bindung werden die Daten nicht Aktualisiert.
  3. Ich habe nicht einen Fehler bis Laufzeit, wenn die Art der Immobilie falsch ist, z.B. Bindung eine ganze Zahl ein DateChooser.

Gibt es ein Design-Muster, die rund um das bekommt, hat aber immer noch die einfache Handhabung von Datenbindung?

(Dies ist ein Problem in Windows Forms, Asp.net und WPF und wahrscheinlich viele andere Systeme)

habe ich jetzt gefunden „ Abhilfen für nameof Operator () in C # : typsichere Datenbindung “, der auch ein guter Ausgangspunkt für eine Lösung hat

.

Wenn Sie bereit sind, einen Postprozessor zu verwenden, nachdem Sie den Code kompilieren, notifypropertyweaver lohnt suchen an.


Wer kennt eine gute Lösung für WPF, wenn die Bindungen in XML fertig sind eher dann C #?

War es hilfreich?

Lösung

Dank Oliver für mich immer begann ich habe jetzt eine Lösung, dass beiden Träger Refactoring und sind typsicher. Lassen Sie mich auch INotifyPropertyChanged implementieren, so meistert sie mit Eigenschaften umbenannt werden.

Es ist Nutzung aussieht wie:

checkBoxCanEdit.Bind(c => c.Checked, person, p => p.UserCanEdit);
textBoxName.BindEnabled(person, p => p.UserCanEdit);
checkBoxEmployed.BindEnabled(person, p => p.UserCanEdit);
trackBarAge.BindEnabled(person, p => p.UserCanEdit);

textBoxName.Bind(c => c.Text, person, d => d.Name);
checkBoxEmployed.Bind(c => c.Checked, person, d => d.Employed);
trackBarAge.Bind(c => c.Value, person, d => d.Age);

labelName.BindLabelText(person, p => p.Name);
labelEmployed.BindLabelText(person, p => p.Employed);
labelAge.BindLabelText(person, p => p.Age);

Die Person-Klasse zeigt, wie man implementiert INotifyPropertyChanged in eine Art sichere Art und Weise (oder sehen diese Antwort für eine andere eher nette Weise INotifyPropertyChanged umzusetzen, ActiveSharp - Automatische INotifyPropertyChanged sieht auch gut aus):

public class Person : INotifyPropertyChanged
{
   private bool _employed;
   public bool Employed
   {
      get { return _employed; }
      set
      {
         _employed = value;
         OnPropertyChanged(() => c.Employed);
      }
   }

   // etc

   private void OnPropertyChanged(Expression<Func<object>> property)
   {
      if (PropertyChanged != null)
      {
         PropertyChanged(this, 
             new PropertyChangedEventArgs(BindingHelper.Name(property)));
      }
   }

   public event PropertyChangedEventHandler PropertyChanged;
}

Die WinForms Hilfsklasse Bindung hat das Fleisch darin, dass sie alle Arbeit macht:

namespace TypeSafeBinding
{
    public static class BindingHelper
    {
        private static string GetMemberName(Expression expression)
        {
            // The nameof operator was implemented in C# 6.0 with .NET 4.6
            // and VS2015 in July 2015. 
            // The following is still valid for C# < 6.0

            switch (expression.NodeType)
            {
                case ExpressionType.MemberAccess:
                    var memberExpression = (MemberExpression) expression;
                    var supername = GetMemberName(memberExpression.Expression);
                    if (String.IsNullOrEmpty(supername)) return memberExpression.Member.Name;
                    return String.Concat(supername, '.', memberExpression.Member.Name);
                case ExpressionType.Call:
                    var callExpression = (MethodCallExpression) expression;
                    return callExpression.Method.Name;
                case ExpressionType.Convert:
                    var unaryExpression = (UnaryExpression) expression;
                    return GetMemberName(unaryExpression.Operand);
                case ExpressionType.Parameter:
                case ExpressionType.Constant: //Change
                    return String.Empty;
                default:
                    throw new ArgumentException("The expression is not a member access or method call expression");
            }
        }

        public static string Name<T, T2>(Expression<Func<T, T2>> expression)
        {
            return GetMemberName(expression.Body);
        }

        //NEW
        public static string Name<T>(Expression<Func<T>> expression)
        {
           return GetMemberName(expression.Body);
        }

        public static void Bind<TC, TD, TP>(this TC control, Expression<Func<TC, TP>> controlProperty, TD dataSource, Expression<Func<TD, TP>> dataMember) where TC : Control
        {
            control.DataBindings.Add(Name(controlProperty), dataSource, Name(dataMember));
        }

        public static void BindLabelText<T>(this Label control, T dataObject, Expression<Func<T, object>> dataMember)
        {
            // as this is way one any type of property is ok
            control.DataBindings.Add("Text", dataObject, Name(dataMember));
        }

        public static void BindEnabled<T>(this Control control, T dataObject, Expression<Func<T, bool>> dataMember)
        {       
           control.Bind(c => c.Enabled, dataObject, dataMember);
        }
    }
}

verwenden Dies macht eine Menge der neuen Sachen in C # 3.5 und zeigt genau das, was möglich ist. Nun, wenn nur wir hatten hygienische Makros Lisp Programmierer stoppen können uns Bürger zweiter Klasse Aufruf)

Andere Tipps

Der nameof Operator wurde in C # 6.0 mit .NET 4.6 und VS2015 im Juli 2015. Im Folgenden ist immer noch gültig für C # <6.0

Strings zu vermeiden, die Eigenschaftsnamen enthalten, habe ich eine einfache Klasse mit Ausdruck Bäume geschrieben, um den Namen des Mitglieds zurück:

using System;
using System.Linq.Expressions;
using System.Reflection;

public static class Member
{
    private static string GetMemberName(Expression expression)
    {
        switch (expression.NodeType)
        {
            case ExpressionType.MemberAccess:
                var memberExpression = (MemberExpression) expression;
                var supername = GetMemberName(memberExpression.Expression);

                if (String.IsNullOrEmpty(supername))
                    return memberExpression.Member.Name;

                return String.Concat(supername, '.', memberExpression.Member.Name);

            case ExpressionType.Call:
                var callExpression = (MethodCallExpression) expression;
                return callExpression.Method.Name;

            case ExpressionType.Convert:
                var unaryExpression = (UnaryExpression) expression;
                return GetMemberName(unaryExpression.Operand);

            case ExpressionType.Parameter:
                return String.Empty;

            default:
                throw new ArgumentException("The expression is not a member access or method call expression");
        }
    }

    public static string Name<T>(Expression<Func<T, object>> expression)
    {
        return GetMemberName(expression.Body);
    }

    public static string Name<T>(Expression<Action<T>> expression)
    {
        return GetMemberName(expression.Body);
    }
}

Sie können mit dieser Klasse wie folgt verwenden. Auch wenn Sie es nur in Code (also nicht in XAML) verwenden können, ist es sehr hilfreich (zumindest für mich), aber der Code ist noch nicht typsicher. Sie könnten die Methodennamen mit einem zweiten Typ Argumente erstrecken, die den Rückgabewert der Funktion definiert, die die Art der Immobilie beschränken würden.

var name = Member.Name<MyClass>(x => x.MyProperty); // name == "MyProperty"

Bis jetzt habe ich noch nichts gefunden, das die Datenbindung Typsicherheit Problem löst.

Mit freundlichen Grüßen

Das Framework 4.5 bietet uns die CallerMemberNameAttribute , die macht den Namen der Eigenschaft als String übergeben unnötig:

private string m_myProperty;
public string MyProperty
{
    get { return m_myProperty; }
    set
    {
        m_myProperty = value;
        OnPropertyChanged();
    }
}

private void OnPropertyChanged([CallerMemberName] string propertyName = "none passed")
{
    // ... do stuff here ...
}

Wenn Sie gerade arbeiten Framework 4.0 mit KB2468871 installiert haben, können Sie installieren Sie die Microsoft BCL Compatibility Pack über nuget , die auch bietet dieses Attribut.

Dieser Blog Artikel wirft einige gute Fragen über die Leistung dieses Ansatzes . Sie könnten durch die Umwandlung der Ausdruck in einen String als Teil einer Art von statischen Initialisierung auf diese Mängel verbessern.

Die eigentliche Mechanik könnte ein wenig unansehnlich sein, aber es wäre noch typsicher und etwa gleiche Leistung auf den rohen INotifyPropertyChanged sein.

Etwas ein bisschen wie folgt aus:

public class DummyViewModel : ViewModelBase
{
    private class DummyViewModelPropertyInfo
    {
        internal readonly string Dummy;

        internal DummyViewModelPropertyInfo(DummyViewModel model)
        {
            Dummy = BindingHelper.Name(() => model.Dummy);
        }
    }

    private static DummyViewModelPropertyInfo _propertyInfo;
    private DummyViewModelPropertyInfo PropertyInfo
    {
        get { return _propertyInfo ?? (_propertyInfo = new DummyViewModelPropertyInfo(this)); }
    }

    private string _dummyProperty;
    public string Dummy
    {
        get
        {
            return this._dummyProperty;
        }
        set
        {
            this._dummyProperty = value;
            OnPropertyChanged(PropertyInfo.Dummy);
        }
    }
}

Eine Möglichkeit, Feedback zu erhalten, wenn Ihre Bindungen gebrochen werden, ist ein Datatemplate zu erstellen und erklären, seinen Datentyp der Typ des Ansichtsmodelles sein, dass es beispielsweise bindet wenn Sie eine PersonView und eine PersonViewModel haben würden Sie Folgendes tun:

  1. Deklarieren ein Datatemplate mit Datatype = PersonViewModel und einem Schlüssel (z.B. PersonTemplate)

  2. Cut alle PersonView XAML und in die Datenvorlage einfügen (die im Idealfall kann an der Spitze sein, nur des PersonView.

3a. Erstellen Sie ein Content und stellen Sie die Content = PersonTemplate und binden ihr Inhalt in die PersonViewModel.

3b. Eine weitere Möglichkeit ist es, nicht einen Schlüssel für die Datatemplate geben und nicht die Content des Content gesetzt. In diesem Fall wird WPF herauszufinden, was Datatemplate zu verwenden, da sie weiß, was Art von Objekt, das Sie verbindlich sind. Es wird um den Baum zu suchen und Ihren Datatemplate finden und da es die Art der Bindung entspricht, wird es automatisch als Content gilt.

Sie enden wie vor mit im Wesentlichen gleicher Ansicht, aber da Sie die Datatemplate zu einem darunter liegenden Datentyp zugeordnet, Tools wie ReSharper können Sie Feedback geben (über Farbkennungen - ReSharper-Optionen-Einstellungen-Color Identifiers) in Bezug auf, ob Ihre Bindungen werden gebrochen oder nicht.

Sie werden noch nicht Compiler-Warnungen erhalten, aber visuell auf gebrochene Bindungen überprüfen, was besser ist, als wenn man hin und her zwischen Ihrer Ansicht und Ansichtsmodell überprüfen.

Ein weiterer Vorteil dieser zusätzlichen Informationen, die Sie geben, ist, dass es auch in der Umbenennung Refactorings verwendet werden kann. Soweit ich mich erinnere ReSharper ist in der Lage automatisch Bindungen auf typisierte Datatemplates umbenennen, wenn der Name zugrunde liegenden Ansichtsmodell der Eigenschaft geändert wird und umgekehrt.

  

1. Wenn die Eigenschaft entfernt oder umbenannt, ich habe keine Compiler-Warnung erhalten.

     

2. Wenn eine Umbenennung die Eigenschaft mit einem Refactoring-Tool, es ist wahrscheinlich, die Datenbindung wird nicht aktualisiert werden.

     

3.I keinen Fehler erst zur Laufzeit, wenn der Typ der Eigenschaft falsch ist, z.B. eine ganze Zahl mit einem DateChooser verbindlich.

Ja, Ian, das sind genau die Probleme mit dem Namen-string angetrieben Datenbindung. Sie bat um ein Design-Muster. Ich entwarf die Typ-Safe-Ansicht Modell (TVM) Muster, das eine Konkretion des View-Modell Teil des Model-View-Viewmodel (MVVM) Muster ist. Es basiert auf einem typsichere Bindung, ähnlich wie Sie Ihre eigene Antwort. Ich habe gerade eine Lösung für WPF geschrieben:

http: // www.codeproject.com/Articles/450688/Enhanced-MVVM-Design-w-Type-Safe-View-Models-TVM

x: bind (auch "kompilierte Datenbindungen" genannt) für XAML (Universal-App) in Windows 10 und Windows Phone 10 kann dieses Problem lösen, finden Sie unter https://channel9.msdn.com/Events/Build/2015/3-635

Ich kann die Online-Dokumentation für sie zu finden, aber haben nicht viel Mühe in, wie es etwas ist, werde ich für einige Zeit nicht verwendet werden. Allerdings sollte diese Antwort ein nützlicher Zeiger auf andere Menschen.

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