Laden...

WPF, MVVM und CommandBindings

Erstellt von Hunv vor 9 Jahren Letzter Beitrag vor 9 Jahren 6.149 Views
Hunv Themenstarter:in
193 Beiträge seit 2005
vor 9 Jahren
WPF, MVVM und CommandBindings

Hi,

ich möchte gerne ein Programm schreiben, welches MVVM-conform ist. Dazu gehört auch, soviel ich bisher verstanden habe, nicht die "klassischen" z.B. Button_Click-Events eines Controls zu benutzen, sondern dies über CommandBindings umzusetzen.
Dazu habe ich bisher folgendes gemacht:

XAML:

<RibbonWindow 
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:MvvmTest.GUI_v2.ViewModels"
        xmlns:cmds="clr-namespace:MvvmTest.GUI_v2.ViewModels.Commands"
        x:Class="MvvmTest.GUI.MainWindow"
        Title="MvvmTest"      
        Height="600"
        Width="800"
        WindowState="Normal"
        WindowStartupLocation="CenterScreen"    
        >
    <RibbonWindow.DataContext>
        <local:ViewModelMain />
    </RibbonWindow.DataContext>

    <RibbonWindow.CommandBindings>
	<!-- Geht nicht -->
        <CommandBinding Command="cmds:Login.CmdLogin" CanExecute="Login_CanExecute" Executed="Login_Executed" />
	<!-- Geht auch nicht -->
        <CommandBinding Command="cmds:Login.CmdLogin" CanExecute="cmds:Login.Login_CanExecute" Executed="cmds:Login.Login_Executed" />    
</RibbonWindow.CommandBindings>

    <Grid>
        <Button Content="Login" Command="cmds:Login.CmdLogin" HorizontalAlignment="Left" VerticalAlignment="Top" IsDefault="True" Width="200"  Margin="0,0,0,5"/>
    </Grid>
</RibbonWindow>

Mein ViewModelMain (NameSpace: MvvmTest.GUI.ViewModels) ist zur Zeit noch leer.
Für die Commands möchte ich gerne im NameSpace MvvmTest.GUI.ViewModels.Commands jeweils eine Klasse erstellen, in denen dann die gewünschten Aktionen durchgeführt werden sollen.

Eine so eine Klasse sieht z.B. so aus:


namespace MvvmTest.GUI_v2.ViewModels.Commands
{
    public class Login
    {
        public static RoutedUICommand CmdLogin = new RoutedUICommand("Login", "Login", typeof(Login));

        public void Login_CanExecute(object sender, CanExecuteRoutedEventArgs e)
        {
            e.CanExecute = true;
            //e.Handled = true;
        }

        public void Login_Executed(object sender, ExecutedRoutedEventArgs e)
        {
            Console.Beep();
        }
    }
}

Mein Problem ist nun:
wie bekomme ich dem CommandBinding Beigebracht, dass er aus der Klasse Login das CanExecute und Executed aufrufen soll und nicht aus der CodeBehind-Datei? Im XAML Code oben habe ich bereits zwei Möglichkeiten ausprobiert, welche mir logisch erschienen, aber beide nicht funktionieren.

Bei der ersten Variante gibt es den Fehler> Fehlermeldung:

'MvvmTest.GUI.MainWindow' does not contain a definition for 'Login_Executed' and no extension method 'Login_Executed' accepting a first argument of type 'MvvmTest.GUI.MainWindow' could be found (are you missing a using directive or an assembly reference?)

Hier sucht er in der Code-Behind-Datei nach den Methoden.

Bei der zweiten Variante gibt es den Fehler:> Fehlermeldung:

CanExecute="cmds:Login.Login_CanExecute" is not valid. 'cmds:Login.Login_CanExecute' is not a valid event handler method name. Only instance methods on the generated or code-behind class are valid.

Beides kompiliert nicht.

Des weiteren hatte ich auch schon versucht die Login-Klasse sowie die (Can)Execute-Methoden static zu machen, aber auch das hat in der Verzweiflung nichts am Fehler und den Meldungen geändert.

Ich bin ein wenig ratlos und finde auch über den Freund mit den 6 Buchstaben keine Lösung, die mir im Ansatz weiterhilft. In die CodeBehind-Datei möchte ich eigentlich nichts schreiben.

Visit me @ www.beremote.net

2.207 Beiträge seit 2011
vor 9 Jahren

Hallo Hunv,

ein Command ist eine eigene Klasse. Somit mach eine Klasse. Leite diese von ICommand ab und du bekommst deine Methoden, die du auch schon kennst.


public class MyCommandImpl : ICommand, IMyCommand
{
	public bool CanExecute(object parameter)
	{
		return true;
	}

	public void Execute(object parameter)
	{
		//Do Something
	}

	public event EventHandler CanExecuteChanged;
}

Dann bietest du das einfach in deinem VM an:


public class MainViewModel
{
	public IMyCommand MyCommand { get; set; }

	public MainViewModel()
	{
		MyCommand = new MyCommandImpl();
	}
}

Wenn der Datacontext gesetzt ist, kannst dus einfach binden.

<Button Command="{Binding MyCommand}" Height="20"></Button>

Gruss

Coffeebean

849 Beiträge seit 2006
vor 9 Jahren

ein Command ist eine eigene Klasse

Ich für meinen Teil bin es leid für jeden Button Klick eine neue Klasse zu erstellen, zumal wenn es absehbar ist, das ich diesen Code genau an einer Stelle benötige. Mich hätte das damals fast wieder zum Code Behind gebracht. Bis ich dann über dutzende Implementierungen von RelayCommand oder DelegateCommand gestolpert bin. Das macht einem das Leben mit Wpf doch sehr viel angenehmer.

T
314 Beiträge seit 2013
vor 9 Jahren

Man sollte dies einfach entsprechend dem jeweiligen Anwendungsfall nutzen. Habe ich ein Command was immer wieder genutzt wird bietet es sich an dafür eine entsprechende Klasse zu erstellen.

Für (einfache) spezifische Fälle sehe ich dies wie unconnected.

Hunv Themenstarter:in
193 Beiträge seit 2005
vor 9 Jahren

Hi Coffeebean,

vielen Dank für deine Antwort. Das hat mir sehr weitergeholfen!
Ob ich wirklich für jeden Click eine eigene Klasse mache oder ob ich das in der Praxis dann ggf. doch anders mache, wird sich zeigen. Erstmal aber möchte ich es mit eigenen Klassen machen um zu wissen wie es dann in der Theorie und Praxis mal alles funktionieren würde.

Visit me @ www.beremote.net