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 и делает этот вид композиции довольно хорошо, поэтому может дать вам некоторые хорошие идеи.