Model-Widok-prezenter w WinForms

Próbuję zaimplementować metodę MVP po raz pierwszy, używając WinForms.

Staram się zrozumieć funkcję każdej warstwy.

W moim programie Mam przycisk GUI, który po kliknięciu otwiera okno openfiledialog.

Więc używając MVP, GUI obsługuje przycisk click event, a następnie wywołuje presenter.openfile ();

Wewnątrz prezentera.openfile (), gdyby wtedy delegować otwarcie tego pliku na warstwę modelu, lub ponieważ nie ma danych ani logiki do proces, czy powinien po prostu działać na żądanie i otworzyć okno openfiledialog?

Aktualizacja: postanowiłem zaoferować nagrodę, ponieważ czuję, że potrzebuję dalszej pomocy w tej sprawie, a najlepiej dostosowanej do moich konkretnych punktów poniżej, aby mieć kontekst.

Dobra, po przeczytaniu MVP postanowiłem zaimplementować Widok pasywny. Efektywnie będę miał kilka kontroli na Winform, które będą obsługiwane przez prezentera, a następnie zadania przekazane modelowi (modelom). Moje konkretne punkty są poniżej:

  1. Gdy winform ładuje się, musi uzyskać widok drzewa. Czy mam rację myśląc, że Widok powinien zatem wywoływać metodę taką jak: presenter.gettree(), to z kolei będzie delegować do modelu, który uzyska dane do widoku drzewa, utworzy go i skonfiguruje, zwróci go do prezentera, który z kolei przejdzie do widoku, który następnie po prostu przypisze go do, powiedzmy, panelu?

  2. Czy byłoby to takie samo dla jakichkolwiek danych kontrola na Winform, ponieważ mam również datagridview?

  3. Moja aplikacja, ma wiele klas modeli z tym samym montażem. Obsługuje również architekturę wtyczek z wtyczkami, które należy załadować przy starcie. Czy Widok wywołałby po prostu metodę prezentera, która z kolei wywołałaby metodę, która ładuje wtyczki i wyświetla informacje w widoku? Która warstwa będzie następnie kontrolować odniesienia do wtyczek. Czy Widok zawiera odniesienia do nich lub prezenter?

  4. Czy mam rację myśląc, że Widok powinien obsługiwać każdą rzecz dotyczącą prezentacji, od koloru węzła treeview, po Rozmiar datagrid itp.?

Myślę, że są to moje główne obawy i jeśli rozumiem, jak przepływ powinien być dla nich myślę, że będzie w porządku.

Author: abatishchev, 2011-01-25

3 answers

To jest moje skromne podejście do MVP i Twoich konkretnych problemów.

Po pierwsze, wszystko, z czym użytkownik może wchodzić w interakcję lub po prostu być pokazane, jest widokiem. Prawa, zachowanie i cechy takiego widoku są opisane przez interfejs . Ten interfejs może być zaimplementowany przy użyciu interfejsu WinForms, interfejsu konsoli, interfejsu webowego lub nawet w ogóle (zwykle podczas testowania prezentera) - konkretna implementacja nie ma znaczenia, dopóki przestrzega praw swojego widoku interfejs.

Drugi , widok jest zawsze kontrolowany przez prezenter. Prawa, zachowanie i cechy takiego prezentera są również opisane przez interfejs . Interfejs ten nie jest zainteresowany konkretną implementacją widoku, o ile przestrzega praw swojego interfejsu widoku.

Po Trzecie , ponieważ prezenter kontroluje swój widok, aby zminimalizować zależności, naprawdę nie ma korzyści z posiadania widoku wiedząc cokolwiek o jego prezenter. Istnieje uzgodniona umowa między prezenterem a widokiem i jest to określone przez interfejs widoku.

Implikacje trzeciej to:

  • prezenter nie ma żadnych metod, które Widok może wywołać, ale Widok ma zdarzenia, które prezenter może subskrybować.
  • prezenter zna swój pogląd. Wolę to osiągnąć wtryskiem konstruktora na betonowy prezenter.
  • widok nie ma pojęcia, co to jest prezenter kontrolowanie go; po prostu nigdy nie zostanie dostarczony żaden prezenter.

Dla Twojego problemu powyższy może wyglądać tak w nieco uproszczonym kodzie:

interface IConfigurationView
{
    event EventHandler SelectConfigurationFile;

    void SetConfigurationFile(string fullPath);
    void Show();
}

class ConfigurationView : IConfigurationView
{
    Form form;
    Button selectConfigurationFileButton;
    Label fullPathLabel;

    public event EventHandler SelectConfigurationFile;

    public ConfigurationView()
    {
        // UI initialization.

        this.selectConfigurationFileButton.Click += delegate
        {
            var Handler = this.SelectConfigurationFile;

            if (Handler != null)
            {
                Handler(this, EventArgs.Empty);
            }
        };
    }

    public void SetConfigurationFile(string fullPath)
    {
        this.fullPathLabel.Text = fullPath;
    }

    public void Show()
    {
        this.form.ShowDialog();        
    }
}

interface IConfigurationPresenter
{
    void ShowView();
}

class ConfigurationPresenter : IConfigurationPresenter
{
    Configuration configuration = new Configuration();
    IConfigurationView view;

    public ConfigurationPresenter(IConfigurationView view)
    {
        this.view = view;            
        this.view.SelectConfigurationFile += delegate
        {
            // The ISelectFilePresenter and ISelectFileView behaviors
            // are implicit here, but in a WinForms case, a call to
            // OpenFileDialog wouldn't be too far fetched...
            var selectFilePresenter = Gimme.The<ISelectFilePresenter>();
            selectFilePresenter.ShowView();
            this.configuration.FullPath = selectFilePresenter.FullPath;
            this.view.SetConfigurationFile(this.configuration.FullPath);
        };
    }

    public void ShowView()
    {
        this.view.SetConfigurationFile(this.configuration.FullPath);
        this.view.Show();
    }
}

Oprócz powyższego, zwykle mam podstawowy interfejs IView, w którym przechowuję Show() i każdy widok właściciela lub tytuł widoku, z którego moje widoki zwykle korzystają.

Na twoje pytania:

1. gdy winform ładuje się, musi uzyskać widok drzewa. Czy mam rację myśląc, że view powinien zatem wywołać metodę taką jak: presenter.gettree(), to z kolei będzie delegować do modelu, który uzyska dane do widoku drzewa, utworzy go i skonfiguruje, zwróci go do prezentera, który z kolei przejdzie do widoku, który następnie po prostu przypisze go do, powiedzmy, panelu?

Zadzwoniłbym IConfigurationView.SetTreeData(...) z IConfigurationPresenter.ShowView(), tuż przed wywołaniem do IConfigurationView.Show()

2. czy to będzie to samo dla każdej kontroli danych na Winform, jak I masz datagridview?

Tak, zadzwoniłbym po to. Do widoku należy sformatowanie danych mu podanych. Prezenter po prostu przestrzega umowy widoku, że chce danych tabelarycznych.

3. Moja aplikacja, ma wiele klas modeli z tym samym montażem. Obsługuje również architekturę wtyczek z wtyczkami, które należy załadować przy starcie. Czy Widok wywoła po prostu metodę prezentera, która z kolei wywoła metodę, która ładuje wtyczki i wyświetlać informacje w widoku? Która warstwa będzie następnie kontrolować odniesienia do wtyczek. Czy Widok zawiera odniesienia do nich lub prezentera?

Jeśli wtyczki są powiązane z widokami, to widoki powinny o nich wiedzieć, ale nie prezenter. Jeśli chodzi o Dane i model, widok nie powinien mieć z nimi nic wspólnego.

4. Czy mam rację myśląc, że pogląd powinien zajmować się każdą sprawą o prezentacja, od koloru węzła treeview, do rozmiaru datagrid itp.?

Tak. Pomyśl o tym jak o prezenterze dostarczającym XML, który opisuje dane oraz o widoku, który pobiera dane i stosuje do nich arkusz stylów CSS. Mówiąc konkretnie, prezenter może wywołać IRoadMapView.SetRoadCondition(RoadCondition.Slippery), a następnie Widok renderuje drogę w kolorze czerwonym.

Co z danymi dla klikniętych węzłów?

5. Jeśli po kliknięciu na treenodes, należy przejść przez konkretny węzeł do prezentera, a następnie prezenter opracuje, jakich danych potrzebuje, a następnie zapyta model o te dane, zanim przedstawi je z powrotem do widoku?

Jeśli to możliwe, przekazałbym wszystkie dane potrzebne do przedstawienia drzewa w widoku w jednym ujęciu. Ale jeśli niektóre dane są zbyt duże, aby można je było przekazać Od początku lub jeśli mają charakter dynamiczny i wymagają "najnowszej migawki" z modelu( za pośrednictwem prezentera), to dodałbym coś w rodzaju event LoadNodeDetailsEventHandler LoadNodeDetails do interfejs widoku, aby prezenter mógł się do niego zapisać, pobiera szczegóły węzła w LoadNodeDetailsEventArgs.Node (być może poprzez jego ID) z modelu, aby Widok mógł zaktualizować wyświetlane szczegóły węzła, gdy powróci delegat obsługi zdarzenia. Należy pamiętać, że wzorce asynchroniczne mogą być potrzebne, jeśli pobieranie danych może być zbyt wolne, aby zapewnić dobre wrażenia użytkownika.

 114
Author: Johann Gerell,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-01-28 14:00:58

Prezenter, który zawiera całą logikę w widoku, powinien odpowiedzieć na kliknięcie przycisku, tak jak mówi @JochemKempe . W praktyce przycisk click event handler calls presenter.OpenFile(). Prezenter jest wtedy w stanie określić, co należy zrobić.

Jeśli zdecyduje, że użytkownik musi wybrać plik, to wywoła z powrotem Widok (poprzez interfejs widoku) i niech widok, który zawiera wszystkie szczegóły interfejsu, wyświetli OpenFileDialog. To bardzo ważne. rozróżnienie polegające na tym, że prezenter nie może wykonywać operacji związanych z używaną technologią interfejsu użytkownika.

Wybrany plik zostanie następnie zwrócony do prezentera, który kontynuuje swoją logikę. Może to dotyczyć dowolnego modelu lub usługi, która powinna obsługiwać przetwarzanie pliku.

Głównym powodem użycia wzorca MVP, imo jest oddzielenie technologii interfejsu użytkownika od logiki widoku. W ten sposób prezenter koordynuje całą logikę, podczas gdy widok jest oddzielony od logiki interfejsu użytkownika. To ma bardzo ładny efekt uboczny uczynienia prezentera w pełni testowalnym.

Update: ponieważ prezenter jest ucieleśnieniem logiki znalezionej w jednym konkretnym widoku , relacja widok-prezenter jest IMO relacją jeden do jednego. I dla wszystkich praktycznych celów, jedna instancja widoku (powiedzmy formularz) współdziała z jedną instancją prezentera, a jedna instancja prezentera współdziała tylko z jedną instancją widoku.

To powiedziawszy, w mojej implementacji MVP z WinForms prezenter zawsze wchodzi w interakcję z widokiem za pośrednictwem interfejsu reprezentującego możliwości interfejsu widoku. Nie ma ograniczeń co do tego, co view implementuje ten interfejs, dlatego różne "widżety" mogą zaimplementować ten sam interfejs widoku i ponownie użyć klasy presenter.

 10
Author: Peter Lillevold,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:10:43

Prezenter powinien działać na końcu żądania Pokaż okno openfiledialog zgodnie z Twoją sugestią. Ponieważ od modelki nie są wymagane żadne dane, prezenter może i powinien obsłużyć żądanie.

Załóżmy, że potrzebujesz danych, aby utworzyć niektóre encje w swoim modelu. Możesz albo przekazać koryto strumienia do warstwy dostępu, gdzie masz metodę tworzenia elementów ze strumienia, ale sugeruję, abyś zajął się parsowaniem pliku w swoim prezenterze i użył konstruktora lub metody Create na jednostkę w twoim modelu.

 2
Author: JochemKempe,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-01-25 14:31:52