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.
CodePudding user response:
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