C# WinForms Model-View-Ведущий (Пассивный Вид)

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

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

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

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

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

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

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

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

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

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

вопросы:

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

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

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

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

Даниил

2 ответов


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

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

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

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

в presenter мы можем использовать другой шаблон дизайна под названием инъекции зависимостей для обеспечения доступа к нашему представлению и любым классам моделей, которые нам могут понадобиться. В этом примере у меня есть 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: включены пользовательские события args

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

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

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

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

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

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