I have a big numbers of labels custom controls let's say 100.
I would like to give them an MouseHover event.
I could do something like:
private void label_custom1_MouseHover(object sender, EventArgs e)
{
TextBox.Text = label_custom1.backcolor.ToString();
}
But then I would need to do that 100 times. Since I have 100 of them.
Is there a way to do that only once?
I guess I should probably declare the function in my custom_label class but so far I couldn't make it work.
Any Idea how to proceed?
Create only one event handler method like this:
private void Label_MouseHover(object sender, EventArgs e)
{
TextBox.Text = (sender as Label).BackColor.ToString();
}
And subscribe all Events to that method:
this.label_custom1.MouseHover += Label_MouseHover;
this.label_custom2.MouseHover += Label_MouseHover;
Thank you, this was very helpful.
This part didnt work though, it couldnt recognize the controls:
private void SetEventAllLabels()
{
var labels = Controls.OfType<Label>().Where(x => x.Name.StartsWith("label"));
foreach (var label in labels)
{
label.MouseHover += Common_MouseHover;
}
}
So I create a list containing all the label names and Then it works using this:
foreach (string i in liste)
{
CustomLabel x = (CustomLabel)(this.Controls.Find(i, true).FirstOrDefault() as Label);
x.MouseEnter += Common_MouseHover;
}
As a side note, using MouseEnter instead of MouseHover makes it much more responsive (faster reaction!)
Firstly, create a common event, we receive the Label posted here.
private void Common_MouseHover(object sender, EventArgs e)
{
TextBox.Text = (sender as Label).BackColor.ToString();
}
Give the common event to all Labels on the form. Here I assume Label names start with label, for example label1, label2, label3 ...
private void SetEventAllLabels()
{
var labels = Controls.OfType<Label>().Where(x => x.Name.StartsWith("label"));
foreach (var label in labels)
{
label.MouseHover += Common_MouseHover;
}
}
Call the SetEventAllLabels () method in the Load method of the form.
private void Form1_Load(object sender, EventArgs e)
{
SetEventAllLabels();
}
Related
Learning WPF with MacDonald's "Pro WPF 4.5 in C#," focusing on Ch5, Events.
How would I write a generic event handler that works with both Labels and TextBoxes to process the MouseDown event initializing a drag & drop procedure?
Here is my TextBox handler:
private void tBSource_MouseDown(object sender, EventArgs e) {
TextBox tBox = (TextBox)sender;
DragDrop.DoDragDrop(tBox, tBox.Text, DragDropEffects.Copy);
}
And my Label handler:
private void lblSource_MouseDown(object sender, EventArgs e) {
Label lbl = (Label)sender;
DragDrop.DoDragDrop(lbl, lbl.Content, DragDropEffects.Copy);
}
As you can see, I'm using the Content property and the Text property, depending on which object starts the event. If I try to use the same property for both senders, I get build errors (regardless of which I use). If I can avoid duplication, I would be very happy. Should I chunk a conditional out into another function and call that in the handler to determine what property should be used?
you could do something like this:
private void Generic_MouseDown(object sender, EventArgs e)
{
object contentDrop = string.Empty;
//Label inherits from ContentControl so it doesn't require more work to make work for all controls inheriting from ContentControl
if (sender is ContentControl contentControl)
{
//If you don't want to filter other content than string, you can remove this check you make contentDrop an object
if (contentControl.Content is string)
{
contentDrop = contentControl.Content.ToString();
}
else
{
//Content is not a string (there is probably another control inside)
}
}
else if (sender is TextBox textBox)
{
contentDrop = textBox.Text;
}
else
{
throw new NotImplementedException("The only supported controls for this event are ContentControl or TextBox");
}
DragDrop.DoDragDrop((DependencyObject)sender, contentDrop, DragDropEffects.Copy);
}
Let me know if you have any question
I wrote some code to create an additional textbox during runtime. I'm using the metro framework, but this shouldn't matter for my question.
When you click a button, a textbox is being created by a private on_click event:
private void BtnAddButton_Click(object sender, EventArgs e)
{
MetroFramework.Controls.MetroTextBox Textbox2 = new MetroFramework.Controls.MetroTextBox
{
Location = new System.Drawing.Point(98, lblHandy.Location.Y - 30),
Name = "Textbox2",
Size = new System.Drawing.Size(75, 23),
TabIndex = 1
};
this.Controls.Add(Textbox2);
}
What I want to do now is to use the click event of another button, to remove the Textbox again. What I am not sure about is, if I have to remove just the controll or also the object itself. Furthermore I can neither access the Textbox2 Control nor the object from another place.
private void BtnRemoveTextbox2_Click(object sender, EventArgs e)
{
this.Controls.Remove(Textbox2);
}
This does not work, since the other form does not know about Textbox2. What would be the best way to achieve my goal? Do I have to make anything public and if so, how do I do that?
You have to find it first before you choose to remove it.
private void BtnRemoveTextbox2_Click(object sender, EventArgs e)
{
MetroFramework.Controls.MetroTextBox tbx = this.Controls.Find("Textbox2", true).FirstOrDefault() as MetroFramework.Controls.MetroTextBox;
if (tbx != null)
{
this.Controls.Remove(tbx);
}
}
Here, Textbox2 is the ID of your textbox. Please make sure you're setting the ID of your textbox control before adding it.
You need to find those controls using Controls.Find method and then remove and dispose them:
this.Controls.Find("Textbox2", false).Cast<Control>().ToList()
.ForEach(c =>
{
this.Controls.Remove(c);
c.Dispose();
});
Since the control was created in another form, the current form has no way of knowing it by its instance name.
To remove it, loop through all controls and look for its Name:
private void BtnRemoveTextbox2_Click(object sender, EventArgs e)
{
foreach (Control ctrl in this.Controls)
{
if (ctrl.Name == "Textbox2")
this.Controls.Remove(ctrl);
}
}
I have a windows form consisting of a series of textboxes and a button.
The user needs to input some data into the textboxes and then the code uses these inputs to do some calculation.
The user then clicks the button and a chart is generated showing the results of the calculations.
The chart is done using R which is connected to C# via R.Net.
The question is: how can I make the chart to update dynamically as soon as the user changes some input in one of the textboxes (so without first clicking the button that generates the graph)?
I thought that I would need some loop that constantly checks if any of the textboxes has been changed but I cannot make this work:
foreach (Control subctrl in this.Controls)
{
if (subctrl is TextBox)
{
((TextBox)subctrl).TextChanged += new EventHandler();
}
}
TextChanged should trigger the buttonClick event so that the reated code that generates the graph is executed.
What is a good approach for this problem? Thanks.
<<<< EDIT >>>>>
Here is my code for the form:
public partial class myInputForm : Form
{
public myInputForm()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
// call methods to run calculations using new inputs from textboxes
plotGraph(); // plot the new results
}
}
I would like to keep the calculation methods and the plot function plotGraph() inside the button1_Click event.
Trying to adopt Refus L's suggestion, I am adding the following to the partial class myInputForm above:
private void TextBox_TextChanged(object sender, EventArgs e)
{
TextBox textbox = sender as TextBox;
if (IsValidText(textBox.Text))
{
textbox.TextChanged += new System.EventHandler(this.button1_Click);
}
else
{
DisplayBadTextWarning(textBox.Text);
}
}
private void myInputForm_Load(object sender, EventArgs e)
{
foreach (Control control in this.Controls)
{
var textBox = control as TextBox;
if (textBox != null)
{
textBox.TextChanged += TextBox_TextChanged;
}
}
}
But this still doesn't work. If I insert the following:
this.myFirstBox.textChanged += new System.EventHandler(this.button1_Click);
directly in the code that is automatically generated by the form designer it works and a change in the textbox myFirstBox triggers the button click and thus the plot.
But I would need to write a line for each textbox cause the foreach doesn't work there.
Can you please explain how to set this up to work in my form? Thanks.
You can just specify an existing method that you want to handle the event. Ideally you'd have a separate method that updates the chart, which can be called from anywhere. Then you can call it from your TextChanged or Button_Click event, but these two control events are not tied together (in TextChanged you may want to do some validation on the text first). Also, if you want to update the chart from some other place, you have an independent method you can call that will do it.
For example, if you have this method to update the chart:
private void UpdateChart()
{
// Do something here to update the chart
}
You can call it from your event handlers:
private void button1_Click(object sender, EventArgs e)
{
UpdateChart();
}
private void TextBox_TextChanged(object sender, EventArgs e)
{
// You might have some data validation on the TextBox.Text here,
// which you wouldn't need in the Button_Click event
TextBox textbox = sender as TextBox;
if (IsValidText(textBox.Text))
{
// Now update the chart
UpdateChart();
}
else
{
DisplayBadTextWarning(textBox.Text);
}
}
Then you can hook up all your TextBox.TextChanged events to the custom handler above:
private void Form1_Load(object sender, EventArgs e)
{
// Dynamically hook up all the TextBox TextChanged events to your custom method
foreach (Control control in this.Controls)
{
var textBox = control as TextBox;
if (textBox != null)
{
textBox.TextChanged += TextBox_TextChanged;
}
}
}
To make it as simple as possible: ComboBox1 is bound to an empty list (in Form1 load event handler), and there is an event handler associated with ComboBox1:
private void CB1_SelectedIndexChanged(object sender, EventArgs e)
{
MessageBox.Show("Event fired");
}
private void Form1_Load(object sender, EventArgs e)
{
CB1.DataSource = list1;
CB1.ValueMember = "Name";
CB1.DisplayMember = "Name";
}
The form is loaded, CB1.SelectedIndex = -1, CB1.Text = "", CB1.Items.Count = 0
When I click on Button1, list1 is populated. Now the situation is as follows:
CB1.SelectedIndex = 0, CB1.Text = "Some Text", CB1.Items.Count =196
BUT, the event didn't fire, although SelectedIndex changed from -1 to 0, and I didn't get MessageBox.Show("Event fired"). However, when the user selects some item from the list, the event fires. Also, there is another button that clears list1, and consequently CB1.Items. When this button is pressed, the event also fires (SelectedIndex changes from X to -1).
I've tried to use other events, such as SelectedValueChanged, TextChanged, SelectionChangeCommitted, with no success.
Although there is a simple brute force workaround for this problem, I still do not understand why the problem appears in the first place, and thus cannot anticipate similar situations. That's why I would be grateful if somebody explained to me why no events are firing in the situation I described.
My comment got enough attention, so it seemed appropriate that I should put this as a potential answer. You should make sure that you've actually assigned the event to the method either through a delegate or in the designer with the properties of the combobox itself.
// Somewhere in the form load or init events
CB1.SelectedIndexChanged += new EventHandler(CB1_SelectedIndexChanged);
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
this.button1.Click += new System.EventHandler(this.comboBox1_SelectionChangeCommitted);
}
private void Form1_Load(object sender, EventArgs e)
{
List<string> list = new List<string> { "a", "b", "c" };
comboBox1.DataSource = list;
comboBox1.SelectedIndex = 0;
}
private void comboBox1_SelectionChangeCommitted(object sender, EventArgs e)
{
MessageBox.Show(comboBox1.SelectedValue.ToString());
}
private void button1_Click(object sender, EventArgs e)
{
comboBox1.SelectedIndex = 1;
}
}
I want to be able to right click on an image and for there to be a menu show up. When I then click on one of the items I want it to point to a class:
private void link1add_MouseDown(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Right)
{
ContextMenu cm = new ContextMenu();
cm.MenuItems.Add("Add", HandleContextMenuAdd);
cm.MenuItems.Add("Remove", HandleContextMenuRemove);
link1add.ContextMenu = cm;
}
}
private void HandleContextMenuAdd(object sender, EventArgs e)
{
MessageBox.Show("Adding");
}
private void HandleContextMenuRemove(object sender, EventArgs e)
{
MessageBox.Show("Removing");
}
Code edited since first posted.
Thanks all for your help.
What about a lambda expression?
cm.MenuItems.Add("Item 2", (_, __) => {
if (...) ReadDocument();
});
or
cm.MenuItems.Add("Item 2", (_, __) => { this.myClassInstance.DoSomething(); });
Alternatively, you can create a method with the signature of the expected event handler:
cm.MenuItems.Add("Item 2", HandleContextMenuClick);
private void HandleContextMenuClick(object sender, EventArgs e)
{
if (...) ReadDocument();
}
That method doesn't need to be in the same class (you can write this.myClassInstance.HandleContextMenuclick for example). But I would hide the implementation detail from other classes to avoid unnecessary coupling.
Pattern for your own code after this:
public class ReadDocumentEventArgs : EventArgs
{
public string ImageInfo { get; set; }
}
public void ReadDocument(object sender, ReadDocumentEventArgs ea)
{
// do whatever you need to do
MessageBox.Show(ea.ImageInfo); // example
}
private void link1add_MouseDown(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Right)
{
PictureBox imageCtrl = sender as PictureBox;
// get the information you need to get from your control to identify it
string imgInfo = "Hello, World!"; // example
ContextMenu cm = new ContextMenu();
cm.MenuItems.Add("Item 1");
cm.MenuItems.Add("Item 2",
(EventHandler)((s, rde) =>
{ ReadDocument(s, new ReadDocumentEventArgs()
{ ImageInfo = imgInfo });
}));
link1add.ContextMenu = cm;
}
}
In your MouseDown over your image you can create a menu item using the code I supplied above that will call an event handler called ReadDocument. Notice that there is a ReadDocumentEventArgs class that you can customize to contain the necessary properties that will help you identify which image you have clicked on.
So, in the example I have above one of the first things that happens in MouseDown is that you get the instance of image control (I assume it's a Picture Box, but you can cast it to whatever it really is).
At that point, you can then get a file name or whatever from your image that identifies it from the other controls on your form.
Next, when creating a context menu item, it tells the menu item to call ReadDocument but passes in the special data just taken from the image control.
In the ReadDocument method, you can then do whatever you need to do. In my example, I simply throw up a MessageBox to show you what data you passed in.
What you will want is to put another event handler and call your readdocument method.