'How to call a callback function on a GUI event? Delegates?

I'm very new to C# and I'm wondering if using delegates is the right way here:

I created a UserControl in Visual Studio Windows Forms Designer. In a TableLayoutPanel I have 3 x 3 of these UserControls. Each of them gets a row and col index through the constructor.

Now in my Form that contains the TableLayoutPanel I want to call a function whenever one of the UserControls is clicked and have the row and col index in that function call.

I know how to process the Click event in the UserControl. But I don't know how I can call some kind of callback that I can register with the UserControl. So the UserControl's Click event handler would call something like Callback(Row, Col);

But I don't know how to get method of my Form into the UserControl. In C I would use a function pointer. Do I need a delegate here?

So in my UserControl partial class I would have something like:

public delegate void DoubleClickHandler(int row, int col);
public DoubleClickHandler Callback;

public void On_Click(object sender, EventArgs e)
{
    Callback(Row, Col);
}

And when creating the form I would do something like:

MyControl elem = new MyControl(1, 3);
elem.Callback = delegate (int row, int col) { Console.WriteLine("row {0}, col {1}", row, col); }

It works but I don't know if this is the right way to do it.



Solution 1:[1]

Winforms would probably use events for this; same idea, different keywords e.g.

For the event prop

//old
public DoubleClickHandler Callback;

//new
public event EventHandler<(int Row, int Col)> SomethingDoubleClicked;  //use a past tense name if you raise after, or a "SomethingDoubleClicking" if you raise before - probably hard to raise a click event before but..

OnClick might have code like

var eh = SomethingDoubleClicked;
eh?.Invoke(this, (theRow, theCol));

And subscription might look like:

//"modern"
yourcontrol.SomethingDoubleClicked += (s, e) => Console.WriteLine("row {0}, col {1}", e.Row, e.Col);



//"classic"
yourcontrol.SomethingDoubleClicked += SomeControl_SomethingDoubleClicked;

void SomeControl_SomethingDoubleClicked(object sender, (int Row, int Col) e)
{
    Console.WriteLine("row {0}, col {1}", e.Row, e.Col);
}

Note the += rather than = - an event is conceptually a list of methods that shall be invoked in an undefined order

Feel free to vary that tuple in the EventHandler to be a class or something.. Could also derive from EventArgs, but doesn't have to.

If you have no userstate to report, you can simplify to using:

public event EventHandler SomethingDoubleClicked; 

//onclick
var eh = SomethingDoubleClicked;
eh?.Invoke(this, EventArgs.Empty);

ourcontrol.SomethingDoubleClicked += (s, e) => ...;

But I would perhaps avoid deriving from EventArgs to provide custom user state, and then using the EventHandler form immediately above - it'll work because when you do:

eh?.Invoke(this, new MyCustomEventArgs(...));

MyCustomEventArgs derives from EventArgs so you can pass the child class in the parent type, but it means the dev that uses it will have to cast it back:

void SomeControl_SomethingDoubleClicked(object sender, EventArgs e)
{
    var mcea = e as MyCustomEventArgs;
}

and it's a bit nasty; better to use EventHandler<MyCustomEvenArgs> to make using it a TAB TAB/no casting affair

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1