2016-12-23 10 views
3

Извинения за длинный код, это так же сжато, как я мог его получить. Надуманный пример ниже, за которым следует объяснение:Как привязать данные к кнопке View's к модели через ее интерфейс в Presenter

namespace Contrived.Example 
{ 
    internal class Model : INotifyPropertyChanged 
    { 
     private bool _isAccountEmpty; 

     public bool IsAccountEmpty 
     { 
      get { return _isAccountEmpty; } 
      set 
      { 
       if (_isAccountEmpty == value) return; 
       _isAccountEmpty = value; 
       OnPropertyChanged(); 
      } 
     } 

     public event PropertyChangedEventHandler PropertyChanged; 

     [NotifyPropertyChangedInvocator] 
     protected void OnPropertyChanged([CallerMemberName] string propertyName = null) 
     { 
      PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); 
     } 
    } 

    internal interface IView 
    { 
     event EventHandler WithdrawalButtonClick; 
    } 

    internal class View : IView 
    { 
     private Button WithdrawalButton; 
     private void InitializeComponent() 
     { 
      WithdrawalButton = new Button(); 
      WithdrawalButton.Location = new System.Drawing.Point(0, 0); 
      WithdrawalButton.Size = new System.Drawing.Size(75, 25); 
      WithdrawalButton.Click += WithdrawalButton_Click; 
     } 

     private void WithdrawalButton_Click(object sender, EventArgs e) 
     { 
      WithdrawalButtonClick?.Invoke(sender, e); 
     } 

     public event EventHandler WithdrawalButtonClick; 
    } 

    internal class Presenter 
    { 
     private readonly IView _view; 
     private readonly Model _model; 
     //private readonly BindingSource bindSource; 


     public Presenter(IView view) 
     { 
      _view = view; 
      _model = new Model(); 
      view.WithdrawalButtonClick += OnWithDrawalButtonClick; 

      /* The behavior that follows is what I am after: 
      * how do I bind my button control to its model counterpart here? 
      * You can't expose the control directly on the IView interface, can you? 
      * 
      * bindSource = new BindingSource {DataSource = typeof(Model)}; 
      * bindSource.Add(_model); 
      * 
      * This next line would throw an exception because 
      * _view isn't actually exposing the button: 
      * _view.WithdrawalButton 
      *  .DataBindings 
      *  .Add("Enabled", bindSource, "IsAccountEmpty"); 
      */ 
     } 

     private void OnWithDrawalButtonClick(object sender, EventArgs e) 
     { 
      // Pretend we withdrew too much money 
      _model.IsAccountEmpty = true; 
     } 
    } 
} 

Так что это в основном структура моей программы. Я хочу привязать кнопку моего представления к свойству на своей модели через презентатора. Прокомментированный код отлично подходит для меня без интерфейс и в vb.Net (элементы управления имеют модификатор Friend вместо Private, поэтому они видны ведущему).

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

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

ответ

2

Сначала позвольте мне сказать вам, что ваша конструкция не совсем слабо связана. Если вы посмотрите на свой Presenter вы можете найти эту строку:

_model = new Model(); 

Думайте новое ключевое слово как зло, если вы хотите создать слабосвязанные программы. Потому что здесь вы привязаны к Модели. Не для какой-либо модели, а именно для этой модели. Если вы хотите, чтобы UnitTest ваш Presenter, вы не сможете моделировать модель. Поэтому вам лучше создать дополнительный IMode-интерфейс интерфейса и передать его в конструкторе.

public Presenter(IView view, IModel model) 
{ 
    this.model = model; 
    this.view = view; 
} 

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

После этого я должен прийти к вашему вопросу ;-). Сначала вы можете создать новый интерфейс, который обрабатывает способность DataBinding. Затем вы создаете новую кнопку, которая запускается из Standardbutton и реализует новый интерфейс. Затем ваш IView содержит свойство, которое содержит объект IBindingObject или что-то новое, что называется вашим новым интерфейсом. Это может выглядеть следующим образом:

public interface IBindingObject 
    { 
     ControlBindingsCollection DataBinding {get;} 
    } 

    public class MyButton : Button, IBindingObject 
    { 
     //Nothing to do here because the Button contains a DataBinding Property by default 
    } 

    public interface IView 
    { 
     IBindingObject WithdrawalButton {get;} 
    } 

    public class View : Form, IView 
    { 
     public IBindingObject WithdrawalButton {get {return new MyButton()}} 
    }  

    public class Presenter 
    { 
     public Presenter(IView view, IModel model) 
     { 
      view.WithdrawalButton.DataBindings.Add(//do your binding); 
     } 
    } 

Примечание такой подход, связанный с ControlBindingCollection, но этот компромисс нормально, я думаю. В противном случае вам всегда нужно предоставить доступ к вашей модели для просмотра или, наоборот, я думаю. Это нарушит шаблон. DataBinding и Winforms не так удобны, как в WPF. Поэтому, если у вас есть возможность использовать WPF, я бы рекомендовал это.

+0

Удивительно, это прекрасно! С этими дополнительными классами мои старые триады MVP теперь превратились в минимум из 6 классов. Очевидно, что тестируемость важна, но есть ли смысл в реальной работе, при которой эти шаблоны запутывают цель/поток до уровня непостижимости? Я создаю программу Inventory-Admin с почти 100 формами. Первоначально он был создан в VB6 с 1 формой, 1 класс SQL для каждого представления; Я воссоздал его в .Net, пытаясь его реструктурировать. Это действительно так, как это делается в реальном мире или есть компромиссы, чтобы сделать его более сжатым и читаемым? – Rhurac

+0

На мой взгляд, в реальном мире это часто делается как ваша программа vb6. Может быть, потому, что проще и быстрее создавать все вещи в одном классе или sth.Но если вы хотите разработать Программное обеспечение, которое должно жить в течение многих лет и продлеваться на протяжении многих лет, эти принципы необходимы. Найдите SOLID Priciples, если вы заинтересованы в разработке программного обеспечения. Далее я Fan of UserControls в Winforms. Каждый UserControl представляет собой представление, но вам не нужно много форм. Мне не нравятся многие Формы, потому что они ведут себя как всплывающие окна. Используйте меньше форм с Tabcontrol и Place usercontrol на каждой странице и вместо этого переключайте страницы ... – Sebi

+0

Отличная благодарность за понимание, которое я ценю. Да пребудет с тобой сила! – Rhurac