3

I am implementing a simple UserControl that is actually a fancy TextBox. One of its features is that you can set a formatting specification and this formatting get's applied to its contents automatically. For example, if you set the formatting specification to "000" and the contents are "42", then "042" will appear.

I am implementing this UserControl following the MVP pattern. The implementation is similar to this: how Implement usercontrol in winforms mvp pattern?. Also, check this question: passive view and display logic

Approach #1

My Title property in the View looks like this:

private string title;
public string Title {
    get { return title; }
    set { title = value; titleTextBox.Text = presenter.Format(title); }
}

It is my feeling that this implementation adds unnecessary coupling. For example, if I change the Presenter's Format method then I will have to go through all Views and change appropriately the call statement.

Approach #2

My Title property in the View looks like this:

public string Title {
    get { return presenter.Title; }
    set { presenter.Title = value; }
}

My Title property in the Presenter looks like this:

private string title;
public string Title {
    get { return title; }
    set { _view.SetTitle(this.Format(value); }
}

Now I have to add the SetTitle method in the View interface and in the View implementation:

public void SetTitle(string title) {
    titleTextBox.Text = title;
}

So, with this approach I get this ugly SetTitle Java-like method.

Approach #3

Instead of calling SetTitle create a new property RealTitle in the View and set that. It remains ugly with this Real prefix.

Your approach

Can you think of a better way?

Use case

The UserControl should be used like this:

var c = new FancyTextBox();
c.Format = "000";
c.Text = "42";
Controls.Add(c);

This snippet should display "042" inside the UserControl.

The big picture

Form            FancyTextBoxView             FancyTextBoxPresenter
  |                     |                              |
  |  ftb.Text = "42"    |                              |
  |-------------------->|                              |
  |                     |                              |
  |                     |              A               |
  |                     |----------------------------->|
  |                     |                              |
  |                     |              B               |
  |                     |<-----------------------------|

What are the A and B actions? I want the formatted text to appear in the UI. The formatting code is in the Presenter. The View has a titleTextBox that will hold the text in the UI.

Community
  • 1
  • 1
prekageo
  • 358
  • 3
  • 15

2 Answers2

2

Why don't you just define your View's Title property like this?

public string Title {
    get { return titleTextBox.Text; }
    set { titleTextBox.Text = value; }
}

There is absolutely no reason to define an additional SetTitle method. Also, in MVP, your view should never know about your Presenter.

Then whenever your Format function gets triggered, you can set the title of your view from there, for example:

void OnFormatCalled()
{
   _view.Title = FormatTitle(_view.Title);
}
BenMorel
  • 34,448
  • 50
  • 182
  • 322
Edwin de Koning
  • 14,209
  • 7
  • 56
  • 74
  • I clarified the use case in the question. How would the formatting be applied if I did the above? Is it true that the `View` should not know about the `Presenter`? Then how am I supposed to delegate events to the `Presenter`? Or even call helper methods, like `Format` in our example? – prekageo Sep 30 '11 at 12:52
  • @prekageo: By letting your view expose events, to which your Presenter can subscribe, for example a button click event. – Edwin de Koning Sep 30 '11 at 12:55
  • You are right about that! But there is no `Format` method exposed by the `UserControl`. It is a method of the `Presenter`. The user can only set `Title` or `Format`. What should happen when the user changes on of them? – prekageo Sep 30 '11 at 13:02
  • @prekageo: That depends on what you want, should it trigger a reformat when changed, or should the user first press a button? – Edwin de Koning Sep 30 '11 at 13:22
  • Let's forget the user for a while and only think of the programmer that will use this control. He wants to write the code mentioned above under "Use case" and expects to see the formatted text. – prekageo Sep 30 '11 at 13:27
  • Ok, well, you could in your view override the OnTextChanged eventhandler of your textboxes, and raise your own custom event instead (i.e. FancyTextBoxChanged). Then catch this event in your presenter, format the text (reading the required values from your view), and set the value back to the view. Got it, or do you need an example? – Edwin de Koning Sep 30 '11 at 13:42
  • Does the above mentioned procedure looks OK to you? It is something like: 1) set the textbox with the unformatted text 2) fire an event 3) read the unformatted text from the textbox 4) set the textbox with the formatted text. Check out the question. I've added a diagram to clarify the situation. – prekageo Sep 30 '11 at 13:45
1

I normally inject the View interface (and Model interface) into the Presenter. Now you have one place to worry about dependencies and testing.

Have your view fire an event when the title is set and have your Presenter subscribe to it. Then the Presenter can format the text and set the title on the view - the Presenter is in charge of the presentation of the view, the view just renders stuff. In this way the view is really dumb with little logic at all. This lends itself to being mocked out during unit testing and makes the presenter really easy to test.

Also notice how the formatting logic is also injected into the Presenter through a delegate property, thus decoupling the formatting from the presenter and making it changeable and testable. Just one way of doing it anyway...I like this approach.

public class Presenter
{
   private IView _view;
   private IModel _model;

   public Func<string, string> TitleFormatter { get; set; }

   public Presenter(IView view, IModel model)
   {
      _model = model;
      _view = view;

      _view.OnSetTitle += (s, e) => {
          _view.Title = TitleFormatter(e.Text);
       };
   }
}

public View : IView
{
    public event EventHandler<TitleChangedEventArgs> TitleChanged;

    public SomeUserActionEvent(object sender, SomeUserInterfaceEventArgs e)
    {
       TitleChanged(e.Text);
    }
}
Peter Kelly
  • 14,253
  • 6
  • 54
  • 63
  • How does the `View` fire the event `OnSetTitle`? Like that: `public string Title { get { /*...*/ } set { /* fire event */ }`. It will cause infinite recursion, I think. – prekageo Sep 30 '11 at 12:57
  • Don't fire the event from the Property. Expose the event in your interface as a separate thing and implement it in the view. – Peter Kelly Sep 30 '11 at 13:03
  • And then who has to fire this event? The programmer will change either `Format` or `Title` property and wants his change to be reflected in the UI. – prekageo Sep 30 '11 at 13:06
  • The View fires the event when the text is changed. The presenter listens for the event and sets the title property with the formatted text. The view renders the Title to the user. Sounds like a roundabout way? WinForms does not lend itself to this separation of concerns easily. – Peter Kelly Sep 30 '11 at 13:08
  • OK. So you describe a situation like this: In the view: `public string Title { set { /* fire event for the presenter to catch /* } }`. In the presenter's event handler: `_view.RealTitle = Format(title)`. In the view: `public string RealTitle { set { titleTextBox.Text = value } }`. Looks like approach #3 or not? – prekageo Sep 30 '11 at 13:14
  • See my updated answer - there is no event being fired from the property. It is fired from elsewhere in the view class. – Peter Kelly Sep 30 '11 at 13:38
  • And who should call the `SomeUserActionEvent` method? Check out the question. I've added a use case and a diagram. – prekageo Sep 30 '11 at 13:42
  • When the user types in the box, the event is fired and the formatting appears. I think you are misunderstanding the use of MVP though. It is a separation of concerns for UI and business logic. It sounds like you just simply want to sub-class a user control and forget about the MVP part. – Peter Kelly Sep 30 '11 at 13:44
  • Let's forget the user for a while and only think of the programmer that will use this control. Check out the use case above. The MVP part is important because this control will be implemented as a web control also. – prekageo Sep 30 '11 at 13:48
  • Look, in your diagram you have a message going from the Form to your FancyView. That is the message that starts the ball rolling and the event being fired up to the Presenter. – Peter Kelly Sep 30 '11 at 13:54
  • I agree. What are the next actions? A and B? A is the "fire event"? What is B? – prekageo Sep 30 '11 at 13:56