Entwickler-Ecke
Sonstiges (.NET) - Wie komme ich an die Instanz eines ViewModels?
Schafschaf - Do 08.10.15 11:20
Titel: Wie komme ich an die Instanz eines ViewModels?
Hallo,
ich weiß mal wieder nicht weiter.
Ich habe ein Viewmodel, in das ich ein paar Datenbindungen habe.
Jetzt möchte ich ein paar Commands im Viewmodel feuern und dabei auf die gebundenen Objekte im ViewModel.
Hier mal ein Ausschnitt:
C#-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22:
| public class MenschenVM : INotifyPropertyChanged { public ObservableCollection<Mensch> Menschen { get; set; } public Add add { get; set; } }
public class Add : ICommand { public event EventHandler CanExecuteChanged;
public bool CanExecute(object parameter) { return true; }
public void Execute(object parameter) { } } |
Die Daten für den Mensch werden in einem Formular eingegeben und sollen dann eben mit dem Button ein Menschobjekt der ObservableCollection hinzugefügt werden.
Nur leider komme ich aus der "Add" Klasse nicht auf die ObservableCollection im Viewmodel.
Jetz könnte ich in MenschenVM noch die ICommand implentieren, aber nur einmal. Mehr Commands gehen dann nicht.
Hat jemand dazu eine Idee?
Freue mich über Antworten :)
LG Schafschaf
Palladin007 - Do 08.10.15 11:50
Gib dem Command doch im Konstruktor die Daten mit, die es braucht?
Ist ja eine Klasse wie jede Andere auch :D
Oder, was auch gerne gemacht wird, streng genommen aber nicht ganz mit MVVM vereinbar ist, ist ein RelayCommand.
Also eine Klasse, die ICommand implementiert und für die Methoden eine Action<object> und eine Func<object, bool> im Konstruktor entgegen nimmt.
So kannst Du das im ViewModel "implementieren" und hast dann dort auch alle Daten, die es im ViewModel gibt - public und private
PS:
Hier gibt es eine Implementierung:
http://docs.telerik.com/data-access/quick-start-scenarios/wpf/quickstart-wpf-viewmodelbase-and-relaycommand
Meine Eigene ist aber etwas komplexer, die nutze ich dann aber auch für jede weitere Command-Klasse:
C#-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56:
| public class RelayCommand<TParameter> : ICommand { private readonly Action<TParameter> _execute; private readonly Func<TParameter, bool> _canExecute;
public event EventHandler CanExecuteChanged { add { CommandManager.RequerySuggested += value; } remove { CommandManager.RequerySuggested -= value; } }
public RelayCommand(Action<TParameter> execute, Func<TParameter, bool> canExecute = null) { if (execute == null) throw new ArgumentNullException(nameof(execute));
_execute = execute; _canExecute = canExecute ?? new Func<TParameter, bool>(_ => true); } protected RelayCommand() { }
public virtual void Execute(TParameter parameter) { _execute(parameter); } public virtual bool CanExecute(TParameter parameter) { return _canExecute(parameter); }
bool ICommand.CanExecute(object parameter) { return CanExecute((TParameter)parameter); } void ICommand.Execute(object parameter) { Execute((TParameter)parameter); } }
public class RelayCommand : RelayCommand<object> { public RelayCommand(Action execute, Func<bool> canExecute = null) : this(_ => execute(), canExecute == null ? null : new Func<object, bool>(_ => canExecute())) { } public RelayCommand(Action<object> execute, Func<object, bool> canExecute = null) : base(execute, canExecute) { } protected RelayCommand() { } } |
Die Nutzung sieht dann so aus:
C#-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11:
| public class MyCommand : RelayCommand<int> { public override bool CanExecute(int parameter) { } public override void Execute(int parameter) { } } |
Du darfst dann natürlich nicht
base.CanExecute und
base.Execute aufrufen, das würde eine
NullReferenceException geben.
Oder die nicht ganz MVVM-Konforme Methode:
C#-Quelltext
1:
| public ICommand MyCommand { get; } |
C#-Quelltext
1:
| MyCommand = new RelayCommand<int>(() => , () => ); |
Ralf Jansen - Do 08.10.15 12:00
Macht man das in MVVM wirklich so oder ähnlich? Für mich fühlt sich das zumindest falsch an da man vermutlich auch einfach MenschenVM.Menschen.Add aufrufen könnte und ich erwarten würde das ein Add Logik auch dort zieht.
Palladin007 - Do 08.10.15 12:10
Soweit ich weiß, sollten Commands alle eine eigene Klasse sein, so lässt es sich besser strukturieren.
In solchen Fällen ist das tatsächlich ein bisschen Overkill, eine Klasse für eine Zeile Code zu schreiben, daher gibt es ja das Konzept mit dem RelayCommand.
Für das Hinzufügen in eine Liste gibt es aber auch andere Möglichkeiten.
Das DataGrid unterstützt glaube ich auch das Hinzufügen und entfernen von Elementen aus der Datenquelle.
Wie das im Detail dann aber aus sieht, weiß ich nicht.
Schafschaf - Fr 09.10.15 14:38
Ich hab es jetzt ganz anders gemacht xD
Ich habe in der Klasse Add noch ein Event definiert, dass in der Excecute Methode gefired wird und in den EventArgs ein Mensch Objekt mitgibt.
Ist diese Lösung MVVM Konform?
Palladin007 - Fr 09.10.15 15:36
Keine Ahnung :D
Ich finde es aber ziemlich unschön, das ist mehr Aufwand als nötig und auch nur um drei Ecken.
Warum verlangst Du im Konstruktor vom Command nicht einfach das ViewModel oder die ObservableCollection als Parameter?
Oder eben das RelayCommand, in dem Umfang finde ich das nicht unbedingt abwegig.
Schafschaf - Mo 12.10.15 14:14
Wenn ich mein Viewmodel oder meine ObservableCollection als Parameter im Konstruktor mitgebe und dann da was ändere passiert in der Collection in der Viewmodel Instanz nichts.
Ist ja eine neue Instanz wenn ichs als Parameter mitgebe und als ref lässt sich "this" und die Collection nicht parametrisieren :(
Das mit dem Event find ich eigentlich ganz praktisch, ich muss zwar für jedes Command ein Event definieren und umgekehrt aber dafür bringe ich alles in einer ViewModel Instanz unter.
Korrigier mich bitte wenn ich falsch liege.
Palladin007 - Mo 12.10.15 19:53
Die ObservableCollection ist wie jeder class-Typ ein Referenz-Typ, das Ding wird immer als Referenz mit gegeben, wenn Du die Collection nicht irgendwo mit einer neuen Instanz überschreibst.
Ich tippe mal darauf, Du übergibst nicht die Collection, um die es dir geht, sondern Du erzuegst mit new eine neue Instanz.
Oder Du möchtest die Collection irgendwo leeren und überschreibst sie dabei mit einer neuen, leeren Instanz. Besser wäre die Clear-Methode.
Das System mit Events ist durchaus sinnvoll, aber auch nur, wenn es etwas gibt über das Informiert werden sollte.
In dem Fall ist es ein einfaches Command, das nur eine Collection bearbeiten soll.
Sinnvoll wäre es z.B., wenn Du einen komplexeren Ablauf hast, der in verschiedenen Abschnitten ausgeführt wird, während der Nutzer über jeden Abschnitt informiert werden soll.
Oder der Ablauf läuft asynchron im Hintergrund und die Beendigung muss dem Nutzer mitgeteilt werden.
Entwickler-Ecke.de based on phpBB
Copyright 2002 - 2011 by Tino Teuber, Copyright 2011 - 2025 by Christian Stelzmann Alle Rechte vorbehalten.
Alle Beiträge stammen von dritten Personen und dürfen geltendes Recht nicht verletzen.
Entwickler-Ecke und die zugehörigen Webseiten distanzieren sich ausdrücklich von Fremdinhalten jeglicher Art!