Подтвердить что ты не робот

Модель-View-Presenter в WinForms

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

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

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

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

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

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

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

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

  • Будет ли это одинаковым для любого управления данными в Winform, поскольку у меня также есть datagridview?

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

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

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

4b9b3361

Ответ 1

Это мое скромное решение 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, он должен получить treeview. Правильно ли я полагаю, что представление должно поэтому вызвать такой метод, как: presenter.gettree(), это, в свою очередь, будет делегировать модели, которая будет получать данные для древовидной структуры, создавать ее и настраивать, возвращать ее в ведущий, который, в свою очередь, перейдет к представлению, которое затем просто назначит его, скажем, панели?

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

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

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

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

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

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

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

Как насчет данных для щелкнутых узлов?

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

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

Ответ 2

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

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

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

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

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

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

Ответ 3

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

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