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

С# WinForms Model-View-Presenter (Пассивный просмотр)

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

Посмотрите общий GUI на следующую ссылку:

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

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

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

Фон:

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

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

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

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

Вопросы:

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

2) Когда дело доходит до "раздачи" событий из представления на презентатор/контроллер (какая бы терминология вы не хотели использовать W.R.T), каким образом я должен это делать? Иногда у меня есть простые свойства, которые необходимо обновить, а иногда мне нужен целый ряд шагов, чтобы развернуть.

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

Спасибо заранее!

Daniel

4b9b3361

Ответ 1

Вот простой пример, демонстрирующий концепцию пассивных представлений с использованием шаблона проектирования 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;
}

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

EDIT: Включенные пользовательские аргументы событий

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

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

    public T Value
    {
        get { return _value; }
    }
}

Ответ 2

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

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

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