Creating controls on runtime - pass data - c#

In Windows Forms project I have this method to set some properties of dynamically created control. In this case I also need to show a tooltip when user hovers mouse over it. This works ok except for one thing, I have no idea how to pass the value of w["text"] to control_MouseEnter.
private void SetProp(ref Control obiekt, Dictionary<string, string> w)
{
obiekt.Name = w["id"];
obiekt.Location = new Point(Convert.ToInt16(w["wspx"]), Convert.ToInt16(w["wspy"]));
obiekt.Height = Convert.ToInt16(w["wys"]);
obiekt.Width = Convert.ToInt16(w["szer"]);
if (w["text"] != "")
{
obiekt.MouseEnter += new EventHandler(control_MouseEnter);
obiekt.MouseLeave += new EventHandler(control_MoouseLeave);
}
}
private void control_MouseEnter(object sender, EventArgs e)
{
toolTip.Show("how to pass a value here ??", (Control)sender, 5000);
}

I have no idea has to pass the value of w["text"] to control_MouseEnter.
You can either link your data with target control directly (for example via Control.Tag property) or indirectly (for example, via global variable/dictionary), or use anonymous delegate and closure to create the local data-context:
obiekt.MouseEnter += (s,e) => {
tooltip.Show(w["text"], (Control)s, 5000);
};
Is there a way to use EventArgs somehow?
No, you can't. Because the args are instantiated within the Control's code exactly at the moment when the mouse event occurs, and you can't control the EventArgs creation from event-subscription point.

How about setting the text into Tag member of Control object?
S.th. like object.Tag = w["text"]; and show it with event handler

Related

Retrieve values that are outside the context

I dynamically create controls and I'd like to be able to use them outside of the context.
For example a dynamically created label :
i = 0;
while (readerBE.Read())
{
Label labelBE = new Label();
labelBE.Name = "labelBE" + i;
labelBE.Text = readerBE["codeArticleComposant"].ToString();
labelBE.Cursor = Cursors.Hand;
labelBE.Click += new EventHandler(this.labelBE_Click);
i++;
}
And when I try to use the OnClick event to retrieve a value like this :
private void labelBE_Click(object sender, EventArgs e)
{
Console.WriteLine(labelBE.Text);
}
labelBE does not exist in the current context.
You can cast the sender argument:
private void labelBE_Click(object sender, EventArgs e)
{
Label labelBE = (Label) sender;
Console.WriteLine(labelBE.Text);
}
But one thing, you have a while-loop and you always create this Label and you never add it to any container control (like GroupBox, Panel or Form). So you would never create multiple and either the while-loop is wrong and should be replaced with an if or you should add the labels to a collection or parent control (well, you should do that anyway).
You can use the sender object that generated the click :
private void labelBE_Click(object sender, EventArgs e)
{
Console.WriteLine(((Label)sender).Text);
}
However I suggest you read more about Variable scope.
Your problem is that you create a variable inside a method, so this variable is no more accessible once you leave it.

How to sync up to different events in Winforms

If I have a button which does something and also a double-click event on a data grid which I want to do the same thing, what is the best way to ensure that only one function has to be maintained?
Apart from doing the following, is there any fancy C# way to indicate that two events are to do the same thing?
void button1_Click(...) { MyFunction(); }
void dataGrid1_DoubleClick(...) { MyFunction(); }
void MyFunction() { // do stuff }
I suppose that you are talking about a DataGridView (WinForms) so the signature of the event DoubleClick in the DataGridView and the signature of Click event on a button control is the same.
(An EventHadler). In this case you can simply set the same method using the form designer or manually bind the event
dataGridView1.DoubleClick += new EventHandler(MyFunction);
button1.Click += new EventHandler(MyFunction);
Of course the MyFunction method should match the expected signature of an EventHandler
private void MyFunction(object sender, EventArgs e)
{
// do your work
}
Reviewing my answer after a few minutes I wish to add:
If you find yourself in a situation in which you need to differentiate between the controls using the sender object (like Control c = sender as Control; if (c.Name == "someName") ) I really suggest you to return to the first idea. Call a common method but keep the EventHandler separated for each control involved.
Using VS, in the form's designer view You can set the procedure You want to call to each control's each event in the control's properties window.
image
Just to add to what Steve said, you will want to bind these events to your function manually in the Load event of your form, instead of using the events under the lightning bolt in the properties window in the designer, like so:
private void Form1_Load(object sender, EventArgs e)
{
button1.Click += MyMethod;
dataGridView1.DoubleClick += MyMethod;
}
void MyMethod(object sender, EventArgs e)
{
//Do Stuff
}
Also, declaring a new instance of the EventHandler class has been redundant since Anonymous methods were introduced to C#, you can just point the event directly at the method as shown above.

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.

Is it possible to define a class-scope objet from a method?

This may seem totally unreasonable to ask, but I have been designing a multi-panel, real device simulator, that has many different screens and my current approach is to add all the screen objects from the code only and dispose them when I switch to another screen.
I have some fixed objects, that are the real device buttons that are already defined and in place. The thing is, I am separating each panel construction in methods, for example: buildLogin(), buildMainScreen(), etc, and I need to edit some of the screen objects from those methods, like changing the color of an enabled function label to green if enabled or white if disabled.
My question is: would it be possible to declare an object from a method that would be accessible in the whole class, like if it were defined in the variable declaration section? It would be something like the GLOBAL in PHP.
I can't declare it on top of everything like they would always be because when I dispose the objects, I can't "re-create" them, because of parenting, or re-using a disposed object or something...
[EDIT] Sample code:
public partial class frmMain : Form
{
//I could as well do this:
//Button button1 = new Button();
public frmMain()
{
buildLogin();
}
private void buildLogin()
{
Panel panel1 = new Panel();
Controls.Add(panel1);
//But then, there is no way to do this:
// if (button1.IsDisposed == true) //because of the panel, or smthing
Button button1 = new Button();
panel1.Controls.Add(button1);
button1.Click += (s, f) => { panel1.Dispose(); buildMainMenu(); };
}
private void buildMainMenu()
{
Panel panel2 = new Panel();
Controls.Add(panel2);
Button button2 = new Button();
panel2.Controls.Add(button2);
}
//This was created from the Designer and is class-scoped
private void btn_Frame_TSK1_Click(object sender, EventArgs e)
{
//Here, I have no access to the objets I've created programatically.
//button1.Text = "Text changed!";
}
}
If you want to make sure things are always completely dynamic and always done in the code behind, you may want to look at searching for the controls you've created in the Controls collection.
For example, make sure to give button1 an ID value (button1.ID="textUpdatedButton") that will identify it uniquely from other controls you create. Then use FindControl or search on the Controls collection to find the control with the ID you want to locate in your event handler.
//This was created from the Designer and is class-scoped
private void btn_Frame_TSK1_Click(object sender, EventArgs e)
{
Control control = this.FindControl("textUpdatedButton");
if(control != null && control is Button){
Button button1 = (Button)control;
button1.Text = "Text changed!";
}
}
Alternatively, to make things look more like a variable, you can use a Property to hide the control finding (as mentioned previously):
private Button Button1 {
get { return (Button)this.FindControl("textUpdatedButton"); }
}
//This was created from the Designer and is class-scoped
private void btn_Frame_TSK1_Click(object sender, EventArgs e)
{
if(this.Button1 != null){
this.Button1.Text = "Text changed!";
}
}
The actual implementation will vary with how you build up your controls, but essentially this approach can let you build everything dynamically in your code behind if you need to do it that way. Just remember to use identifiers to let you find things later.
Define your object at the class level, as Static. This way it will be accessible from all methods of all instances of the class(disposing an instance will not affect it).

How do I change the properties of a ContextMenuItem from inside an event handler

I'm slowly getting the hang of C# and this question is probably a result of bad design but here goes.
I have dynamic menus being generated thusly:
public Form1()
{
InitializeComponent();
AddContextMenu();
}
public void AddContextMenu()
{
ContextMenuStrip mnuContextMenu = new ContextMenuStrip();
mnuContextMenu.ItemClicked+=
new ToolStripItemClickedEventHandler(mnuContextMenu_ItemClicked);
this.ContextMenuStrip = mnuContextMenu;
ToolStripMenuItem mnuItemEnable = new ToolStripMenuItem("Enable");
mnuContextMenu.Items.Add(mnuItemEnable);
}
and the event handler:
private void mnuContextMenu_ItemClicked (Object sender,
ToolStripItemClickedEventArgs e)
{
//do stuff here
}
How do I change mnuContextMenu.Text (or any other property) from inside the event handler?
VS says :
mnuContextMenu does not exist in the
current context
There's a reason that all event handler methods have the exact same signature in the .NET world. You've probably noticed that the sender and e arguments are always there, no matter which event you're handling. They provide all the information you need.
In this particular case, you're looking for the sender parameter, which is a reference to the specific control that raised the event.
Of course, it's typed as an Object, so you'll have to cast it to a more derived type in order to use it like you want to. That's straight-forward enough—since you know that an ItemClicked event is only going to be raised by a ContextMenuStrip object, just cast it directly:
private void mnuContextMenu_ItemClicked (Object sender, ToolStripItemClickedEventArgs e)
{
((ContextMenuStrip)sender).Text = "Your text";
}
Or, if you want to play it safe (and you probably do), follow the standard idiom:
private void mnuContextMenu_ItemClicked (Object sender, ToolStripItemClickedEventArgs e)
{
// Try to cast the object to a ContextMenuStrip
ContextMenuStrip cmnu = sender as ContextMenuStrip;
// Verify that the cast was successful
// (if it failed, the cmnu variable will be null and this test will fail,
// preventing your code from being executed and your app from crashing)
if (cmnu != null)
{
cmnu.Text = "Your text";
}
}
There's absolutely no reason to litter your code with maintaining class-level references to these objects when there's a perfectly good, built-in way of obtaining references to exactly the ones that you want, when you want them.
mnuContextMenu only existed in the scope of AddContextMenu.
You have a couple of options:
this.ContextMenuStrip.Text = "Hello World";
or:
((ContextMenuStrip) sender).Text = "Hello World";
The first works because you stored the local mnuContextMenu in the class propery ContextMenuStrip. The second way casts the sender paramater (object raising the event) to a ContextMenuStrip.
Clearly it fails because you declare the context menu object inside the AddContextMenu method as local method variable instead of having it as private member of the containing class. the solution MegaHerz has suggested would probably work, or you keep a reference to your object as private member of the class.

Categories