wpf mvvm - INotifyPropertyChanged vs.DependencyProperty in ViewModel




pattern best (13)

Es gibt nur eine Sache, warum man ein DependencyObject bevorzugen sollte - Binding wird besser funktionieren. ListBox TextBox ein Beispiel mit einer ListBox und TextBox , ListBox TextBox die Liste mit Daten aus der INotifyPropertyChanged Eigenschaft vs. DependencyProperty und bearbeiten Sie das aktuelle Element aus TextBox ...

Bei der Implementierung des ViewModel in einer WPF-Anwendung mit Model-View-ViewModel-Architektur scheint es zwei große Möglichkeiten zu geben, wie man es in eine Datenbank einbinden kann. Ich habe Implementierungen gesehen, die DependencyProperty für Eigenschaften verwenden, gegen die die View binden wird, und ich habe stattdessen ViewModel gesehen, das INotifyPropertyChanged implementiert INotifyPropertyChanged .

Meine Frage ist, wann sollte ich eine über die andere bevorzugen? Gibt es Leistungsunterschiede? Ist es wirklich eine gute Idee, die ViewModel-Abhängigkeiten zu WPF zu geben? Was muss ich noch bei der Designentscheidung beachten?


Aus der Sicht der Expressivität genieße ich es sehr, Abhängigkeitseigenschaften zu verwenden und beim Gedanken an INotifyPropertyChanged . Abgesehen von den string Eigenschaftsnamen und möglichen Speicherlecks aufgrund von Ereignisabonnements ist INotifyPropertyChanged ein viel expliziterer Mechanismus.

Abhängigkeitseigenschaften implizieren "wenn dies geschieht" mithilfe leicht verständlicher statischer Metadaten. Es ist ein deklarativer Ansatz, der meine Stimme für Eleganz bekommt.


Auch ich musste diese Entscheidung in letzter Zeit prüfen.

Ich stellte fest, dass der INotifyPropertyChanged-Mechanismus meinen Anforderungen besser entsprach, da ich dadurch meine GUI an ein vorhandenes Geschäftslogik-Framework kleben konnte, ohne den Zustand zu duplizieren. Der Rahmen, den ich benutzte, hatte sein eigenes Beobachtermuster und es war einfach, eine Benachrichtigungsstufe auf die nächste weiter zu leiten. Ich hatte einfach eine Klasse, die die Observer-Schnittstelle aus meinem Business-Logik-Framework und der INotifyPropertyChanged-Schnittstelle implementierte.

Mit DP können Sie nicht das Backend definieren, das den Status selbst speichert. Ich hätte .net eine Kopie jedes Staatspostens cachen lassen müssen, an den ich mich gebunden habe. Das schien wie ein unnötiger Overhead - mein Zustand ist groß und kompliziert.

Hier habe ich INotifyPropertyChanged besser gefunden, um Eigenschaften von der Geschäftslogik zur GUI freizulegen.

Da wo ich ein benutzerdefiniertes GUI-Widget brauchte, um eine Eigenschaft verfügbar zu machen, und dass Änderungen an dieser Eigenschaft sich auf andere GUI-Widgets auswirkten, erwies sich DP als einfache Lösung.

Also ich fand DP nützlich für GUI zu GUI Benachrichtigung.


Ist es wirklich eine gute Idee, die ViewModel-Abhängigkeiten zu WPF zu geben?

.NET 4.0 wird System.Xaml.dll haben, so dass Sie keine Abhängigkeit von einem beliebigen Framework benötigen müssen, um es zu verwenden. Siehe Rob Relyeas Post über seine PDC-Sitzung.

Mein Nehmen

XAML ist eine Sprache zum Beschreiben von Objekten und WPF ist ein Framework, dessen beschriebene Objekte UI-Elemente sind.

Ihre Beziehung ähnelt C #, einer Sprache zum Beschreiben von Logik, und .NET, einem Framework, das bestimmte Arten von Logik implementiert.

Der Zweck von XAML sind deklarative Objektgraphen. Die W * F-Technologien sind großartige Kandidaten für dieses Paradigma, aber XAML existiert unabhängig von ihnen.

XAML und das gesamte Abhängigkeitssystem wurden als separate Stacks für WF und WPF implementiert, wahrscheinlich um die Erfahrung verschiedener Teams zu nutzen, ohne eine Abhängigkeit (kein Wortspiel beabsichtigt) zwischen ihnen zu schaffen.


Abhängigkeitseigenschaften sind die Grundlage für die Erstellung benutzerdefinierter Steuerelemente. Wenn Sie daran interessiert sind, mit Intelli-Sense Ihre Eigenschaften im Eigenschaftenfenster zur XAML-Entwurfszeit anzuzeigen, müssen Sie die Abhängigkeitseigenschaften verwenden. INPC zeigt zur Entwurfszeit niemals eine Eigenschaft im Eigenschaftenfenster an.


Wenn Sie Eigenschaften anderen Steuerelementen zugänglich machen möchten, müssen Sie Abhängigkeitseigenschaften verwenden ... Aber viel Glück, weil sie eine Weile brauchen, um herauszufinden ...


Ich bevorzuge einen direkteren Ansatz, über den ich im Präsentationsmodell ohne INotifyPropertyChanged gebloggt habe . Mit einer Alternative zur Datenbindung können Sie ohne Buchhaltungscode direkt an CLR-Eigenschaften binden. Sie schreiben einfach reinen .NET-Code in Ihr Ansichtsmodell und es wird aktualisiert, wenn sich Ihr Datenmodell ändert.


INotifyPropertyChanged wenn verwendet, gibt Ihnen auch die Möglichkeit, mehr Logik im Code Ihrer Getter und Setter Ihrer Eigenschaften hinzuzufügen.

DependencyProperty Beispiel:

public static DependencyProperty NameProperty = DependencyProperty.Register( "Name", typeof( String), typeof( Customer ) );

public String Name
{
    set { SetValue( NameProperty, value ); }
    get { return ( String ) GetValue( NameProperty ); }
}

In Ihrem Getter und Setter --- können Sie SetValue und GetValue einfach aufrufen, b / c In anderen Teilen des Frameworks wird der Getter / Setter nicht aufgerufen, stattdessen ruft SetValue, GetValue direkt auf, also würde Ihre Eigenschaftslogik nicht funktionieren zuverlässig ausgeführt werden.

INotifyPropertyChanged mit INotifyPropertyChanged ein Ereignis:

public event PropertyChangedEventHandler PropertyChanged;

Und dann haben Sie einfach irgendeine Logik irgendwo in Ihrem Code, dann rufen Sie:

// ...
// Something cool...
// ...

if( this.PropertyChanged != null )
{
    PropertyChanged( this, new PropertyChangedEventArgs( "Name" ) );
}

// More cool stuff that will reliably happen...

Dies könnte in einem Getter / Setter oder anderswo sein.


Ich denke, DependencyProperty und INotifyPropertyChanged werden für zwei verschiedene Dinge in der Bindung verwendet: die erste, um eine Eigenschaft zu ermöglichen, ein Ziel einer Bindung zu sein und die Eingabe von einer anderen Eigenschaft zu empfangen (verwenden Sie {Binding ...}, um die Eigenschaft festzulegen) Wenn Sie möchten, dass der Wert einer Eigenschaft als Quelle einer Bindung verwendet wird (Name im Binding Path-Ausdruck). Die Auswahl ist also rein technischer Natur.


Die Auswahl basiert vollständig auf Ihrer Geschäftslogik und Abstraktionsebene. Wenn Sie keine gute Trennung wünschen, wird DP für Sie arbeiten.

DependencyProperties wird hauptsächlich auf VisualElements-Ebene angewendet, daher ist es keine gute Idee, wenn wir für jede unserer Geschäftsanforderungen viele DPs erstellen. Außerdem gibt es höhere Kosten für DP als eine INotifyPropertyChanged. Wenn Sie ein WPF / Silverlight entwerfen, versuchen Sie, die Benutzeroberfläche und das ViewModel vollständig voneinander zu trennen, sodass wir jederzeit die Layout- und UI-Steuerelemente ändern können (basierend auf Design und Stilen).

Verweise auch auf diesen Beitrag - https://.com/questions/275098/what-applications-could-i-study-to-understand-datamodel-view-viewmodel . Der Link verweist auf das Model-View-ViewModel-Muster, das für diese Diskussion sehr relevant ist.


Offenbar sollten die Abhängigkeitseigenschaften in Steuerelementen verwendet werden, die Sie erstellen, z. B. Schaltflächen. Um Eigenschaften in XAML zu verwenden und alle WPF-Features zu verwenden, müssen diese Eigenschaften Abhängigkeitseigenschaften aufweisen.

Mit INotifyPropertyChanged ist Ihr ViewModel jedoch besser aufgehoben. Wenn Sie INotifyPropertyChanged verwenden, können Sie bei Bedarf Getter / Setter-Logik verwenden.

Ich empfehle Josh Smiths Version einer Basisklasse für ein ViewModel, das bereits INotifyPropertyChanged implementiert:

http://joshsmithonwpf.wordpress.com/2007/08/29/a-base-class-which-implements-inotifypropertychanged/

Ich denke, das ist ein hervorragendes Beispiel für ein ViewModel.



Für UWP ist es nicht so einfach: Sie müssen durch einen zusätzlichen Rahmen springen, um einen Feldwert als Parameter zu übergeben.

Beispiel 1

Gültig für WPF und UWP.

<MyControl>
    <MyControl.MyProperty>
        <Binding Converter="{StaticResource EnumToBooleanConverter}" Path="AnotherProperty">
            <Binding.ConverterParameter>
                <MyLibrary:MyEnum>Field</MyLibrary:MyEnum>
            </Binding.ConverterParameter>
        </MyControl>
    </MyControl.MyProperty>
</MyControl>

Beispiel 2

Gültig für WPF und UWP.

...
<MyLibrary:MyEnum x:Key="MyEnumField">Field</MyLibrary:MyEnum>
...

<MyControl MyProperty="{Binding AnotherProperty, Converter={StaticResource EnumToBooleanConverter}, ConverterParameter={StaticResource MyEnumField}}"/>

Beispiel 3

Nur gültig für WPF!

<MyControl MyProperty="{Binding AnotherProperty, Converter={StaticResource EnumToBooleanConverter}, ConverterParameter={x:Static MyLibrary:MyEnum.Field}}"/>

UWP unterstützt nicht x:Static so dass Beispiel 3 nicht in Frage kommt; Angenommen, Sie gehen mit Beispiel 1 , ist das Ergebnis mehr verbose Code. Beispiel 2 ist etwas besser, aber immer noch nicht ideal.

Lösung

public abstract class EnumToBooleanConverter<TEnum> : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, string language)
    {
        var Parameter = parameter as string;

        if (Parameter == null)
            return DependencyProperty.UnsetValue;

        if (Enum.IsDefined(typeof(TEnum), value) == false)
            return DependencyProperty.UnsetValue;

        return Enum.Parse(typeof(TEnum), Parameter).Equals(value);
    }

    public object ConvertBack(object value, Type targetType, object parameter, string language)
    {
        var Parameter = parameter as string;
        return Parameter == null ? DependencyProperty.UnsetValue : Enum.Parse(typeof(TEnum), Parameter);
    }
}

Definieren Sie dann für jeden Typ, den Sie unterstützen möchten, einen Konverter, der den Enum-Typ enthält.

public class MyEnumToBooleanConverter : EnumToBooleanConverter<MyEnum>
{
    //Nothing to do!
}

Der Grund, warum es ConvertBack werden muss, ist, dass es scheinbar keine Möglichkeit gibt, den Typ in der ConvertBack Methode zu referenzieren. das Boxen sorgt dafür. Wenn Sie mit einem der ersten beiden Beispiele fortfahren, können Sie einfach auf den Parametertyp verweisen und müssen nicht mehr von einer Box-Klasse erben. Wenn Sie alles in einer Zeile und mit möglichst wenig Ausführlichkeit ausführen möchten, ist die letztere Lösung ideal.

Die Verwendung ähnelt Beispiel 2 , ist jedoch weniger ausführlich.

<MyControl MyProperty="{Binding AnotherProperty, Converter={StaticResource MyEnumToBooleanConverter}, ConverterParameter=Field}"/>

Der Nachteil ist, dass Sie einen Konverter für jeden Typ definieren müssen, den Sie unterstützen möchten.





wpf data-binding architecture mvvm dependency-properties