WPF MVVM - Requests in Business Model

  • WPF

Es gibt 5 Antworten in diesem Thema. Der letzte Beitrag () ist von Majomi.

    WPF MVVM - Requests in Business Model

    Hallo und einen schicken Abend an alle,

    als Vorabinformation - ich nutze C#, WPF, Prism 6 und die Mahapps....

    Im MVVM - Pattern wird ja zwischen der View, dem Viewmodel und der Businesslogic (Model) getrennt.

    Ich habe das insofern jetzt gelöst, dass In meiner View alle Bindings zu Propertys direkt mit meinem Model agieren. Im Viewmodel wird lediglich Viewrelevante Belange bearbeitet - wie bspw. Buttons etc. Das Model soll ja komplett unabhängig von View und Viewmodel sein. Sprich, das Model kennt weder die View, das Viewmodel noch die Art und Weise der Darstellung der Daten.

    Um das mal herunterzubrechen (so verstehe ich das) ist ja das Model für das Verarbeiten (Laden und Speichern) der Daten verantwortlich.

    Ich habe bspw. ein Model mit einer Liste von Objekten, welche gespeichert werden sollen - also, jedes Objekt aus der Liste soll gespeichert werden (foreach). Ich habe einen Punkt, an welchem ich eine Nachfrage habe, ob ein vorhandener Eintrag in der Datenbank überschrieben werden soll oder nicht.

    Nun kam ich auf den Gedanken ich löse ein Event aus, welches im Viewmodel empfangen wird und entsprechend ein Dialog ausgelöst.

    Wichtig ist allerdings, dass dieses asynchron mit await aufgerufen werden kann und mir den Rückgabecode der Nachfrage liefert, damit ich diese dann im Model verarbeiten kann.

    Ist das ein richtiger Gedanke, wenn ja, wie erstelle ich solch ein Event oder wie löst man solch etwas besser?

    Ich bedanke mich schon einmal im Voraus...
    Hallo

    Da du gefragt hast wie sowas auch anders gelöst werden kann erlaube ich mir mich hier einzuklinken.

    Wenn eine Anwendung wenig Logik hat kann diese ins ViewModel intergriert werden wie z.b. dein Event. Wenn etwas mehr Logik von Nöten ist mache ich hier zwischen Model und ViewModel noch eine BusinessLogic dazwischen. Die Bezeichnung MVVM ist ja die Abkürzung für Model-View-ViewModel, impleziert jetzt aber nicht das es genau (nur) diese Layer gibt.

    Es wäre jetzt aber ohne eine Businesslogik nicht falsch wenn man beim z.b. Speichern einen Rückgabewert ins VM bekommt der sagt ob korrekt gespeichert wurde. Dann kannst du alles andere im VM erledigen.

    Leider hab ich gerade nur ein Handy zum Antworten aber ich hoffe ich konnte dir trotzdem helfen.

    Grüße
    Sascha
    If _work = worktype.hard Then Me.Drink(Coffee)
    Seht euch auch meine Tutorialreihe <WPF Lernen/> an oder abonniert meinen YouTube Kanal.

    ## Bitte markiere einen Thread als "Erledigt" wenn deine Frage beantwortet wurde. ##

    So, nun komme ich endlich mal dazu zu antworten.

    Ich will halt Businesslogik-Dinge aus dem VM raus haben und dort nur Steuerungsrelevante Sachen drin haben - hat den Vorteil, ich bin dann Oberflächenunabhängiger und es ist wiederverwendbar.

    Bei der Speicherung nur eines Objekts hätte ich es mir bestimmt auch einfach gemacht, das Speichern ins VM integriert. Da aber mehrere Objekte betroffen sein könnten kann das auch gleich im Model verbleiben.

    So, nun habe ich aber eben das Problem, dass bei der Speicherung eine Nachfrage entstehen kann. Vielleicht gehe ich das ganze Problem auch falsch an - unter WinForms - oder unter VBA bleibt eben fast alles in der View. Obwohl ich meine Sachen auch da getrennt habe...

    Nachdem ich aber am Sonntag die Anfrage hier geschrieben habe - und kurz bevor ich den Laptop zugeklappt habe, bin ich noch auf eine Lösung gestossen um ein Event auszulösen und mit async / await die Beantwortung zu erreichen. Die Programmierung muss aber dann eben so erfolgen, dass, wenn das Event nicht abonniert wird eine Exception ausgelöst wird oder eine Standartauswahl getroffen wird.

    Die Exception erinnert bei der späteren Neuverwendung daran, dass das Event abonniert werden muss. Ich weiß nicht, ob es so etwas wie eine abstract - Lösung für das Abonnieren gibt (also, genau wie ich gezwungen werde bei abstrakten Klassen gewisse Funktionen zu implementieren, dass ich gezwungen werde gewisse Events zu abonnieren).

    Ich möchte jetzt mal die Lösung vorstellen. Gelesen habe ich aber, dass solcherlei Events ungünstig sind, weil es Speicherleaks geben kann und bei gleichzeitiger, mehrmaliger Verwendung es auch zu Problemen kommen kann. Soweit bin ich aber noch nicht mit testen gekommen, da ich bisher keine weitere Zeit mehr hatte. Im ersten Moment klappt es aber.

    Die Lösung habe ich bei stackoverflow.com/a/35280607

    Es gibt einen Eventhandler:

    C#-Quellcode

    1. using System;
    2. using System.Linq;
    3. using System.Threading.Tasks;
    4. public delegate Task AsyncEventHandler<TEventArgs>(
    5. object sender,
    6. TEventArgs e)
    7. where TEventArgs : EventArgs;
    8. public static class AsyncEventHandlerExtensions
    9. {
    10. public static Task[] InvokeAll<TEventArgs>(
    11. this AsyncEventHandler<TEventArgs> handler,
    12. object sender,
    13. TEventArgs e)
    14. where TEventArgs : EventArgs
    15. => (
    16. from AsyncEventHandler<TEventArgs> h in handler.GetInvocationList()
    17. select h(sender, e)).ToArray();
    18. // Convenience
    19. public static Task WhenInvokeAll<TEventArgs>(
    20. this AsyncEventHandler<TEventArgs> handler,
    21. object sender,
    22. TEventArgs e)
    23. where TEventArgs : EventArgs
    24. => Task.WhenAll(handler.InvokeAll(sender, e));
    25. }


    und dieser kann dann ganz einfach in der jeweiligen Klasse

    mit einem Event verbastelt werden:

    C#-Quellcode

    1. public event AsyncEventHandler<SaveRequestEventArgs> SaveRequest;
    2. public async Task<MessageDialogResult> OnSaveRequest(string title, string message, MetroDialogSettings dialogSettings, MessageDialogStyle dialogStyle = MessageDialogStyle.AffirmativeAndNegative)
    3. {
    4. var args = new SaveRequestEventArgs(title, message, dialogSettings, dialogStyle);
    5. await SaveRequest?.WhenInvokeAll(this, args);
    6. return args.Result;
    7. }


    und so dann abonniert:

    C#-Quellcode

    1. Objekt.ObjektListen.SelectedItem.SaveRequest += async (sender, e) =>
    2. {
    3. e.Result = await GetMainWindow.MainWindow.ShowMessageAsync(e.Title,e.DialogSettings.AffirmativeButtonText +" " + e.DialogSettings.NegativeButtonText,MessageDialogStyle.AffirmativeAndNegative,e.DialogSettings);
    4. };


    <Edit:> zu erwähnen ist dabei noch, dass bei dieser Lösung die Events genau wie "normale" Events verwendet werden können und jede beliebigen Eventargs übergeben werden können...
    </Edit>
    Jetzt muss ich gestehen ist bei dieser Lösung noch nicht alles standart, sondern bedarf eigentlich noch einem Wrapper, so dass ich nicht auf DialogSettings und DialogStyle zurückgreifen muss und die verwendete Oberfläche bestimmt, wie die Abfrage erfolgen soll. Aber wie gesagt, soweit bin ich noch nicht gekommen...

    Grundsätzlich funktioniert es im ersten Versuch schon mal so wie ich es möchte.

    Ich vermute aber, dass ich aber noch in den alten Vorgehensweisen verwurzelt bin. Löse mich nur so nach und nach davon und meist dann wieder am umschreiben smile

    sronny schrieb:

    So, nun habe ich aber eben das Problem, dass bei der Speicherung eine Nachfrage entstehen kann.
    Dann gehört das ins VM - nicht inne BL.

    Inne BL ein Event anlegen, feuern, und erzwingen wollen, dass das VM das Event auch abonniert hat... bisserl von hinten durch Brust ins Auge, odr?
    Weil der Speicher-Vorgang wird doch vom VM ausgelöst.

    Also schon möglich, dasses technisch nicht anders (oder nicht mit vertretbarem Aufwand) lösbar ist, aber eher straight forward wäre wohl ein Design, wo man die BL erstmal nachfragen kann, ob und welche Nachfragen es gibt, für einen bestimmten Speicher-Auftrag.
    Und das dann abfrühstücken, bevor der eigliche Speicher-Auftrag abgefahren wird.