Tach!
Es ist mir klar, dass in ProductViewModel:
public ICommand GetProductCommand { get { if (_getProductCommand == null) { _getProductCommand = new RelayCommand(param => GetProduct(), param => ProductId > 0); } return _getProductCommand; } }
die Methode GetProduct() und der boolsche Wert ProductId > 0 als Parameter an der RelayCommand-Konstruktor übergeben und in die Variablen _execute und _canExecute gespeicher werden.
Nein, das ist nicht richtig. Da werden Funktionen übergeben.
param => GetProduct() ist die Kurzform von
noname(object param) {
GetProduct();
}
Und beim zweiten Parameter ähnlich. Da wird auch nicht nur ein Ergebnis übergeben sondern eine Funktion, die das Ergebnis berechnet.
Dabei seh ich grad, dass es zumindest bei _execute sinnlos ist, den Typ Action<object> zu verwenden, denn das Object wird nie verwendet. Also kann man auch gleich Action nehmen. Bei Predicate<T> ist hier ebenso der Parameter unverwendet, aber das gibt nicht ohne <T>.
An dieser Stelle, so scheint mir, fehlt dir grundlegendes Wissen zu C#. Siehe beispielsweise Anonyme Funktionen.
Werden diese in Execute und CanExecute:
public bool CanExecute(object parameters) { return _canExecute == null ? true : _canExecute(parameters); } public void Execute(object parameters) { _execute(parameters); }
ausgeführt? _canExecute ist kann doch nur true oder false sein.
Nicht "kann sein" sondern "kann zurückgeben". Es ist ein Verweis auf eine Funktion.
Welche Rolle spielt an dieser Stelle der Parameter "parameters"? Ist das eine Übergabeparameter an die GetProduct()? Ich verstehe das nicht ganz.
Ah, hab ich ja grad schon so beantwortet: keine Rolle, kann (teilweise) weg.
Ich kann sofort mit "MVVM Light" oder "Prism" anfangen. Ich gehe davon aber aus, dass meine Probleme da erheblich größer werden, wenn ich die Zusammenhänge nicht versetehe, die z.B. in diesem Beispiel zum Tragen kommen. Daher möchte ich zunächst mal dieses Beispiel mehr oder weniger verstehen, bevor ich mit etwas anderem anfange.
Ok, aber der Artikel hilft dir nur bedingt. Der Autor gesteht ja selbst, dass er auf dem Gebiet Anfänger ist. Und da macht er einen Haufen Dinge umständlicher als notwendig und das Wichtigste lässt er so gut wie unerwähnt: die Verbindung zum XAML. Das macht man mit diesen Binding-Direktiven.
Kennst du eventuell ein MVVM Beispiel, in dem auch die Handhabung mit den Menüs (in der Menüleiste meine ich) behandelt werden?
Menü-Elemente klickt man an und dann wird ein Kommando ausgeführt. Dabei kommt nur ein Teil vom MVVM zum Tragen.
<Window Vorspann weggelassen>
<DockPanel>
<Menu DockPanel.Dock="Top">
<MenuItem Header="_Edit">
<MenuItem Header="_Cut" Command="{Binding Cut}" />
<MenuItem Header="C_opy" Command="{Binding Copy}" />
<MenuItem Header="_Paste" Command="{Binding Paste}" />
</MenuItem>
</Menu>
</DockPanel>
</Window>
Das ist mal eine ganz simple View mit Menu. Die braucht jetzt noch das ViewModel an den DataContext übergeben. Der MVVM-Light-Weg wäre deklarativ und mit Dependency Injection. Man kann das aber auch wie im Artikel unter "Starting the Sample" beschrieben im Code zuweisen.
Cut, Copy und Paste sind die gleichnamigen Eigenschaften aus dem nachfolgenden ViewModel, das - nur für das Menü - so aussieht:
public YourViewModel : ViewModelBase {
public RelayCommand Cut { get; private set; }
public RelayCommand Copy { get; private set; }
public RelayCommand Paste { get; private set; }
public ViewModel() {
Cut = new RelayCommand(() => cut());
Copy = new RelayCommand(copy);
Paste = new RelayCommand(delegate { paste(); });
}
private void cut() { }
private void copy() { }
private void paste() { }
}
Ich hab mal drei verschiedene Varianten genommen, wie man die Callback-Funktionen übergeben kann. Und ich hab das CanExecute weggelassen. In der Praxis braucht man das aber doch, denn Cut und Copy darf nur ausgeführt werden, wenn man was markiert hat und Paste nur, wenn was in der Zwischenablage ist.
Die Cut-Variante zeigt eine anonyme Funktion, die keinen Parameter übergeben bekommt. Die Copy-Variante übergibt einfach eine Funktionsreferenz und Paste nimmt einen herkömmlichen Delegate. Seit es anonyme Funktionen gibt, sind aber die Delegates am Aussterben. In dem Fall wäre die Copy-Variante vorzuziehen, weil die Vorgänge vermutlich umfangreicher als ein Einzeiler werden. Ansonsten nehme ich eher die Cut-Variante.
Neben den Kommandos könnte man in dem Beispiel auch noch die Header-Eigenschaften ans ViewModel binden, um programmatisch den Text ändern zu können. Üblicherweise wird man das in dem Fall wohl kaum machen, sondern Ressourcen (resx-Dateien) anbinden, in denen die jeweilige Lokalisierung enthalten ist. Sei's drum, hier das erweiterte Beispiel
<Window Vorspann weggelassen>
<DockPanel>
<Menu DockPanel.Dock="Top">
<MenuItem Header="{Binding EditHeader}">
<MenuItem Header="{Binding CutHeader}" Command="{Binding Cut}" />
<MenuItem Header="{Binding CopyHeader}" Command="{Binding Copy}" />
<MenuItem Header="{Binding PasteHeader}" Command="{Binding Paste}" />
</MenuItem>
</Menu>
</DockPanel>
</Window>
public YourViewModel : ViewModelBase {
private string editHeader;
private string cutHeader;
private string copyHeader;
private string pasteHeader;
public RelayCommand Cut { get; private set; }
public RelayCommand Copy { get; private set; }
public RelayCommand Paste { get; private set; }
public string EditHeader {
get { return editHeader; }
set { Set(ref editHeader, value); }
}
// Cut-, Copy- und PasteHeader analog zu EditHeader
public ViewModel() {
Cut = new RelayCommand(() => cut());
Copy = new RelayCommand(copy);
Paste = new RelayCommand(delegate { paste(); });
EditHeader = "_Edit";
CutHeader = "_Cut";
CopyHeader = "C_opy";
PasteHeader = "_Paste";
}
private void cut() { }
private void copy() { }
private void paste() { }
}
Das Beispiel zeigt die MVVM-Light-Variante mit dem Set(). Ansonsten müsstest du das Interface INotifyPropertyChanged einbinden. Beispiele dazu (außer denen im Artikel) sollten sich genügend finden lassen.
dedlfix.