Laden...

[gelöst] DependencyProperty in einem UserControl und Binding

Erstellt von Mossi vor 12 Jahren Letzter Beitrag vor 12 Jahren 13.527 Views
Mossi Themenstarter:in
199 Beiträge seit 2006
vor 12 Jahren
[gelöst] DependencyProperty in einem UserControl und Binding

Hallo zusammen,

ich hab mir ein UserControl gebaut, das ein Objekt als DependencyProperty beinhaltet. Das Ganze sieht folgendermaßen aus:

public static readonly DependencyProperty ScoreProperty =
    DependencyProperty.Register("Score", typeof(Score), typeof(ScoreControl));

public Score Score
{
    get { return base.GetValue(ScoreProperty) as Score; }
    set
    {
        base.SetValue(ScoreProperty, value);
        SetValues();
    }
}

In der Methode SetValues werden dann aus dem Score-Objekt einzelne werte ausgelesen und in mehreren TextBlocks im XAML ausgegeben. Das geschieht vom Code aus und ohne Binding.

Jetzt kommt das Control, das das oben genannte UserControl einbindet (mehrmals, aber das spielt glaub ich keine Rolle). Der Code in XAML sieht so aus:

<FitaScore_Controls:ScoreControl Margin="0" HorizontalAlignment="Center" Score="{Binding Score1}"/>

Im ViewModel hab ich dann das Property Score1 ganz normal vom Type Score angelegt:

private Score _score1;
public Score Score1
{
    get { return _score1; }
    set
    {
        _score1 = value;
        NotifyOfPropertyChanged(() => Score1);
    }
}

Aus irgendeinem Grund, wird aber im UserControl nichts angezeigt. Ich bekomme auch keinen Fehler und im Trace steht auch nichts drin, was auf einen Fehler hindeuten könnte.
Breakpoints UserControl bringen auch nichts... es wird nie einer davon angesprungen, wie wenn die Property im UserControl nie aufgerufen werden würde.
Ich hab jetzt überhaupt keine Ahnung mehr, was ich noch kontrollieren könnte.

Achja... vielleicht spielt das auch eine Rolle: Ich verwende Caliburn.Micro als MVVM Framework. Bisher hatte ich damit auch nie Probleme, aber das bekomm ich jetzt einfach nicht hin. Wobei ich aber bisher auch kaum was mit DependencyProperties gemacht habe.

6.862 Beiträge seit 2003
vor 12 Jahren

Hallo,

das SetValues darfst du nicht machen im PropertyWrapper. Dort gehört nur SetValue bzw. GetValue jeweils rein, mehr nicht.

Der Grund liegt einfach darin, dass der XAML Parser beim Binding eines DPs immer direkt SetValue und GetValue aufruft. Was da für PropertyWrapper definiert sind, interessiert den überhaupt nicht, und das ist auch der Grund warum dein SetValues nie aufgerufen wird beim Binding.

Man kann beim registrieren des DP ein ChangeDelegate mit angeben, was bei Änderung des DPs mit aufgerufen wird. Dort könntest du das erledigen, was du in SetValues drin hast. Wobei das aber so oder so irgendwie komisch ist. Das mit dem Auslesen einzelner Werte aus dem Score Objekt könntest du doch auch einfach per Binding machen.

Baka wa shinanakya naoranai.

Mein XING Profil.

Mossi Themenstarter:in
199 Beiträge seit 2006
vor 12 Jahren

Ich find das ehrlich gesagt auch seltsam mit dem SetValues. Aber das war ein Versuch, wie ich ein Problem mit dem Binding umgehe 😃

wenn ich im UserControl mit Binding arbeite, bekomme ich in der Trace-Ausgabe folgende Fehlermeldung

BindingExpression path error: 'Score1' property not found on 'object' ''ScoreControl' (Name='')'. BindingExpression:Path=Score1; DataItem='ScoreControl' (Name=''); target element is 'ScoreControl' (Name=''); target property is 'Score' (type 'Score')

Im UserControl hab ich dabei den DataContext gesetzt, damit ich auf die Prtoperty im CodeBehind zugreifen kann.

DataContext="{Binding RelativeSource={RelativeSource Self}}"

vielleicht hab ich ja schon einen grundlegenden Fehler in der ganzen Sache.

Wie würde denn die Syntax mit dem ChangeDelegate aussehen?

446 Beiträge seit 2004
vor 12 Jahren

Du hast den DataContext auf dein Control gesetzt. Die Property Score1 gibts in deinem Control nicht, weil diese im MVVM vorhanden ist.

Lass das setzen des DataContext im UserControl weg. Du willst ja als DataContext das MVVM haben.

Bzw. du hast im Prinzip das hier gemacht:

Wir gehen davon aus, dass das UserControl genau den gleichen DataContext wie das Window besitzt. Sämtliche Standard UserControls, wie zum Beispiel TextBox'es, ListViews, erben den DataContext vom Übergeordneten Control. Weil aber das UserControl den DataContext auf sich selber setzt, haben wir einen anderen DataContext wie beim Window.<-- hier wird ja eigentlich das MVVM erwartet

Mach im UserControl das Binding Relativ dann müsste es gehen.

Diese Problem kann man mithilfe der RelativeSource umgehen. Hätte das Binding wie folgt ausgeshene, wäre es egal, welchen DataContext das UserControl besitzt.

<TextBlock Text="{Binding Path=Score,RelativeSource=
{RelativeSource Mode=FindAncestor,AncestorType={x:Type local:UserControlName}}}"></TextBlock>

Dennoch gibt es Gegebenheit in dem ein Binding ohne RelativeSource berechtigt ist. http://get-the-solution.net/2009/02/20/itemssource-zu-usercontrol-hinzufugen/

[EDIT] Sry, es muss natürlich Score heißen, weil du ja auf die DP des Control binden willst.[/Edit].

Schaut mal im IRC vorbei:
Server: https://libera.chat/ ##chsarp

Mossi Themenstarter:in
199 Beiträge seit 2006
vor 12 Jahren

Irgendwie versteh ich das aber nicht.

<TextBlock Text="{Binding Path=Score1,RelativeSource=
{RelativeSource Mode=FindAncestor,AncestorType={x:Type local:UserControlName}}}"></TextBlock>

Dieses Binding kann ich nicht im UserControl setzen, weil das UserControl ja Score1 nicht kennt. Score1 ist eine Property im ViewModel vom Type Score. Insgesamt gibt es davon 6 Properties (Score1 - Score6)
Der View zum ViewModel soll für diese 6 Properties ein UserControl zur Anzeige verwenden. In diesem UserControl gibt es ein DependencyProperty vom Typ Score (in meinem Fall heißt die Property auch Score)

Wenn ich jetzt im UserControl die Angabe vom DataContext (siehe oben) weglasse bekomme ich folgende Fehlermeldung

System.Windows.Data Error: 40 : BindingExpression path error: 'Score' property not found on 'object' ''SheetViewModel' (HashCode=4029665)'. BindingExpression:Path=Score.Arrow1.Value; DataItem='SheetViewModel' (HashCode=4029665); target element is 'TextBlock' (Name='_arrow1'); target property is 'Text' (type 'String')

Das ist auch irgendwie klar, weil der DataContext ja dann auf das ViewModel zeigt, in dem es aber die Property Score nicht gibt. Das UserControl soll das Binding ja aus dem CodeBehind holen.

Ich hab langsam das Gefühl, dass ich vollkommen den falschen Ansatz habe. Ich denke, dass es nicht sonderlich gut gelöst ist, wenn ich ein UserControl verwende, das selbst CodeBehind hat und das Ganze dann innerhalb von einer MVVM Umsetzung. Vielleicht sollte ich mir das nochmal durch den Kopf gehen lassen.

Mossi Themenstarter:in
199 Beiträge seit 2006
vor 12 Jahren

Jetzt hab ich doch noch selber eine Lösung gefunden bzw. hab den Fehler gefunden.

Im UserControl hab ich ja eingebaut, dass ich per Binding auf das Score-Objekt im CodeBehind zugreif. Und hier hab ich zu wenig angegeben
Ursprünglich hatte ich folgendes im XAML stehen:

<TextBlock Text="{Binding Score.Arrow1.Value}" />

Hier bekam ich dann eben die Fehlermeldung, dass Score im ViewModel nicht vorhanden ist. Daher hab ich dann den DataContext gesetzt, was allerdings dazu geführt hat, dass der View der das UserControl verwendet die Properties des ViewModels nicht mehr gefunden hat.
Die Lösung ist letztendlich ganz einfach. Weg mit dem DataContext wie "Briefkasten" schon geschrieben hat und stattdessen das Binding genauer spezifizieren:

<TextBlock Text="{Binding ElementName=SC, Path=Score.Arrow1.Value}" />

Und jetzt klappt's auch mit der Nachbarin.

Vielen Dank an alle für Erklärungen und die Denkanstöße