2010-11-30 3 views
20

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

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

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

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

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

фона:

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

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

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

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

Вопросы:

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

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

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

Заранее благодарен!

Daniel

ответ

18

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

В предъявитель мы можем использовать другой шаблон проектирования под названием dependency injection, чтобы обеспечить доступ к нашему мнению и любой модели классов, которые мы, возможно, потребуется. В этом примере у меня есть 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; } 
    } 
} 
+0

Я ценю информативный ответ, но он особенно касается моих вопросов. Должен ли я использовать один вид (с этими 4-5 подкомпонентами, с которыми пользователь может взаимодействовать), или должны быть Представления в Представлениях (следовательно, интерфейсы в интерфейсах)? Кроме того, я до сих пор не уверен в «передаче» событий от представления к докладчику, и мне все еще нужно разъяснить, как View наблюдает за моделью. Меня тоже беспокоит «структура» программы. – 2010-11-30 21:43:26

+1

Я предпочитаю разбивать его на более мелкие компоненты, особенно если вы хотите поощрять повторное использование.Создавайте представления с единственной целью, но их можно использовать вместе для создания составного UI. – 2010-11-30 21:51:18

0

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

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

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