Willkommen auf myCSharp.de! Anmelden | kostenlos registrieren
 | Suche | FAQ

Hauptmenü
myCSharp.de
» Startseite
» Forum
» Suche
» Regeln
» Wie poste ich richtig?

Mitglieder
» Liste / Suche
» Wer ist online?

Ressourcen
» FAQ
» Artikel
» C#-Snippets
» Jobbörse
» Microsoft Docs

Team
» Kontakt
» Cookies
» Spenden
» Datenschutz
» Impressum

  • »
  • Community
  • |
  • Diskussionsforum
Binding auf Property aus ViewModel
theSoulT
myCSharp.de - Member



Dabei seit:
Beiträge: 40

Themenstarter:

Binding auf Property aus ViewModel

beantworten | zitieren | melden

Hallo zusammen,

ich komm leider bei meinem Programm nicht weiter.
Ich möchte drei Buttons in Abhängigkeit von zwei Variablen in meinem ViewModel auf IsEnabled = True setzen bzw false.
Dazu habe ich mit den MultiDataTriggern versucht auf die Variablen zu binden. Aber leider klappt das nicht so wie ich mir das vorstelle.
Habt ihr noch eine Idee?

                    <StackPanel Orientation="Vertical" Margin="200 10" Grid.ColumnSpan="2">
                        <StackPanel.Resources>
                            <Style BasedOn="{StaticResource {x:Type Button}}" TargetType="Button">
                                <Setter Property="Background" Value="{DynamicResource clrPrimary}"/>
                                <Setter Property="Margin" Value="0 0 0 10"/>
                                <Setter Property="FontSize" Value="20"/>
                                <Setter Property="Height" Value="Auto"/>
                                <Setter Property="IsEnabled" Value="True"/>
                                <Style.Triggers>
                                    <MultiDataTrigger>
                                        <MultiDataTrigger.Conditions>
                                            <Condition Binding="{Binding AdaptIsReady}" Value="false"/>
                                            <Condition Binding="{Binding UnitIsReady}" Value="false"/>
                                        </MultiDataTrigger.Conditions>
                                        <Setter Property="IsEnabled" Value="False"/>
                                    </MultiDataTrigger>
                                </Style.Triggers>
                            </Style>
                        </StackPanel.Resources>
                        <Button Content="FUT starten" DataContext="{Binding Source={StaticResource Locator}, Path=MainVM}" Command="{Binding UpdateViewCommand}" CommandParameter="startFUT"/>
                        <Button Content="Manuell testen" DataContext="{Binding Source={StaticResource Locator}, Path=MainVM}" Command="{Binding UpdateViewCommand}" CommandParameter="manuellTesting"/>
                        <Button Content="PVIS Befehle ausführen" DataContext="{Binding Source={StaticResource Locator}, Path=MainVM}" Command="{Binding UpdateViewCommand}" CommandParameter="pvisAction"/>
                    </StackPanel>


        private bool adaptIsReady;
        public bool AdaptIsReady
        {
            get { return adaptIsReady; }
            set
            {
                if (adaptIsReady != value)
                {
                    adaptIsReady = value;
                    RaisePropertyChanged();
                }
            }
        }

        private bool unitIsReady;
        public bool UnitIsReady
        {
            get { return unitIsReady; }
            set
            {
                if (unitIsReady != value)
                {
                    unitIsReady = value;
                    RaisePropertyChanged();
                }
            }
        }
private Nachricht | Beiträge des Benutzers
Palladin007
myCSharp.de - Member

Avatar #avatar-4140.png


Dabei seit:
Beiträge: 1.762
Herkunft: Düsseldorf

beantworten | zitieren | melden

Ich sehe so auf Anhieb keinen groben Fehler, außer dass ich so gar kein Fan vom (Service) Locator Pattern bin ^^

Sind die gezeigten Properties im MainVM, oder woanders?

Und schau mal in VisualStudio (ich nutze 2022), das hat ein "XAML Binding Failures"-Fenster. Alternativ sollten Binding-Fehler im Output landen.
Wenn das Binding scheitert, dann sollte das dort auftauchen.
private Nachricht | Beiträge des Benutzers
theSoulT
myCSharp.de - Member



Dabei seit:
Beiträge: 40

Themenstarter:

beantworten | zitieren | melden

Danke dir, das war schon der Hinweis, der mir gefehlt hat. Die Variablen liegen in einer anderen VM.
Ich vermute, es gibt keine Möglichkeit, für die Commands einen anderen DataContext zu setzen, als für meine Variablen, oder?
private Nachricht | Beiträge des Benutzers
Palladin007
myCSharp.de - Member

Avatar #avatar-4140.png


Dabei seit:
Beiträge: 1.762
Herkunft: Düsseldorf

beantworten | zitieren | melden

Du kannst jeden DataContext setzen, den Du setzen willst, aber irgendwie musst Du den ja auch finden.
Der einfachste Weg ist, dass ein MainViewModel Referenzen zu den einzelnen Dialogen/Bestandteilen hat und die haben Referenzen zu den Details, die sie benötigen.

Im Style arbeitest Du jedenfalls immer gegen den DataContext, den auch das Ziel-Objekt (der Button) hat.
Wenn Du da einen anderen DataContext brauchst, dann setze am Button selber einen anderen oder keinen DataContext.
Oder Du setzt ein Binding, in dem Du mit Hilfe einer RelativeSource ein anderes Control (im VisualTree hoch suchen, z.B. das StackPanel) suchst und dann direkt an den DataContext davon bindest.

{Binding DataContext.MyProperty, RelativeSource={RelativeSource AncestorType=StackPanel}}

Normalerweise ist das nicht notwendig, das macht man nur, wenn man aus irgendeinem Grund einen anderen DataContext hat, das aber nicht ändern kann.
private Nachricht | Beiträge des Benutzers
theSoulT
myCSharp.de - Member



Dabei seit:
Beiträge: 40

Themenstarter:

beantworten | zitieren | melden

Zitat von Palladin007
{Binding DataContext.MyProperty, RelativeSource={RelativeSource AncestorType=StackPanel}}

Normalerweise ist das nicht notwendig, das macht man nur, wenn man aus irgendeinem Grund einen anderen DataContext hat, das aber nicht ändern kann.

Das war aber jetzt genau das, was ich gebraucht habe
Mein DataContext für den Button hab ihc ja auf MainVM gelegt, da dort die UpdateViewcommand liegt.
Aber die Variable für meine Prüfung liegt in einer anderen VM.

Vielen Dank dir. Jetzt läuft es so, wie ich mir das vorstelle...
private Nachricht | Beiträge des Benutzers
theSoulT
myCSharp.de - Member



Dabei seit:
Beiträge: 40

Themenstarter:

beantworten | zitieren | melden

Servus Palladin007 und alle anderen ,

bin jetzt etwas weiter und hab meine Variable verschoben.
Aktueller Stand:

StartView.xaml

<StackPanel Orientation="Vertical" Grid.ColumnSpan="2" DataContext="{Binding Source={StaticResource Locator}, Path=DataVM}">
                        <StackPanel.Resources>
                            <Style BasedOn="{StaticResource {x:Type Button}}" TargetType="Button">
                                <Setter Property="Background" Value="{DynamicResource clrPrimary}"/>
                                <Setter Property="Margin" Value="0 0 0 10"/>
                                <Setter Property="FontSize" Value="20"/>
                                <Setter Property="Height" Value="Auto"/>
                                <Setter Property="IsEnabled" Value="False"/>
                                <Setter Property="ToolTip" Value="Adapter oder Gerät ist nicht bereit!"/>
                                <Setter Property="ToolTipService.IsEnabled" Value="True"/>
                                <Setter Property="ToolTipService.ShowOnDisabled" Value="True"/>
                                <Style.Triggers>
                                    <MultiDataTrigger>
                                        <MultiDataTrigger.Conditions>
                                            <Condition Binding="{Binding DataContext.UnitsModel.Instance.AdaptIsReady, RelativeSource={RelativeSource AncestorType=StackPanel}}" Value="True"/>
                                            <Condition Binding="{Binding DataContext.UnitsModel.Instance.UnitIsReady, RelativeSource={RelativeSource AncestorType=StackPanel}}" Value="True"/>
                                        </MultiDataTrigger.Conditions>
                                        <MultiDataTrigger.Setters>
                                            <Setter Property="IsEnabled" Value="True"/>
                                            <Setter Property="ToolTipService.IsEnabled" Value="False"/>
                                        </MultiDataTrigger.Setters>
                                    </MultiDataTrigger>
                                </Style.Triggers>
                            </Style>
                        </StackPanel.Resources>
                        <Button Content="FUT starten" DataContext="{Binding Source={StaticResource Locator}, Path=MainVM}" Command="{Binding UpdateViewCommand}" CommandParameter="startFUT"/>
                        <Button Content="Manuell testen" DataContext="{Binding Source={StaticResource Locator}, Path=MainVM}" Command="{Binding UpdateViewCommand}" CommandParameter="manuellTesting"/>
                        <Button Content="PVIS Befehle ausführen" DataContext="{Binding Source={StaticResource Locator}, Path=MainVM}" Command="{Binding UpdateViewCommand}" CommandParameter="pvisAction"/>
                    </StackPanel>

DataViewModel.cs (DataVM):


        private UnitsModel untisModel = UnitsModel.Instance;
        public UnitsModel UntisModel
        {
            get { return untisModel; }
            set
            {
                if (untisModel != value)
                {
                    untisModel = value;
                    RaisePropertyChanged();
                }
            }
        }

UnitsModel.cs:


        private static UnitsModel instance = null;
        private static readonly object singletonLock = new object();
        public static UnitsModel Instance
        {
            get
            {
                lock (singletonLock)
                {
                    if (instance == null)
                    {
                        instance = new UnitsModel();
                    }
                    return instance;
                }
            }
        }

        private bool adaptIsReady;
        public bool AdaptIsReady
        {
            get { return adaptIsReady; }
            set
            {
                if (adaptIsReady != value)
                {
                    adaptIsReady = value;
                    OnPropertyChanged();
                }
            }
        }

        private bool unitIsReady;
        public bool UnitIsReady
        {
            get { return unitIsReady; }
            set
            {
                if (unitIsReady != value)
                {
                    unitIsReady = value;
                    OnPropertyChanged();
                }
            }
        }

Leider funktioniert mein Binding mit dem Singleton so nicht, wie ich mir das vorstelle. Hat hier noch jemand einen Tipp?

Lg TheSoulT
private Nachricht | Beiträge des Benutzers
FZelle
myCSharp.de - Experte



Dabei seit:
Beiträge: 9.972

beantworten | zitieren | melden

Warum machst Du es so umständlich?

1. Warum 1 Command mit verschiedenen Parametern?
Das sind meist Einzeiler und lassen sich durch 2. besser managen. Kannst ja das UpdateView() weiterhin aufrufen

2. Ein RelayCommand erlaubt auch ein Can..., da machst du dann die Abfrage der Bedingungen.
private Nachricht | Beiträge des Benutzers
theSoulT
myCSharp.de - Member



Dabei seit:
Beiträge: 40

Themenstarter:

beantworten | zitieren | melden

Servus FZelle,

ich bin da noch ziemlich am Anfang und hab das halt so gelernt
Ich werde es mir mal anschauen.
Aber das hat ja nichts mit meinem eigentlichen Problem zu tun oder?

Lg
private Nachricht | Beiträge des Benutzers
Palladin007
myCSharp.de - Member

Avatar #avatar-4140.png


Dabei seit:
Beiträge: 1.762
Herkunft: Düsseldorf

beantworten | zitieren | melden

Den Binding-Pfad "DataContext.UnitsModel.Instance" gibt es nicht.
"Instanz" ist static und kann nicht in Bindings genutzt werden.
An statische Quellen bindet man so:

{Binding MyProperty, Source={x:Status UnitsModel.Instance}}

Sauber oder klug ist das aber nicht, so verlierst Du früher oder später nur den Überblick, was wie worauf zugreift.
Am einfachsten ist, wenn ein Control an das Nächste den DataContext vererbt (passiert automatisch) oder ein untergeordnetes Objekt (klassisches Binding) setzt.

Es kommt vor, dass man (was Du eingangs gefragt hast) nicht mit dem aktuellen DataContext arbeiten kann.
Das ist kommt z.B. beim ItemsControl immer wieder vor, weil im ItemTemplate das Item als DataContext steht und nicht das ursprüngliche ViewModel, was alle Items hat.
Wenn Du nun z.B. an etwas vom ursprünglichen ViewModel binden willst, geht das mit einer RelativeSource, die das ItemsControl sucht.

Und warum setzt Du am Button erst den DataContext, wenn Du die andere Instanz nur an einer Stelle (Command) brauchst und an zwei anderen Stellen nicht?
Pass das Command-Binding so an, dass es das richtige ViewModel sucht und lass den DataContext, wie er ist.
Im Trigger hast Du dann den DataContext, den der Button hat und der hat den DataContext, den dein StackPanel hat und so weiter.
private Nachricht | Beiträge des Benutzers
FZelle
myCSharp.de - Experte



Dabei seit:
Beiträge: 9.972

beantworten | zitieren | melden

Zitat
Aber das hat ja nichts mit meinem eigentlichen Problem zu tun oder?
Jain.
Wenn du die richtige Herangehensweise benutzt, musst du deine Klimmzüge nicht machen
private Nachricht | Beiträge des Benutzers
theSoulT
myCSharp.de - Member



Dabei seit:
Beiträge: 40

Themenstarter:

beantworten | zitieren | melden

Vielen Dank schonmal für eure Mühen. Leider hab ich mir das alles selber beigebracht und versteh das noch nicht so ganz, was Ihr versucht zu erklären
Könntet ihr das bitte anhand eines Beispiels erklären? Das ist dann für mich verständlicher
private Nachricht | Beiträge des Benutzers
FZelle
myCSharp.de - Experte



Dabei seit:
Beiträge: 9.972

beantworten | zitieren | melden

[Artikel] MVVM und DataBinding
Ist das eigentlich alles sehr einfach erklärt.
Unter 3. steht dann u.a. wie das mit dem CanExecute geht
private Nachricht | Beiträge des Benutzers