Using Event Handlers for Multiple Objects - c#

I have 20 items in a List<myObject>. Each has an instance of a UserControl associated with it. Each object is accessible via a MenuStrip that needs to display the UserControl when the appropriate item is clicked. Currently I have an event handler for each of them, which works, but I was wondering if a way exists to simplify this and use a single event handler for all of the items.
Is this possible? If so what is the best way to go about doing so.
EDIT:Can anybody else provide any input on this issue? I'm having trouble with Mailo's answer. Essentially all I need to do is make an event handler that can display the appropriate UserControl stored in a List<myObject> as a property when the correct MenuStrip item is clicked. Is there a more straightforward way to do this? Ideally I'd like to make it so that a foreach loop can go through the list and set up the handlers.
Is there nobody who can help me with this?

It's not very difficult. First is you need some way to associate a menu item with a control in the list.
1) Since you have a list, index is simplest way ( you could use Dictionary<> to simplify this association). So, lets say when you click the first menu item, you want myObjecList[0] to appear. When you click second MenuItem, myObjectList[1] would appear and so on. For this go to each menu item, and in the Properties, assign a value to Tag property. For first menu item, assign Tag to 0, for second item, assign Tag to 1 - and so on.
2) Create one event handler and assign the same handler to all menu items. The event handler could look something like this:
private void myToolStripMenuItem_Click(object sender, EventArgs e)
{
// source menu item which was clicked
ToolStripMenuItem item = sender as ToolStripMenuItem;
if(item != null)
{
int index = int.Parse(item.Tag.ToString()); // get the index from Tag
myObject control = myObjectList[index];
// do your stuff with your control
}
}

you can pass the sender object to your event handler and check for the type inside the handler
it will be something like this
//this will contain any properties you wanna send to the handler
public class MyHandlerEventArgs : EventArgs
{
}
//this delegate gets the sender, you can change the sender type to be the encapsulated type of your controls
public delegate void MyHandler(object sender, MyHandlerEventArgs args);
//this is the class that fires the event in your case it will UI class I think
public class MyController
{
public event MyHandler myEvent;
public void MyEvent_Fire()
{
if(myEvent != null)
myEvent(this, new MyHandlerEventArgs());
}
}
//here you can do your business logic for each control
public class MyAction
{
MyController mc = new MyController();
public MyAction()
{
mc.myEvent += new MyHandler(mc_myEvent);
}
void mc_myEvent(object sender, MyHandlerEventArgs args)
{
//check the sender type
//do your action
}
}
this link might be useful to
check

You might want the ItemClicked event from the MenuStrip. You can use the ClickedItem from the ToolStripItemClickedEventArgsto find your UserControl
Some pseudocode:
// Initialize
myMenuStrip.ItemClicked += itemClickedEvent;
// ...
void itemClickedEvent(Object sender, ToolStripItemClickedEventArgs e)
{
int index = myObjectList.FindIndex(e => e.instanceOfUserControl == e.ClickedItem);
// Now that we have the clicked item, display it how we would in an individual event handler.
myObjectList[index].instanceOfUserControl.DoDisplay();
}
Ideally I'd like to make it so that a foreach loop can go through the list and set up the handlers.
For this approach you'd use a foreach on the List
foreach (var listItem in myObjectList)
{
listItem.TheEvent += myEventHandler;
}

Related

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.
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

WPF DataGrid CanUserAddRows event

Is there an event or command I could use so I can invoke the object that is getting added to the ObservableCollection before it gets added?
At the moment, once the user clicks the row in the grid, it adds it to the collection, however I need to specifically assign properties in C# that I don't want to assign in the grid.
public void event
{
// I want to do something before the CanUserAddRow event does this
collection.Add(<T>;
}
You can use DataGrid.InitializingNewItem event:
private void InitializingNewItem(object sender, InitializingNewItemEventArgs e)
{
//use e.NewItem here
}
From MSDN
You can set default values for the new item by handling the InitializingNewItem event and setting the values programmatically
I'm not exactly sure is that work for you...
private void DataGrid_RowEditEnding(object sender, DataGridRowEditEndingEventArgs e)
{
YourObject obj = e.Row.Item as YourObject;
if (obj != null)
{
//see obj properties
}
}
Explanation :
In here after user enter the data to the grid, and It takes as e.Row.Item Then you can change any of modification to your object.

Adding events to Form designer file

I created a new Event in my user control (SearchControl) like this:
//Event which is triggered on double click or Enter
public event EditRecordEventHandler EditRecord;
public delegate void EditRecordEventHandler(object sender, EventArgs e);
//Supressing the events
private bool _raiseEvents = true;
private void OnEditRecord(System.EventArgs e)
{
if (_raiseEvents)
{
if (this.SearchResultGridView.FocusedRowHandle > -1)
{
if (EditRecord != null)
{
EditRecord(this, e);
}
}
}
}
Now this Event is called when user double click a row in a grid. So from the properties window I selected the MouseDoubleClick Event of the grid view and called the above created EditRecord event.
private void SearchResultListGridControl_MouseDoubleClick(object sender, MouseEventArgs e)
{
// Check whether the user clicked on a real and not a header row or group row
DevExpress.XtraGrid.Views.Grid.ViewInfo.GridHitInfo info = SearchResultGridView.CalcHitInfo(e.Location);
if (info.InRow && !SearchResultGridView.IsGroupRow(info.RowHandle))
{
OnEditRecord(e);
}
}
Now the issue I am facing is every time I double click a row in grid view it calls the SearchResultListGridControl_MouseDoubleClick() which then calls OnEditRecord(), however the value of EditRecord is everytime null.
To solve this I checked the designer file of the Main Control which has SearchControl and could not find the EditRecord Event entry in this. So I manually created it like this:
this.MySearchControl.EditRecord += new performis.BA.Merkmalsleisten.Search.SearchControl.EditRecordEventHandle(this.MySearchControl_EditRecord);
Now the things are working fine, but my question is why it did not create it automatically at the first place? And as far I know it is not recommendable to add anything manually to the designer file..is there any other way I can do it?
Thanks
When you create event it has to be used in the form designer similar to how you are using MouseDoubleClick for the Grid (so you need to find event in the Misc category, because you didn't define CategoryAttribute, double clicked there, etc).
If I understand it right you want to subscribe to event automatically, when form is created. You can do this in the control constructor (find parent form control.Parent or control.FindForm()) or perhaps in the special method, which you have to call from the form constructor, which in turn is basically similar to wiring event manually (which you did in the designer created file, but, instead, you can do in the form file, which is totally ok to edit) Up to you.
Sure.
A better practice would be to add your binding line:
this.MySearchControl.EditRecord += new performis.BA.Merkmalsleisten.Search.SearchControl.EditRecordEventHandle(this.MySearchControl_EditRecord);
To the form's constructor. something like:
public MyForm()
{
this.MySearchControl.EditRecord += new performis.BA.Merkmalsleisten.Search.SearchControl.EditRecordEventHandle(this.MySearchControl_EditRecord);
//The rest of your constructor.
}

C# combobox appropriate event type

I have a combobox and with this I have a associated event
private void comboBox8_SelectedIndexChanged(object sender, EventArgs e)
{
}
My comobox is populated with two items a and b
I am setting combobox8.selectedItem = x where x= a or b. My event only fires if I select a from b or b from a. It does not fire if I again select a from a.
How can I do it and what's the appropriate event to deal with it ?
Moreover I am doing it all programmatically.
It makes sense that the event doesn't fire again. The selected item doesn't change. Depending on what you actually want, there are a lot of events you can utilize. You might start with Click, or DropDown, or DropDownClosed, for instance.
It won't fire because Selected Index did not change...
Take a look at msdn documentation for a list of comboBox events:
http://msdn.microsoft.com/en-us/library/system.windows.forms.combobox_events.aspx
You'll find out you can use more one depending on what you want to achieve (leave,lostfocus, [...])
Because the index did not change, the event is not fired. Since you need this processing when you are refreshing the form programmatically, call the appropriate code programmatically as well:
private void comboBox8_SelectedIndexChanged(object sender, EventArgs e)
{
ProcessComboBoxInput();
}
private void RefreshFormProgrammatically()
{
// Refresh the form here...
ProcessComboBoxInput();
}
private void ProcessComboBoxInput()
{
// Process the comboBox8 here...
}
Because its selected index changed event. From a to a nothing has been changed. You can try onclick event.

Hooking up generic event handlers to multiple controls of the same type

I have a WinForms app that contains many NumericUpDown controls. In a nutshell, if my users enter a value into the control and then delete the text, I want to restore it (the text) when the control loses focus. So I decided that I'd check .Text when the control loses focus and if it's empty, I set .Text = .Value.ToString().
I'm doing this in the Leave event handler and it works just fine. But as I said, I have many of these controls (18, to be exact). I don't like creating 18 Leave event handlers that all do the same thing so I created a generic one like this:
private void numericUpDown_GenericLeave(object sender, EventArgs e)
{
if (string.IsNullOrEmpty(((NumericUpDown)sender).Text))
((NumericUpDown)sender).Text = ((NumericUpDown)sender).Value.ToString();
}
I started to hook up all of the controls to this generic event handler but I quickly got tired of doing this:
numericUpDown1.Leave += numericUpDown_GenericLeave;
numericUpDown2.Leave += numericUpDown_GenericLeave;
numericUpDown3.Leave += numericUpDown_GenericLeave;
...
numericUpDown18.Leave += numericUpDown_GenericLeave;
So I thought I'd create a function that would return a list of all the controls of a specified type and then loop through that list and hookup the event handlers. That function looks like this:
public static List<Control> GetControlsOfSpecificType(Control container, Type type)
{
var controls = new List<Control>();
foreach (Control ctrl in container.Controls)
{
if (ctrl.GetType() == type)
controls.Add(ctrl);
controls.AddRange(GetControlsOfSpecificType(ctrl, type));
}
return controls;
}
I call the function like this:
var listOfControls = GetControlsOfSpecificType(this, typeof(NumericUpDown));
foreach (var numericUpDownControl in listOfControls)
{
numericUpDownControl.Leave += numericUpDown_GenericLeave;
}
When I run my app, however, I don't see the expected behavior that occurs when I manually hookup each control to the generic event handler. This code is currently in the constructor of my form and I've tried calling it before as well as after the call to InitializeComponent() but neither one seems to be working. I get no error of any kind, I just don't see the behavior that I was expecting. I have a breakpoint set inside the generic event handler but the debugger never breaks so it seems like the event handler isn't being hooked up correctly. Does anyone know why this might be or how I can troubleshoot it further? Thanks!
EDIT
I just realized that the call to:
var listOfControls = GetControlsOfSpecificType(this, typeof(NumericUpDown));
was happening before the call to InitializeComponent() so of course the list of controls being returned was empty. DOH! Thanks for all the replys. I apologize for wasting everyones time. :-(
You're passing this to your method, which is presumably a reference to your form. Your method will only catch the controls that are placed directly on your form. Any NumericUpDown controls that are not directly on the form (i.e. they're sitting on a panel or something) will be missed.
Why not create a user control that has a NumericUpDown control in it.
Then handle this is in the user control events.
This worked for me:
private decimal _previous = 0;
private void numericUpDown1_ValueChanged(object sender, EventArgs e)
{
if (((NumericUpDown)sender).Text.Length > 0)
{
_previous = this.numericUpDown1.Value;
}
}
private void UserControl1_Leave(object sender, EventArgs e)
{
if (this.numericUpDown1.Text == "")
{
this.numericUpDown1.Value = _previous;
this.numericUpDown1.Text = System.Convert.ToString(_previous);
}
}
Just note that the Leave event is on the user control not on the updown control itself.
Question answered. See Edit above. Thanks to bsegraves for pointing me in the right direction.

Categories