Laden...

Binding auf Property aus ViewModel

Erstellt von theSoulT vor 2 Jahren Letzter Beitrag vor 2 Jahren 736 Views
T
theSoulT Themenstarter:in
64 Beiträge seit 2018
vor 2 Jahren
Binding auf Property aus ViewModel

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();
                }
            }
        }

2.078 Beiträge seit 2012
vor 2 Jahren

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.

T
theSoulT Themenstarter:in
64 Beiträge seit 2018
vor 2 Jahren

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?

2.078 Beiträge seit 2012
vor 2 Jahren

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.

T
theSoulT Themenstarter:in
64 Beiträge seit 2018
vor 2 Jahren

{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... 👍

T
theSoulT Themenstarter:in
64 Beiträge seit 2018
vor 2 Jahren

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

F
10.010 Beiträge seit 2004
vor 2 Jahren

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.

T
theSoulT Themenstarter:in
64 Beiträge seit 2018
vor 2 Jahren

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

2.078 Beiträge seit 2012
vor 2 Jahren

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.

F
10.010 Beiträge seit 2004
vor 2 Jahren

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

T
theSoulT Themenstarter:in
64 Beiträge seit 2018
vor 2 Jahren

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 😁

F
10.010 Beiträge seit 2004
vor 2 Jahren

[Artikel] MVVM und DataBinding
Ist das eigentlich alles sehr einfach erklärt.
Unter 3. steht dann u.a. wie das mit dem CanExecute geht