Модель-Представление-Презентатор в WinForms - PullRequest
86 голосов
/ 25 января 2011

Я пытаюсь реализовать метод MVP впервые, используя WinForms.

Я пытаюсь понять функцию каждого слоя.

В моей программе у меня есть кнопка GUIчто при нажатии открывается окно openfiledialog.

Таким образом, используя MVP, GUI обрабатывает событие нажатия кнопки, а затем вызывает Presenter.openfile ();

Внутри Presenter.openfile (), если этозатем делегировать открытие этого файла на уровень модели, или, поскольку нет данных или логики для обработки, он должен просто воздействовать на запрос и открывать окно openfiledialog?

Обновление: Я решил предложить вознаграждение, так как считаю, что мне нужна дополнительная помощь по этому вопросу, и желательно с учетом моих конкретных моментов ниже, чтобы у меня был контекст.

Хорошо, после прочтения MVP, я решил реализовать пассивный просмотр.По сути, у меня будет куча элементов управления в Winform, которые будут обрабатываться докладчиком, а затем задачи, делегированные моделям.Мои конкретные пункты приведены ниже:

  1. Когда winform загружается, она должна получить древовидное представление.Правильно ли я считаю, что представление должно вызывать такой метод, как: Presenter.gettree (), это, в свою очередь, делегирует модели, которая получит данные для древовидного представления, создаст и настроит их, вернет ихPresenter, который, в свою очередь, перейдет к представлению, которое затем просто назначит его, скажем, панели?

  2. Это будет то же самое для любого элемента управления данными в Winform, как ятакже есть представление данных?

  3. Мое приложение содержит несколько классов моделей с одинаковой сборкой.Он также поддерживает архитектуру плагинов с плагинами, которые должны быть загружены при запуске.Будет ли представление просто вызывать метод презентатора, который в свою очередь вызовет метод, который загружает плагины и отображает информацию в представлении?Какой уровень будет контролировать ссылки на плагин.Будет ли представление содержать ссылки на них или на докладчика?

  4. Правильно ли я считаю, что представление должно обрабатывать все, что касается представления, от цвета узла древовидного представления до размера сетки данных и т. Д.?

Я думаю, что они являются моей главной заботой, и если я понимаю, каким должен быть поток для них, я думаю, я буду в порядке.

Ответы [ 3 ]

118 голосов
/ 27 января 2011

Это мой скромный взгляд на MVP и ваши конкретные проблемы.

Первый , все, с чем пользователь может взаимодействовать или просто показывается, - это представление .Законы, поведение и характеристики такого представления описываются интерфейсом .Этот интерфейс может быть реализован с использованием пользовательского интерфейса WinForms, пользовательского интерфейса консоли, веб-интерфейса или вообще без пользовательского интерфейса (обычно при тестировании докладчика) - конкретная реализация не имеет значения, если она подчиняется законам своего интерфейса представления.

Второй , представление всегда контролируется предъявителем .Законы, поведение и характеристики такого презентатора также описываются интерфейсом .Этот интерфейс не заинтересован в реализации конкретного представления, если он подчиняется законам интерфейса этого представления.

Третий , поскольку докладчик управляет своим представлением, чтобы минимизировать зависимости, на самом деле выгоды нетв том, что представление вообще ничего не знает о его предъявителе.Существует согласованный договор между докладчиком и представлением, и это определяется интерфейсом представления.

Последствия Третий :

  • Докладчик неесть любые методы, которые может вызвать представление, но у представления есть события, на которые докладчик может подписаться.
  • Докладчик знает свое представление.Я предпочитаю делать это с помощью инжектора конструктора в конкретном презентере.
  • Представление не имеет представления о том, каким образом его контролирует докладчик;он просто никогда не будет предоставлен ни одному докладчику.

Для вашей проблемы приведенное выше может выглядеть следующим образом в несколько упрощенном коде:

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();
    }
}

В дополнение к вышесказанному яобычно имеет базовый IView интерфейс, в котором я прячу Show() и любое представление владельца или заголовок представления, из которого обычно получают преимущества мои представления.

На ваши вопросы:

1. Когда winform загружается, она должна получить древовидную структуру.Правильно ли я считаю, что представление должно вызывать такой метод, как: Presenter.gettree (), это, в свою очередь, делегирует модели, которая получит данные для древовидного представления, создаст и настроит их, вернет ихдокладчик, который, в свою очередь, перейдет к виду, который затем просто назначит его, скажем, панели?

Я бы позвонил IConfigurationView.SetTreeData(...) с IConfigurationPresenter.ShowView(), прямо перед вызовомна IConfigurationView.Show()

2. Это будет то же самое для любого элемента управления данными в Winform, так как у меня также есть представление данных?

Да, я бы позвонил IConfigurationView.SetTableData(...) для этого.Это до представления, чтобы отформатировать данные, предоставленные ему.Презентатор просто подчиняется договору представления о том, что ему нужны табличные данные.

3. Мое приложение имеет несколько классов моделей с одинаковой сборкой.Он также поддерживает архитектуру плагинов с плагинами, которые должны быть загружены при запуске.Будет ли представление просто вызывать метод презентатора, который в свою очередь вызовет метод, который загружает плагины и отображает информацию в представлении?Какой уровень будет контролировать ссылки на плагин.Будет ли представление содержать ссылки на них или докладчика?

Если плагины связаны с представлениями, то представления должны знать о них, а не о докладчике.Если они все о данных и модели, то представление не должно иметь с ними ничего общего.

4. Правильно ли я считаю, что представлениедолжен обрабатывать все, что касается представления, от цвета узла дерева, до размера сетки данных и т. д.?

Да.Думайте об этом как о докладчике, предоставляющем XML, который описывает данные и представление, которое берет данные и применяет к ним таблицу стилей CSS.Конкретно, докладчик может позвонить IRoadMapView.SetRoadCondition(RoadCondition.Slippery), и представление затем отобразит дорогу красным цветом.

Как насчет данных для узлов, по которым щелкнули?

5. Если я нажму на триоды, я должен пройти через определенный узел к докладчику, а затем докладчик определит, какие данные ему нужны, а затем спросит модельдля этих данных, прежде чем представить их обратно в виде?

Если возможно, я бы передал все данные, необходимые для представления дерева в виде в одном кадре.Но если некоторые данные слишком велики, чтобы их можно было передать с самого начала, или если они динамические по своей природе и нуждаются в «последнем снимке» из модели (через докладчика), то я бы добавил что-то вроде event LoadNodeDetailsEventHandler LoadNodeDetails в интерфейс представления,чтобы презентатор мог подписаться на него, извлеките сведения об узле в LoadNodeDetailsEventArgs.Node (возможно, с помощью своего некоторого идентификатора) из модели, чтобы представление могло обновить свои показанные сведения об узле при возврате делегата обработчика событий.Обратите внимание, что асинхронные шаблоны этого могут потребоваться, если выборка данных может быть слишком медленной для хорошего пользовательского опыта.

10 голосов
/ 25 января 2011

Докладчик, который содержит все логика в представлении, должен ответить на нажатие кнопки как @JochemKempe говорит .В практическом плане обработчик события нажатия кнопки вызывает presenter.OpenFile().Затем докладчик может определить, что должно быть сделано.

Если он решает, что пользователь должен выбрать файл, он вызывает обратно в представление (через интерфейс представления) и позволяетпредставление, содержащее все технические аспекты пользовательского интерфейса, отображает OpenFileDialog.Это очень важное различие в том, что докладчику нельзя разрешать выполнять операции, связанные с используемой технологией пользовательского интерфейса.

Выбранный файл будет затем возвращен докладчику, который продолжит свою логику.Это может включать любую модель или службу, которые должны обрабатывать файл.

Основная причина использования шаблона MVP, imo, заключается в том, чтобы отделить технологию пользовательского интерфейса от логики представления.Таким образом, докладчик управляет всей логикой, в то время как представление хранит ее отдельно от логики пользовательского интерфейса.Это имеет очень приятный побочный эффект, заключающийся в том, что презентатор полностью тестируется на единицу.

Обновление: , поскольку презентатор является воплощением логики, обнаруженной в одном конкретном представлении отношения между представителем и представителем - это отношения IMO один-к-одному.И для всех практических целей один экземпляр представления (скажем, форма) взаимодействует с одним экземпляром презентатора, а один экземпляр презентатора взаимодействует только с одним экземпляром представления.

Тем не менее, в моей реализации MVP с WinForms презентатор всегдавзаимодействует с представлением через интерфейс, представляющий пользовательский интерфейс возможностей представления.Нет ограничений на то, какое представление реализует этот интерфейс, поэтому разные «виджеты» могут реализовывать один и тот же интерфейс представления и повторно использовать класс презентатора.

2 голосов
/ 25 января 2011

Докладчик должен действовать в конце запроса и показывать окно openfiledialog, как вы предложили.Поскольку от модели не требуется никаких данных, докладчик может и должен обработать запрос.

Предположим, вам нужны данные для создания некоторых объектов в вашей модели.Вы можете либо передать поток через слой доступа, где у вас есть метод для создания объектов из потока, но я предлагаю вам обработать анализ файла в презентаторе и использовать конструктор или метод Create для каждого объекта в вашей модели.1003 *

...