none
Frage zur Anbindung von DependencyProperties RRS feed

  • Frage

  • Eine Klasse:

    publicclassViewModelStadt: DependencyObject, ...

    Gegeben ist eine durch Linq erzeugte Liste, die über eine Eigenschaft dargestellt wird:

           private IEnumerable _Personen   // Alle Personen, die zu dieser Stadt gehören ...
            {
                get
                {
                    var ie = from Person p in Content.Personen select p;
                    return ie;
                }
            }

    Änderungen an der Liste werden durch einen Event mitgeteilt.

    Diese _Personen würde ich gerne an eine DependencyProperty anschliessen wie folgt:

    (Sollte - da nur getter - eine ReadOnly property sein ... Normalo geht aber auch)

            
            public static readonly DependencyPropertyKey PersonenPropertyKey 
                = DependencyProperty.RegisterReadOnly("Personen", typeof(IEnumerable), typeof(ViewModelStadt), new PropertyMetadata(null));
    
            public static readonly DependencyProperty PersonenProperty = PersonenPropertyKey.DependencyProperty;
    

    [[ Anmerkung: Ich weiß, durch INotifyPropertyChanged gehts einfacher, das funktioniert auch. 
    Die Frage ist eher prinzipieller Natur, es geht um einen klugen Weg DPs ans Hinterland anzuschliessen. ]]

    Bisher habe ich mich - zur Initialisierung im Konstruktor und im Callback - wie folgt beholfen:

    SetValue(PersonenPropertyKey, _Personen);

    Ich setze also einfach das property jedesmal neu. Tut auch. Ist aber aus meiner Sicht unsauber, da der Linq sehr früh ausgewertet wird.
    Und das fortwährende setzen einer Instanz unschöner ist, als diese der property einfach zu benennen.

    Also nehmen wir eine Bindung (Obacht: SetValue überschreibt die Bindung, dieses also vorher tun ...):

    SetValue(PersonenProperty, _Personen);

    BindingOperations.SetBinding(this,ViewModelStadt.PersonenProperty,
                new Binding{ Source = this, Path = new PropertyPath("_Personen"), Mode=BindingMode.OneWay});

    Und updaten die im Callback mit:

    BindingExpressionp = BindingOperations.GetBindingExpression(this, ViewModelStadt.PersonenProperty);
    if(p != null) p.UpdateTarget();

    Das funktioniert soweit. Aber nur mit einer Normalo-DP, ums verrecken nicht nicht mit einer ReadOnly. 

    Und jetzt die Fragen:
    1. Hat noch jemand eine Idee ? Wie schliesst ihr denn DPs ans "Hinterland" an ?
    2. Warum funktioniert die von mir beabsichtigte Bindung nicht mit ReadOnly-DPs ?

    Gruß Rudolf

    Samstag, 11. Februar 2012 14:09

Antworten

  • Hi Rudolf,

    falls es Dein Ziel war, jemanden mit Deinem Posting zu verwirren - bei mir hat's geklappt. Trotzdem versuch ich es mal:

    Wenn Du eine Liste hast, so werden Änderungen an den Elementen - egal ob bei normalen Properties oder DependencyProperties - nicht kommuniziert, denn die Liste selbst (der Zeiger auf Adresse xyz) ändert sich nicht. Es gibt also aus Sicht des Frameworks nichts zu berichten. Ähnliches gilt für komplexe Properties (zB Adresse mit Strasse, PLZ und Ort als enthaltene Eigenschaften): das Ändern der "Strasse" führt zu keiner Änderung an der Adresse; es gibt nichts zu kommunizieren.

    Für Listen gibt es im Framework die ObservableCollection<T>. Wenn Du also Deine "_Personen" als ObservableCollection anlegst und in Deinem Linq-Änderungs-Event entsprechend modifizierst, wird dies ans "Hinterland" weitergereicht. Mit jeder anderen Liste hast Du keine andere Möglichkeit als die Liste selbst komplett zu erneuern (neue Instanz) oder via 'manuellem' PropertyChanged-Event so zu tun, als ob.

    Deine Variante kann erstens nicht funktionieren, weil "_Personen" private ist (kann nicht als Binding-Quelle dienen). Zweitens sehe ich die Deklaration von "Personen" (für die Registrierung der DP) nicht; hier muss es einen Setter geben. Falls Du das also meintest mit "readonly DP" - die gibt es nicht für DataBinding und all die anderen Fälle, die ansonsten Grund sind eine DP anstatt INotifyPropertyChanged zu verwenden (http://msdn.microsoft.com/en-us/library/ms754044.aspx Abschnitt "Existing Read-Only Dependency Properties").

    Gruß
    Jürgen

    PS: Du sagst selbst: Mit INotifyPropertyChanged wäre es einfacher und ich denke, es gibt keinen "klugen Weg" eine einfache Sache kompliziert zu lösen.

    Samstag, 11. Februar 2012 17:56
  • Hallo Rolf,

    wenn ich Deinen Code ausprobiere, erhalte ich bei einer readonly-DP einen Fehler beim Ausführen von SetValue - was wohl auch leicht zu verstehen ist.

    Bei einer 'Normalo'-DP erhalte ich beim Erstellen der Bindung "BindingExpression path error: '...' property not found". Für mich sieht es also weiterhin so aus, dass das nicht geht. Und ich halte es auch für zwingend erforderlich, dass das nicht geht, denn sonst könnten private Eigenschaften von außen verändert werden.

    Zur DP-Anbindung: Hier scheint es mir, als zäumtest Du das Pferd von hinten auf. Es geht doch nicht darum, eine DP zu verwenden und dann zu überlegen, wie ich die mit dem UI kommunizieren lasse. Ausgangspunkt ist doch: Du hast eine Liste von Personen und willst die anzeigen. Das geht in WPF tatsächlich mit einer Bindung am besten - und dafür taugen readonly DPs nicht.

    Zudem sind DPs im Wesentlichen nur nötig, wenn Du die zugehörigen Eigenschaften in Animationen, Styles oder für Trigger verwenden willst. Sonst ist INPC ausreichend und weil einfacher - für mich - der Weg der Wahl. Außer DependencyProperties und INPC gibt es keinen Weg, UI an Eigenschaften zu binden. Bliebe also nur noch Code behind.

    Und Du hast recht, wenn Du sagst, auch eine ObservableCollection sei letztendlich nur eine Property (wenn sie als solche definiert wurde). Aber sie unterscheidet sich von allen anderen Listen/Aufzählungen dadurch, dass nur hier Änderungen an der Menge der enthaltenen Elementen kommuniziert werden. Ich dachte, dies sei ein Teil Deines Problems.

    Gruß
    Jürgen

    Sonntag, 12. Februar 2012 08:02

Alle Antworten

  • Hi Rudolf,

    falls es Dein Ziel war, jemanden mit Deinem Posting zu verwirren - bei mir hat's geklappt. Trotzdem versuch ich es mal:

    Wenn Du eine Liste hast, so werden Änderungen an den Elementen - egal ob bei normalen Properties oder DependencyProperties - nicht kommuniziert, denn die Liste selbst (der Zeiger auf Adresse xyz) ändert sich nicht. Es gibt also aus Sicht des Frameworks nichts zu berichten. Ähnliches gilt für komplexe Properties (zB Adresse mit Strasse, PLZ und Ort als enthaltene Eigenschaften): das Ändern der "Strasse" führt zu keiner Änderung an der Adresse; es gibt nichts zu kommunizieren.

    Für Listen gibt es im Framework die ObservableCollection<T>. Wenn Du also Deine "_Personen" als ObservableCollection anlegst und in Deinem Linq-Änderungs-Event entsprechend modifizierst, wird dies ans "Hinterland" weitergereicht. Mit jeder anderen Liste hast Du keine andere Möglichkeit als die Liste selbst komplett zu erneuern (neue Instanz) oder via 'manuellem' PropertyChanged-Event so zu tun, als ob.

    Deine Variante kann erstens nicht funktionieren, weil "_Personen" private ist (kann nicht als Binding-Quelle dienen). Zweitens sehe ich die Deklaration von "Personen" (für die Registrierung der DP) nicht; hier muss es einen Setter geben. Falls Du das also meintest mit "readonly DP" - die gibt es nicht für DataBinding und all die anderen Fälle, die ansonsten Grund sind eine DP anstatt INotifyPropertyChanged zu verwenden (http://msdn.microsoft.com/en-us/library/ms754044.aspx Abschnitt "Existing Read-Only Dependency Properties").

    Gruß
    Jürgen

    PS: Du sagst selbst: Mit INotifyPropertyChanged wäre es einfacher und ich denke, es gibt keinen "klugen Weg" eine einfache Sache kompliziert zu lösen.

    Samstag, 11. Februar 2012 17:56
  • Hallo Jörg,

    Ja klar, _Personen gehört public. Aber natürlich funktioniert das so. Der Instanzen-Setter macht auch nix anderes als SetValue. Ist also für die Funktion des Beispiels unerheblich. Glaub mir also, es tut.

    Ich kenne grundsätzlich zwei Wege etwas an WPF anzuschliessen:

    1. INotifyPropertyChanged und Klassen-Properties (hierzu zähle ich auch ObservableCollection, das ist auch nichts anderes) oder

    2. gleich die Implementierung von DependencyProperties. Hier aber die Frage wie binde ich diese an meine Businessobjekte an. Hierbei fällt mir eben nur ein, entweder eine Bindung einzurichten oder das Objekt bei jedem Update (den mir ein CallBack der Businesslogik schickt, das hat mit dem Framework nichts zu tun) neu zu setzen. Meine Frage war nur ob es noch eine andere Möglichkeit gibt.

    Falls nein werde ich INotifyPropertyChanged verwneden und keine DependencyProperties in meine Klassen einbauen, denn das sieht irgendwie kompliziert aus. Ist ja nur eine Frage ...

    Gruß Rudolf


    • Bearbeitet _Rudolf Samstag, 11. Februar 2012 21:27
    Samstag, 11. Februar 2012 21:26
  • Hallo Rolf,

    wenn ich Deinen Code ausprobiere, erhalte ich bei einer readonly-DP einen Fehler beim Ausführen von SetValue - was wohl auch leicht zu verstehen ist.

    Bei einer 'Normalo'-DP erhalte ich beim Erstellen der Bindung "BindingExpression path error: '...' property not found". Für mich sieht es also weiterhin so aus, dass das nicht geht. Und ich halte es auch für zwingend erforderlich, dass das nicht geht, denn sonst könnten private Eigenschaften von außen verändert werden.

    Zur DP-Anbindung: Hier scheint es mir, als zäumtest Du das Pferd von hinten auf. Es geht doch nicht darum, eine DP zu verwenden und dann zu überlegen, wie ich die mit dem UI kommunizieren lasse. Ausgangspunkt ist doch: Du hast eine Liste von Personen und willst die anzeigen. Das geht in WPF tatsächlich mit einer Bindung am besten - und dafür taugen readonly DPs nicht.

    Zudem sind DPs im Wesentlichen nur nötig, wenn Du die zugehörigen Eigenschaften in Animationen, Styles oder für Trigger verwenden willst. Sonst ist INPC ausreichend und weil einfacher - für mich - der Weg der Wahl. Außer DependencyProperties und INPC gibt es keinen Weg, UI an Eigenschaften zu binden. Bliebe also nur noch Code behind.

    Und Du hast recht, wenn Du sagst, auch eine ObservableCollection sei letztendlich nur eine Property (wenn sie als solche definiert wurde). Aber sie unterscheidet sich von allen anderen Listen/Aufzählungen dadurch, dass nur hier Änderungen an der Menge der enthaltenen Elementen kommuniziert werden. Ich dachte, dies sei ein Teil Deines Problems.

    Gruß
    Jürgen

    Sonntag, 12. Februar 2012 08:02
  • Hallo _Rudolf,

    Ich gehe davon aus, dass die Antworten Dir weitergeholfen haben.
    Solltest Du noch "Rückfragen" dazu haben, so gib uns bitte Bescheid.

    Grüße,
    Robert


    Robert Breitenhofer, MICROSOFT  Twitter Facebook
    Bitte haben Sie Verständnis dafür, dass im Rahmen dieses Forums, welches auf dem Community-Prinzip „Entwickler helfen Entwickler“ beruht, kein technischer Support geleistet werden kann oder sonst welche garantierten Maßnahmen seitens Microsoft zugesichert werden können.

    Mittwoch, 29. Februar 2012 12:32
    Moderator