如何通过 Presenter 中的界面将我的视图按钮数据绑定到模型

How to data-bind my View's button to the Model, via its interface in the 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;
        }
    }
}

这就是我的程序的基本结构。 我想通过演示者将我的视图按钮绑定到其模型上的 属性。 注释代码对我来说工作正常 没有 一个界面,in vb.Net(控件有 Friend 修饰符而不是 Private,因此它们对演示者可见)。

现在在 C# 中,按钮是私有的,此外,我正在尝试通过将接口放入组合中来练习松散耦合。现在我遇到了 运行 问题,我无法再像以前那样将按钮绑定到模型。谁能告诉我解决这个问题的正确方法是什么?非常感谢。

P.S。如果这个设置一开始就完全错误,我也想知道,这是我在学校没有对这个主题进行任何真正集中培训的情况下能够自己想出的最好的设置。

首先让我告诉您,您的构造并非完全松散耦合。如果 你看看你的 Presenter 你会发现这一行:

_model = new Model();

如果您想创建松散耦合的程序,请将新关键字视为邪恶。因为在这里你被绑定到模型。不是针对任何模型,而是针对那个模型。如果您想对 Presenter 进行单元测试,您将无法模拟模型。所以你最好再创建一个 Interface IModel 并在构造函数中传递它。

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

要了解更多关于为什么你应该这样做以及你如何处理这个以及阅读某事。大约 DependencyInjection。完成此步骤后,您就可以传递适合您的界面的任何模型。

在此之后我应该来回答你的问题 ;-)。首先,您可以创建一个处理数据绑定功能的新接口。然后创建一个新的 Button,它继承自 Standardbutton 并实现新的 Interface。然后,您的 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,我会推荐它。