Laden...

Wie mit MVVM auf ComboBox SelectionChanged-Event reagieren?

Erstellt von GeneVorph vor 4 Jahren Letzter Beitrag vor 4 Jahren 4.386 Views
G
GeneVorph Themenstarter:in
180 Beiträge seit 2015
vor 4 Jahren
Wie mit MVVM auf ComboBox SelectionChanged-Event reagieren?

Hallo,

ich glaube, es ist nur ein kleiner Denkfehler, der mich gerade vom Weiterkommen abhält, aber ich bekommes es alleine nicht hin.

Einer ObservableCollection vom Typ int soll immer das in einer ComboBox ausgewählte Element hinzugefügt werden.

Das Problem: die ComboBox befindet sich auf einem UserControl, das dynamisch hinzugefügt wird. Ich müsste also, nach allem was ich zu wissen glaube, im ViewModel des UserControls auf den SelectionChange reagieren. Da ich gerade versuche MVVM zu lernen, soll der CodeBehind unangetastet bleiben.

Die einzige Methode, die ich mir nun vorstellen kann, ist im XAML-Code auf das "SelectionChanged"-Event zu reagieren, mit Hilfe eines Event-Triggers. Sieht bei mir so aus:

<UserControl x:Class="UserControlTest.PathControl"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 

             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"            <!-- stimmt das hier?-->

             xmlns:local="clr-namespace:UserControlTest"            
             mc:Ignorable="d" 
             d:DesignHeight="60" d:DesignWidth="800">
   
    
    <StackPanel Orientation="Horizontal">

        <ComboBox x:Name="combo" Margin="10" MinWidth="60" VerticalAlignment="Center" ItemsSource="{Binding AvailableNumbers}" SelectedIndex="{Binding TheIndex}" SelectedItem="{Binding TheValue, Mode=TwoWay}">
            <d:Interaction.Triggers EventName="SelectionChanged">
                <d:InvokeCommandAction Command="{Binding SelectionChangedCommand}" CommandParameter="SelectedItem" />
            </d:Interaction.Triggers>
        </ComboBox>

        <TextBox Margin="10" MinWidth="120" Text="{Binding TheText, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
    </StackPanel>
</UserControl>

Das Command (SelectionChangedCommand) stelle ich im ViewModel des UserControls bereit. Wenn ich ein Item aus der ComboBox auswähle, kann ich im Debugger jedoch sehen, das dieses Command nie ausgeführt wird.
Ich vermute derzeit zwei Fehler: die Deklaration des Namespace für Interactions (s. Kommentar im XAML-Code), bzw. ein fehlender Schritt in der UserControl ViewModel-Klasse?

viele Grüße
Vorph

P
441 Beiträge seit 2014
vor 4 Jahren

Der Namespace "d" ist meines Wissens immer vordefiniert, den solltest du nicht ändern. Generell hat dieser Einfluss auf den Designer (im Originalzustand).

Du suchst diesen hier:
http://schemas.microsoft.com/expression/2010/interactivity

An sich sollte der Designer dir bereits sagen, dass im Namespace d kein Interaction enthalten ist.

Gefunden durch Google: https://stackoverflow.com/questions/1048517/wpf-calling-commands-via-events

G
GeneVorph Themenstarter:in
180 Beiträge seit 2015
vor 4 Jahren

Hallo Papst,

vielen Dank erstmal. Der von dir verlinkte Post auf StackOverflow war mein Ausgangspunkt; zwar motzt der Compiler nicht, wenn ich d: schreibe, aber du hast natürlich Recht.

Ich habe außerdem herausgefunden, dass man sich die Library für Interativity über NuGet-Konsole holen kann. Jedoch - ich bin derzeit etwas ratlos WAS ich genau in die Anweisung für die Event.Trigger schreiben muss



<i:Interaction.Triggers>
    <i:EventTrigger>
        <!-- ??? -->
    </i:EventTrigger>
</i:Interaction.Triggers>


[Im code, den ich zuvor gepostet habe, wird noch eine Klasse InvokeCommandAction aufgerufen - die aber bei mir gar nicht existiert 8o kann also auch nicht funktionieren.] --> EDIT: Blödsinn gelabert - bitte ignorieren!

Wie gesagt, Fehlermeldungen bekomme ich keine und das Ganze wird auch kompiliert.

Ich bin derzeit leider nicht an einem PC mit meinem Projekt; muss also leider bis morgen warten. Frage mich aber, wass ich nun anstelle der Fragezeichen ansprechen soll? Setter?

Gruß
Vorph

301 Beiträge seit 2009
vor 4 Jahren

Das magische Suchwort für dich lautet wohl EventToCommand. Unter diesem Begriff versteckt sich am häufigsten was du tun möchtest. Jedes gängige MVVM Framework hat dazu eine fertige Implementierung mitsamt der Einbindung der notwendigen Abhängigkeiten.

https://stackoverflow.com/questions/5868589/mvvm-light-adding-eventtocommand-in-xaml-without-blend-easier-way-or-snippet

Das hier ist ein Beispiel mit MVVM Light. Eventuell ziehst du ja in Betracht ein bestehendes MVVM Framework zu nutzen anstatt selbst eines zu schreiben.

5.657 Beiträge seit 2006
vor 4 Jahren

Wenn der Benutzer einen Wert in der ComboBox auswählt, wird durch das Binding die Eigenschaft TheValue im ViewModel geändert. Wenn du dort im Setter auf die Änderung reagierst, brauchst du kein Event, kein Command und kein Package.

Weeks of programming can save you hours of planning

G
GeneVorph Themenstarter:in
180 Beiträge seit 2015
vor 4 Jahren

Hallo,

vielen Dank für die Infos - ich hab's hinbekommen! Hier der Code, wie er hätte sein müssen, damit es funktioniert:

In der View des UserControls


<UserControl x:Class="UserControlTest.PathControl"
             <!-- ...-->
             
             <!--diesen Namespace einfügen-->
             xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity" 
             <!-- ...-->            
    
    <StackPanel Orientation="Horizontal">
        <ComboBox x:Name="combo" Margin="10" MinWidth="60" VerticalAlignment="Center" ItemsSource="{Binding AvailableNumbers}" SelectedIndex="{Binding TheIndex}" SelectedItem="{Binding TheValue, Mode=TwoWay}">
            <i:Interaction.Triggers>
                <i:EventTrigger EventName="SelectionChanged">
                    <i:InvokeCommandAction Command="{Binding SelectionChangedCommand}"/>
                </i:EventTrigger>                
            </i:Interaction.Triggers>
        </ComboBox>
        <TextBox Margin="10" MinWidth="120" Text="{Binding TheText, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
    </StackPanel>
    
    <!-- -->
</UserControl>


Im ViewModel des UserControls wird dann einfach das SelectionChangedCommand angemeldet - fertig. Danke für das Zauberwort, KroaX - tatsächlich fehlt mir manchmal das Vokabular 🤔

Aber:

Wenn der Benutzer einen Wert in der ComboBox auswählt, wird durch das Binding die Eigenschaft TheValue im ViewModel geändert. Wenn du dort im Setter auf die Änderung reagierst, brauchst du kein Event, kein Command und kein Package.

Ich frage mich ja, warum ich da nicht gleich draufgekommen bin! Ich überlege, ob das nicht sogar sauberer ist; denn tatsächlich brauche ich ja nur den geänderten Wert! Die View selbst interssiert sich in meinem Fall gar nicht mehr dafür, was mit dem Wert passiert; so vom Gefühl her würde ich sagen, dass man vlt. auf das SelectionChanged-Event nur dann mit einem EventToCommand reagieren sollte, wenn dadurch eine Relevanz für die View gegeben ist (veränderte Darstellungsoptionen, etc). Da ich hier aber aus dem Bauch raus argumentiere, kann und will ich dafür nicht die Hand ins Feuer legen. Es würde mich aber schon interessieren, wie sich das verhält, oder ob es schlicht Wurscht ist.

Nochmals Danke, macht's gut,
Vorph

301 Beiträge seit 2009
vor 4 Jahren

Ich frage mich ja, warum ich da nicht gleich draufgekommen bin! Ich überlege, ob das nicht sogar sauberer ist; denn tatsächlich brauche ich ja nur den geänderten Wert! Die View selbst interssiert sich in meinem Fall gar nicht mehr dafür, was mit dem Wert passiert; so vom Gefühl her würde ich sagen, dass man vlt. auf das SelectionChanged-Event nur dann mit einem EventToCommand reagieren sollte, wenn dadurch eine Relevanz für die View gegeben ist (veränderte Darstellungsoptionen, etc). Da ich hier aber aus dem Bauch raus argumentiere, kann und will ich dafür nicht die Hand ins Feuer legen. Es würde mich aber schon interessieren, wie sich das verhält, oder ob es schlicht Wurscht ist.

Nochmals Danke, macht's gut,
Vorph

Am saubersten ist das was am ehesten deinem Usecase entspricht. Wenn du eine Aktion nur auslösen möchtest wenn der Benutzer über die UI eine Änderung an der Combobox vollzieht wäre es möglicherweise sauberer es über ein EventToCommand zu lösen.

Bedenke nämlich: Eine Änderung am Property innerhalb deines ViewModels würde dieselbe Bedeutung haben wie die Benutzerinteraktion aus der UI. Und manchmal möchte man dazwischen unterscheiden können.