C# WinForms – Presenter Accessing the Value in Text Box Using Sender Object

cmvpnetvalidationwinforms

I have following method in my presenters which validates the user inputs on the UI. Basically to capture the value of the text property of the text box, it uses sender object which was passed in by the event fire on the form.

PRESENTER

_View.ValidateInPut += new EventHandler(_View_ValidateInPut);

private void _View_ValidateInPut(object sender,  EventArgs e)
{
    var controller = (TextBox)sender;
    var text = controller.Text;

    If (text is InValid)
    MessageBox.Show ("The value in the Text box is invalid");

}

VIEW

private void txtACNo_Validating(object sender, CancelEventArgs e)
{
    ValidateInPut(sender, e); // Firing the event
}

Will this lead to unexpected errors?

If so please let me know a better way to do this?

Best Answer

I don't think it is a wise idea to have View and Presenter being coupled so tight.

Based on either the Passive Screen or Supervising Presenter the presenter and the view are separated by an interface that is implemented by the view. The presenter calls the interface. Here is an example of a supervising presenter.

PRESENTER

this class has no dependency on the used GUI library

public class Presenter 
{
    IView view;
    public Presenter(IView view)
    {
        this.view = view;
    }

    public void ValidateInPut(string value)
    {
        if (!String.IsEmptyOrNull(value) && value.Contains("9")) 
        {  
           view.ShowError("The value in the Text box is invalid");
        }
    }
}

ViewInterface

public inteface IView
{
    void ShowError(string message);
}

VIEW

public class Form1:Form, IView
{
    private Presenter presenter;
    public Form1()
    {
         this.presenter = new Presenter(this); // or have a Dependcy Injection Framework do this
    }

    private void txtACNo_Validating(object sender, CancelEventArgs e)
    {
        presenter.ValidateInPut(txtACNo); // validate
    }

    public void IView.ShowError(string message);
    {
        MessageBox.Show(mesage);
    }
}

Rationale

By utilizing your view and presenter the presenter (with all its (busines)logic) becomes easily testable or the view can be replaced by a WebPage without the need to adapt your presenter logic.

Test

public class Test:IView
{
    bool showErrorCalled = false;

    public void Test()
    {  
       var p = new Presenter(this);
       showErrorCalled = false;
       p.ValidateInput("8");
       Test.Assert(showErrrocalled);
    }

    public void IView.ShowError(string message);
    {
        showErrorCalled = true;
    }
}

WebPage

public Default:Page, IView
{
      private Presenter presenter;
      public Default()
      {
          this.presenter = new Presenter(this);
      }

      public Page_Load(object sender, EventArgs e)
      {
          if (IsPostBack)
          {
               presenter.ValidateInput(txtAcNo.Text);
          }
      }

      public void IView.ShowError(string message);
      {
          Error.Text = message;
      }

}

PS

Remember that the cast operator on an object may fail. Your code

var controller = (TextBox)sender;

will only work as long as sender is a TextBox. It is better to check if there is a more generalized type up the hierarchy that still provides you with the needed interface. In your case that could be Control that has a Text property as well. A more defensive approach would be:

var controller = sender as Control; // try to cast, if not return null
if (controller != null) 
{
     // do stuff with controller
}
else
{
    // logging
    Debug.WriteLine("controller is null");
}
Related Topic