Binding von Eigenschaften aus einer anderen Klasse

  • WPF

Es gibt 6 Antworten in diesem Thema. Der letzte Beitrag () ist von PadreSperanza.

    Binding von Eigenschaften aus einer anderen Klasse

    Hallo Leute,

    ich stehe mal wieder vollkommen auf dem Schlauch. Ich bin mittlerweile soweit, das MVVM Pattern zu verwenden und auch die Bindings habe ich (dachte ich) bereits verstanden. Aber nun stehe ich arg auf dem Schlauch und weiß nicht wo mein Denkfehler liegt:

    Ich habe eine Klasse AppSettings, in der ich App-übergreifende Variablen speichern möchte und diese dann auch auf Festplatte schreiben möchte. Innerhalb dieser habe ich mehrere Variablen definiert auf die ich zugreife (INotifyChanged ist dort implementiert). Und das Binding an diese Variablen funktioniert auch ohne Probleme, wie das Beispiel zeigt:

    XML-Quellcode

    1. <ComboBox Grid.Row="1" Grid.Column="2" ItemsSource="{Binding Source={x:Static ISource:FontSize.Values}, Path=ArraySource}" SelectedItem="{Binding Source={x:Static model:AppSettings.Settings}, Path=FontSize}"/>
    2. <TextBlock Text="Schriftart:" Grid.Row="2"/>
    3. <ComboBox Grid.Row="2" Grid.Column="2" ItemsSource="{Binding Source={x:Static ISource:FontFamily.Fonts}, Path=Values}" SelectedItem="{Binding Source={x:Static model:AppSettings.Settings}, Path=FontFamily}" />
    4. <TextBlock Text="Größe der Schaltflächen:" Grid.Row="3"/>
    5. <Slider Minimum="1" Maximum="7" TickFrequency="1" IsSnapToTickEnabled="True" Grid.Row="3" Grid.Column="2" TickPlacement="BottomRight" Value="{Binding Source={x:Static model:AppSettings.Settings}, Path=ButtonSize}"/>
    6. <TextBlock Text="{Binding Source={x:Static model:AppSettings.Settings}, Path=ButtonSize}" Grid.Row="3" Grid.Column="3" Margin="10,0,0,0"/>
    7. <TextBlock Text="Größe der Tabs:" Grid.Row="4"/>
    8. <Slider Minimum="1" Maximum="5" TickFrequency="1" IsSnapToTickEnabled="True" Grid.Row="4" Grid.Column="2" TickPlacement="BottomRight" Value="{Binding Source={x:Static model:AppSettings.Settings}, Path=TabSize}"/>
    9. <TextBlock Text="{Binding Source={x:Static model:AppSettings.Settings}, Path=TabSize}" Grid.Row="4" Grid.Column="3" Margin="10,0,0,0"/>


    Wie man sieht greife ich zB mit dem Slider auf TabSize zu und lasse mir diese Variable auch in der TextBox daneben anzeigen. Die Werte verändern sich auch, wenn ich am Slider schiebe.
    Soweit so gut.

    Wenn ich nun aber versuche, den Wert anderweitig zu nutzen, zB die TabSize als Binding-Referenz zu verwenden, aktualisiert sich da nichts:

    Bsp:

    XML-Quellcode

    1. <TabItem.Header>
    2. <Image Width="{Binding TabSize, Converter={StaticResource TabSizeConverter}, Source={StaticResource AppSettings}}" Source="images/autostart/autostart.png" ToolTipService.ToolTip="Autostart"/>
    3. </TabItem.Header>


    Hier wird nun weder der Converter aufgerufen, noch ein anderer Wert gesetzt. Wenn ich zur Designzeit nun den Wert in der AppSettings setze, wird das Verhalten auch korrekt implementiert, nur das Schieben des Schiebereglers (oder das Auswählen der ComboBox) zeigen kein dynamisches Verhalten. Und ich frage mich wieso? Ich meine, da die Textbox den richtigen Wert bekommt, muss INotifyPropertyChanged auch richtig ausgelöst werden. Doch der Wert kommt bei anderen Elementen nicht an. Und nun sehe ich den Wald vor lauter Bäumen nicht mehr.

    Vielleicht hat ja jemand Rat :)
    Hallo

    Ich kann es hier leider nicht testen solange ich die Settings-Klasse nicht kenne. Da ich nie direkt auf eine Statische Klasse binde habe ich hier keine Erfahrung. Poste doch mal ein Beispiel für die Settings-Klasse.
    Aber es könnte schon helfen von ​, Source={StaticResource AppSettings}} auf ​, Source={DynamicResource AppSettings}} zu stellen. Nur ne vermutung.

    Grüße
    Sascha
    If _work = worktype.hard Then Me.Drink(Coffee)
    Seht euch auch meine Tutorialreihe <WPF Lernen/> an oder abonniert meinen YouTube Kanal.

    ## Bitte markiere einen Thread als "Erledigt" wenn deine Frage beantwortet wurde. ##

    Aber es könnte schon helfen von , Source={StaticResource AppSettings}} auf , Source={DynamicResource AppSettings}} zu stellen. Nur ne vermutung.
    Hatte ich auch erst gedacht. Aber das bringt tatsächlich keinen Erfolg, da der Unterschied darin besteht, die Ressource erst dynamisch zuzuweisen, nicht aber den gebundenen Inhalt.

    Ich habe früher mit der Settings-Property gearbeitet. Muss mich davon nun loslösen. Und die war auch statisch. Es funktioniert im Grunde auch (siehe TextBox neben den Slidern, die das auch richtig annehmen). hier der Auszug der AppSettings:

    C#-Quellcode

    1. public class AppSettings : NotifyBase
    2. {
    3. public static AppSettings Settings
    4. {
    5. get;
    6. private set;
    7. }
    8. static AppSettings()
    9. {
    10. Settings = LoadConfigFromDisk();
    11. }
    12. ..
    13. private byte _buttonSize;
    14. [DataMember]
    15. public byte ButtonSize { get { return _buttonSize; } set { SetField(ref _buttonSize, value); } }
    16. private byte _tabSize;
    17. [DataMember]
    18. public byte TabSize { get { return _tabSize; } set { SetField(ref _tabSize, value); } }
    19. ..


    Die Klasse ist als typischer Singleton implementiert und auch als Ressource im XAML-Code eingebunden. Das funktioniert auch soweit. Nur der Umschwung auf andere Eigenschaften interessanterweise nicht.

    NotifyBase ist die Klasse, von der ich erbe, die das INotifyPropertyChanged implementiert:

    C#-Quellcode

    1. public class NotifyBase : INotifyPropertyChanged
    2. {
    3. public event PropertyChangedEventHandler PropertyChanged;
    4. protected void NotifyPropertyChanged([CallerMemberName] string PropertyName = null)
    5. {
    6. PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(PropertyName));
    7. }
    8. protected bool SetField<T>(ref T field, T value, [CallerMemberName] string PropertyName = null)
    9. {
    10. if (EqualityComparer<T>.Default.Equals(field, value)) return false;
    11. field = value;
    12. NotifyPropertyChanged(PropertyName);
    13. return true;
    14. }
    15. }



    Edit: Eine Sache ist mir schon mal aufgefallen:

    XML-Quellcode

    1. ​<Slider Minimum="1" Maximum="5" TickFrequency="1" IsSnapToTickEnabled="True" Grid.Row="4" Grid.Column="2" TickPlacement="BottomRight" Value="{Binding TabSize, Source={StaticResource AppSettings}}"/>
    (So sollten statische Variablen gebunden werden)

    und

    XML-Quellcode

    1. ​<Slider Minimum="1" Maximum="5" TickFrequency="1" IsSnapToTickEnabled="True" Grid.Row="4" Grid.Column="2" TickPlacement="BottomRight" Value="{Binding Source={x:Static model:AppSettings.Settings}, Path=TabSize}"/>


    haben einen Unterschied: das obere ist wirklich die statische Eigenschaft des Singleton. Das untere ist durch x:static und Einbindung in die Ressourcen-Datei eine eigene Instanz. Das heißt, Werte des einen sind dem anderen unbekannt und umgekehrt.

    Aber auch wenn ich alles auf die rein statische Variante umändere erhalte ich dennoch keine Änderung der Width-Eigenschaft des Images

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „PadreSperanza“ ()

    Ja, das war mir auch aufgefallen (als ich das Binding mit dem VS19 Automatismus versucht hatte) und gesehen, dass es anders geschrieben wurde. Habe den Vergleich angestellt (bereits im oberen Post als Edit hinzugefügt) und musste feststellen, dass damit tatsächlich zwei unterschiedliche Instanzen angesprochen werden. Wenn ich alles jedoch einheitlich schreibe, bleibt es dabei, dass die Textbox den richtigen Text anzeigt, das Image aber seine Länge nicht verändert.
    So, nachdem ich nun alle Bindings auf das tatsächliche gestellt habe:

    XML-Quellcode

    1. .. Value="{Binding Source={x:Static model:AppSettings.Settings}, Path=TabSize}"/>

    nach dem oben genannten Modus, funktioniert jetzt zumindest das Setzen der FontFamily und der FontSize. Die haben auch einen Converter. Und funktionieren.

    Ich habe nun, um deine Anmerkung zu überprüfen die Werte des Sliders von

    Quellcode

    1. Maximum=5
    auf

    Quellcode

    1. Maximum=50
    gestellt und den Converter eliminiert und direkt gebunden. Siehe da, nun funktioniert es auch einwandfrei. Tatsächlich hatte ich das schonmal versucht, aber da waren tatsächlich noch die Unterschiede mit den zwei Instanzen vorhanden. Also bleibt nach dem Geradeziehen des ersten Schlamassels nur noch der Converter. Da muss ich mal reinschauen.

    Vielen Dank :)

    EDIT:

    So, das Mysterium Nr. 2 ist gelöst... und ich bin a Depperl.

    hier der Code für den Converter:

    C#-Quellcode

    1. ​[ValueConversion(targetType: typeof(byte), sourceType: typeof(int))]
    2. class IntegerToButtonSizeConverter : IValueConverter
    3. {
    4. public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    5. {
    6. switch (value)
    7. {
    8. case 1:
    9. return 20;
    10. case 2:
    11. return 25;
    12. case 4:
    13. return 35;
    14. case 5:
    15. return 40;
    16. case 6:
    17. return 45;
    18. case 7:
    19. return 50;
    20. default:
    21. return 30;
    22. }
    23. }
    24. public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    25. {
    26. throw new NotImplementedException();
    27. }
    28. }


    Was ich komplett außer Acht gelassen habe: im Switch-Statement überprüfe ich value und vergleiche. Leider ist eine 2 als byte nicht genau gleich einer 2 als integer... ich muss das Casten, damit er damit was anfangen kann... Aber dieser Umstand hat zum Glück dazu geführt, dass ich auch verstanden habe, wie man statisch korrekt bindet. Danke auch für die schnellen Anmerkungen

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „PadreSperanza“ ()