4

In my little WinForm app, intended to practice as a hobbyist, I'm using this code to pass data from the View to its Presenter:

public event Action<object, string, string?>? SearchInAlbums;

        private void Btn_SearchInAlbums_Click(object sender, EventArgs e)
        {
            if (!string.IsNullOrEmpty(cmbSearchContext.SelectedValue.ToString()))
                SearchInAlbums?.Invoke(this, Txt_Search.Text, cmbSearchContext.SelectedValue.ToString());
        }

Then in the listener of this event, the Presenter, I can write code like this:

        public void OnSearchInAlbums(object? sender, string searchPhrase, string? tableName)
        {
            if (!string.IsNullOrEmpty(tableName))
            {
                _albumList = _albumRepository.GetAlbumsByValue(searchPhrase, tableName);
                SetGridViewBindingSource();
            }
            else
            {
                _mainView.Message =
                    "No records were affected: the table name was NULL or EMPTY (no table name)";
            }
        }

This way this consumer (Presenter), doesn't have to ask for data - instead it gets it and then tells the Repository what to do with it. This way, in my mind, I have enforced the "Tell Don't Ask" principle.

But, I see nobody using code like:

public event Action<object, string, string?>? SearchInAlbums;

Instead, they all use:

public event EventHandler SearchInAlbums; //or something similar

By doing the latter, the presenter is forced to ask for data from the view and therefore the TDA principle has been broken. I'm a hobby-learner, so I don't know. What is the take by more experienced programmers? Am I actually doing things right here? Or am I using concepts wrong?

Teachers on YouTube always ask for data from the View of the Presenter, totally ignoring the TDA principle. Maybe that's normal in this case. But I wouldn't know why as it strikes me as unnecessary.

Rohit Gupta
  • 203
  • 2
  • 3
  • 12
Valmont
  • 173
  • 6

2 Answers2

5

But, I see nobody using code like:

 public event Action<object, string, string?>? SearchInAlbums;

Instead, they all use:

 public event EventHandler SearchInAlbums; //or something similar

By doing the latter, the presenter is forced to ask for data from the view and therefore the TDA principle has been broken.

Em, no. If you create a special SearchEventHandler with specific event args, like

public delegate void SearchEventHandler(object sender, SearchEventArgs e);

then you can pass all the data you like in the SearchEventArgs object and implement "tell don't ask" just as with a delegate declared by the Action syntax.

Note Action<...> was introduced with .Net Fw 3.5, whilst EventHandler is much older, it was available already in .Net Fw 1.1 and is still used extensively inside Winforms itself. That is probably the reason why you find more examples with the EventHandler syntax. Today, I guess I would prefer Action<object, string, string?> when it helps to avoid boilerplate code, and when I don't need a uniform implementation where each handler derives from EventHandler.

Doc Brown
  • 199,015
  • 33
  • 367
  • 565
  • Yes, thank you. Technically there's no need to avoid a custom `event Action` instead of using `event EventHandler` . Though I will encapsulate data so that I don't have to change signatures when expanding the data. So no `event Action` . Those parameters don't communicate either on top of aforementioned problem. Since everybody is using `EventHandler<>` , so will I unless I need a return type. I.e. `Func`. – Valmont Jun 11 '23 at 18:56
1

Teachers on YouTube always ask for data from the View within the Presenter totally ignoring the TDA principle

Well, maybe you took TDA a bit too far. Tell don't ask is a motto for us to improve the cohesion and encapsulation of the components. For example

class GridView {
  public visible: true;
  public enabled: true;
  public doSomething(): void { .... }
}

class Presenter {
  private grid: GridView;
  public doSomething(): void {
     //breaking TDA: poor encapsulation and asking for info the grid already knows.
     if(this.grid.visible && this.grid.enabled){
        this.grid.doSomeThing();
     }
  }
}

Would be better if

class GridView {
  private visible: true;
  private enabled: true;
  //high cohesion and encapsulation
  public doSomething(): void { 
     if(this.visible && this.enabled){
       //then ...
     } 
     //otherwise ...
  }
}

class Presenter {
  private grid: GridView;
  public doSomething(): void {
     //Compliant with TDA, the presenter is telling "grid", to do something
     this.grid.doSomething();
  }
}

Am I actually doing things right here? Or am I using concepts wrong?

Technically, it's correct, but you might not have weighed the pros and cons of each solution. Essentially, this code is neither taking 40h/week of your time nor under the same pressure as a real-world project (frequent, unexpected and WTF changes).

If I got it right, EventHandler SearchInAlbums seems more convenient because extending the event handling to custom events only takes you to extend EventArg vs changing signatures, which is in many cases a considerable drawback.

In other words, passing Event-related data is fine using a derived class from EventArgs

Laiv
  • 14,283
  • 1
  • 31
  • 69
  • I may have taken TDA to far. The question remains whether it's wise to use events like this to pass data around. I think it's an elegant method. In fact, instead of creating a DTO (our previous example where you were there) I first implemented a `RecordToEditEventArg : EventArgs` to pass all of the data of an album record to the presenter. But then I was told I wasn't allowed to do that. But when events can't be used like that, I feel we're missing out. That's where my question comes in: making sure I'm not missing something high-level like unit-testing problems or something. – Valmont Jun 10 '23 at 20:00
  • 1
    Ok! I see it now. [It might interest](https://stackoverflow.com/questions/1431359/event-action-vs-event-eventhandler) – Laiv Jun 10 '23 at 20:20
  • Yes, excellent find. Thank you. So my initial take, deriving from `EventArgs` to pass relevant event data was the right choice indeed. Maybe I should abandon YouTube and come here more often. I've entered a phase where I'm more interested in design issues than coding fundamentals. – Valmont Jun 10 '23 at 20:33
  • 1
    @Valmont youtube tutorials are good at "how I do X with Y **for dummies**". For learning, worth spending some money on e-learning platforms because of the content (more variety and sometimes more advanced). S.E. for everything else. Regarding blogs of random people (who probably did copy-paste from other blogs), avoid them as much as possible. – Laiv Jun 10 '23 at 20:41
  • Yuppers, learning that. Soon I will refactor my test project and then it's off to learning XUnit. Then I will be faced with my design choices. I bought an introductory course into XUnit and unit testing. But I couldn't wait to have a few burning questions answered. Can you add the "answer"? I'll accept it again. The obvious answer is that passing Event-related data is fine using a derived class from EventArgs. That's the whole point of it. – Valmont Jun 10 '23 at 20:53
  • 1
    @valmont Better now? – Laiv Jun 10 '23 at 20:58