C # WinForms Model-View-Presenter (Пассивное представление) - PullRequest
20 голосов
/ 30 ноября 2010

Я разрабатываю приложение WinForms на C #.У меня ограниченный опыт программирования GUI, и мне приходится многому учиться на лету.При этом, вот что я строю.

См. Общий графический интерфейс пользователя по следующей ссылке:

GUI http://img227.imageshack.us/img227/1084/program0.jpg

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

Я много изучал шаблоны проектирования GUI и шаблон, которым я являюсь.желающим реализовать это пассивное представление (см. http://martinfowler.com/eaaDev/PassiveScreen.html). Я ищу некоторую помощь о том, как собрать все это вместе.

Справочная информация:

1) В зависимости от того, что пользовательщелкнув в «TreeView», «Список» в нижнем левом углу отобразит список объектов, которые могут заполнить область «Редактор».Эти объекты могут быть TextBox или DataGridView.Пользователь переключает Список, чтобы выбрать то, что он / она хочет видеть в «Редакторе»

2) Модель, по сути, представляет собой папку с данными и файлами конфигурации.Существует внешняя программа, которая запускается в заданном каталоге, создает выходные файлы / папки и т. Д. Эта программа, которую я разрабатываю, предназначена для эффективного управления / настройки этих объектов удобным для пользователя способом

3) Проблемас тем, как я это делал, это то, что тестирование практически невозможно, и, следовательно, переход к шаблону проектирования пассивного представления MVP

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

Вопросы:

1) Нужно ли реализовывать один большой интерфейс / представление для всего «вида» программы, а затем реализовывать подчиненные интерфейсы / вложенные представления для каждого из TreeView?, Редактор, логгер и т. Д.?Или есть лучшая «структура» для этого?

2) Когда дело доходит до «передачи» событий от Представления к Presenter / Controller (какой бы терминологии вы ни хотели использовать WRT, шаблон проектирования Passive View).) как мне это делать?Иногда у меня есть простые свойства, которые необходимо обновить, а иногда мне нужно развернуть целую серию шагов.

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

Заранее спасибо!

Даниэль

Ответы [ 2 ]

18 голосов
/ 30 ноября 2010

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

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

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

public interface ICustomerManagementView
{
    void InitializeCustomers(ICustomer[] customers);
    void DisplayCustomer(ICustomer customer);
    event EventHandler<EventArgs<ICustomer>> SelectedCustomerChanged;
}

Он предоставляет один метод InitializeCustomers , который будет использоваться для инициализации нашего представления с объектами из нашей модели.

У нас также есть событие SelectedCustomerChanged , которое будет использоваться нашим докладчиком для получения уведомления о том, что в представлении произошло действие.

Как только у нас будет контракт, мы можем начать обрабатывать эти взаимодействия в нашем докладчике.

public class CustomerManagementPresenter
{
    private ICustomer _selectedCustomer;
    private readonly ICustomerManagementView _managementView;
    private readonly ICustomerRepository _customerRepository;

    public CustomerManagementPresenter(ICustomerManagementView managementView, ICustomerRepository customerRepository)
    {
        _managementView = managementView;
        _managementView.SelectedCustomerChanged += this.SelectedCustomerChanged;

        _customerRepository = customerRepository;

        _managementView.InitializeCustomers(_customerRepository.FetchCustomers());
    }

    private void SelectedCustomerChanged(object sender, EventArgs<ICustomer> args)
    {
        // Perform some logic here to update the view
        if(_selectedCustomer != args.Value)
        {
            _selectedCustomer = args.Value;
            _managementView.DisplayCustomer(_selectedCustomer);
        }
    }
}

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

В конструкторе у нас есть две важные строки кода, во-первых, мы подписались на событие SelectedCustomerChanged, по нашему мнению, именно здесь мы можем выполнять связанные действия. Во-вторых, мы вызвали InitilaizeCustomers с данными из хранилища.

На данный момент мы фактически не определили конкретную реализацию для нашего представления, все, что нам нужно сделать, - это создать объект, который реализует ICustomerManagementView . Например, в приложении Windows Forms мы можем сделать следующее

public partial class CustomerManagementView : Form, ICustomerManagementView
{
    public CustomerManagementView()
    {
        this.InitializeComponents();
    }

    public void InitializeCustomers(ICustomer[] customers)
    {
        // Populate the tree view with customer details
    }

    public void DisplayCustomer(ICustomer customer)
    {
        // Display the customer...
    }

    // Event handler that responds to node selection
    private void CustomerTreeViewAfterSelect(object sender, TreeViewEventArgs e)
    {
        var customer = e.Node.Tag as ICustomer;
        if(customer != null)
        {
            this.OnSelectedCustomerChanged(new EventArgs<ICustomer>(customer));
        }
    }

    // Protected method so that we can raise our event
    protected virtual void OnSelectedCustomerChanged(EventArgs<ICustomer> args)
    {
        var eventHandler = this.SelectedCustomerChanged;
        if(eventHandler != null)
        {
            eventHandler.Invoke(this, args);
        }
    }

    // Our view will raise an event each time the selected customer changes
    public event EventHandler<EventArgs<ICustomer>> SelectedCustomerChanged;
}

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

РЕДАКТИРОВАТЬ: включены пользовательские аргументы событий

public class EventArgs<T> : EventArgs
{
    private readonly T _value;

    public EventArgs(T value)
    {
        _value = value;
    }

    public T Value
    {
        get { return _value; }
    }
}
0 голосов
/ 02 декабря 2010

Я бы разбил их на отдельные представления с их собственными подарками и использовал бы «управляющий» презентатор / представление для управления делегированием сообщений между ними.Это не только улучшит тестируемость, но и обеспечит выполнение SRP для ваших элементов управления.

Таким образом, в вашем случае у вас может быть IFormManager, который будет реализовано вашим главным окном, а затем IFileManager, ILoggerWindow и т. Д.

Хотя это может быть немного излишним в использовании, я бы посоветовал вам взглянуть на Smart Client Software Factory (из команды Microsoft Patterns and Practices) - он больше не разрабатывается активно, но имеетхорошая реализация MVP, и она хорошо справляется с таким видом композиции, поэтому может дать вам несколько хороших идей.

...