How to know which button was clicked in winforms c#? - c#

Okay so this is a little bit tricky to explain.
I'm working on a basic time table form.
I have 7 buttons, named btnMonTime, btnTueTime and so on till btnSunTime based on the days of the week. Now on each button click, a pop up window (winform) opens which lets the user select a certain time through a dateTimePicker control. The time is parsed into a string and stored. There is an Accept button on the popup which when pressed, the popup closes and a label beside the particular day stating the time is posted.
`
Now I know how to do it for one particular day, but the thing is that I have one single function doing this label creating. But how do I know which Time button was clicked to place it at the right place?
This is the code that I could come up with:
private void btnAccept_Click(object sender, EventArgs e)
{
formPopup.time = timePicker.Value.ToShortTimeString();
//label1.Text = formPopup.time;
Label newLabel = new Label();
newLabel.Text = formPopup.time;
newLabel.Location = new System.Drawing.Point(205 + (100 * formTimeTable.CMonTime), 78);
formTimeTable.CMonTime++;
newLabel.Size = new System.Drawing.Size(100, 25);
newLabel.ForeColor = System.Drawing.Color.White;
thisParent.Controls.Add(newLabel);
this.Close();
}
This is the Accept button click handler which places the label at the right place. Whereas the variable CMonTime keeps track of how many times a particular Time button was pressed.
public static int CMonTime = 0;
private void btnMonTime_Click(object sender, EventArgs e)
{
formPopup f2 = new formPopup();
f2.thisParent = this;
f2.Show();
}
And this is what is happening inside the Monday's Time button click handler.
But how can I know which day's Time button was actually clicked for proper placement of the timestamp label?
Like if Tuesday's Time button would be clicked, the timestamp should be displayed beside the Time button for Tuesday.
I tried to be as clear as possible.
Thanks in advance.

You can get the button that was clicked by casting the sender parameter as a Button control.
Use the button's location as a parameter for your formPopup constructor
private void btnMonTime_Click(object sender, EventArgs e)
{
var button = (Button)sender;
formPopup f2 = new formPopup(button.Location);
f2.thisParent = this;
f2.Show();
}
formPopup
Point _buttonLocation;
public frmPopup(Point buttonLocation)
{
_buttonLocation = buttonLocation;
}
Then use the button's location to set your label's location
private void btnAccept_Click(object sender, EventArgs e)
{
formPopup.time = timePicker.Value.ToShortTimeString();
Label newLabel = new Label();
newLabel.Text = formPopup.time;
newLabel.Location = new Point(_buttonLocation.X + 100, _buttonLocation.Y);
formTimeTable.CMonTime++;
newLabel.Size = new System.Drawing.Size(100, 25);
newLabel.ForeColor = System.Drawing.Color.White;
thisParent.Controls.Add(newLabel);
this.Close();
}

sender is the object that raised the event. In your case, it will be the button the user clicked on.

Because these combinations of controls repeat, it may be easier to create a UserControl that contains the buttons and labels you want. Think of a UserControl as a small form consisting of a few controls, and you place it on your form as many times as you need to. It has its own event handlers. That way there's technically only one button (not seven) and one event handler. Then you can place that UserControl on your form seven times.
There are other ways to avoid duplicating the code, like having one event handler and assigning it to the click events for all seven buttons. But the UserControl will really save you time if you want to edit the layout of the buttons themselves. You don't want to have to do that seven times.
To try it out:
Add > New Item > User Control. You'll get what looks like a new form. Add a few controls to it the same way you would add controls to a form. Then save it using the name (just for the sake of example) MyUserControl.cs.
Build the project.
A new toolbox tab will appear in the toolbox, and your control will be there.
Drag your control onto your form just as you would any other control.
More info on creating user controls

Related

Grabbing the textbox label

Hello im making my first project with about 10 different textboxes where the user puts data in. when he/she clicks the the textbox the textbox text clears and a virtual numpad form pops up and when he leaves the textbox the numpad "hides".
right now (or i would) i have 2 events for every textbox, a click event and a leave event,
private void sheetWidthBox_Enter(object sender, EventArgs e)
{
vnumPadForm.Location = PointToScreen(new Point(sheetWidthBox.Right, sheetWidthBox.Top));
vnumPadForm.Show();
}
Im sure there is a way of dynamically coding that in one event and just grabbing the label name. i have played around with it a bit on my numpad like this and it works good;
private void button_Click(object sender, EventArgs e)
{
Button b = (Button)sender;
string num = b.Text;
SendKeys.SendWait(num);
}
Like that but instead i want to get the label name
right now (or i would) i have 2 events for every textbox, a click event and a leave event,
it works but very inefficient.
Change the name of the handler to something generic like "anyBox_Enter()", and update to the code below:
TextBox curTextBox = null;
private void anyBox_Enter(object sender, EventArgs e)
{
curTextBox = sender as TextBox;
vnumPadForm.Location = PointToScreen(new Point(curTextBox.Right, curTextBox.Top));
vnumPadForm.Show();
}
Note that I added a class level variable called "curTextBox" that gets set from the generic handler! This will track whatever TextBox was entered last.
Now, one by one, select each TextBox on your Form that you want to wire up to this common handler. After selecting each one, in the Properties Pane, click on the "Lightning Bolt" Icon to switch to the events for that control if they are not already showing. Find the "Enter" entry and change the dropdown to the right so that it says "anyBox_Enter".
Then in your button click handlers you can use the "curTextBox" variable to know where to send the key:
private void button_Click(object sender, EventArgs e)
{
Button b = (Button)sender;
string num = b.Text;
if (curTextBox != null) {
curTextBox.Text = num; // or possibly APPEND this to the TB?
}
}

Button.Text is changing after Form is visible

I have a button that loads a Form, as it gets lots of data from a database and takes a few seconds, I want to advise the user to wait.
When I click the button the button text does not change.
This is the button Click code I am using:
private void btnItemConfigForm_Click(object sender, EventArgs e)
{
var itemConfigBtnText = btnItemConfigForm.Text;
btnItemConfigForm.Text = "Waiting...";
ItemConfigForm form = new ItemConfigForm();
form.Show();
if (form.Created)
{
btnItemConfigForm.Text = itemConfigBtnText;
}
}
If I Comment out
if (form.Created)
{
btnItemConfigForm.Text = itemConfigBtnText;
}
Then the button text changes to waiting after the new form window is visible.
What am I missing to get the button text to change before the form window is visible.
the simple solution is to add this row:
btnItemConfigForm.Refresh();
after this row
btnItemConfigForm.Text = "Waiting...";
Otherwise the text of the button will be changed only when the function ends, this function will redraw the form display!
P.s.
If you want the form will not be blocked - you can use in asynchronic running to the function "Show" (or New) then you will need Event to notify the first form when the form will be loaded
sorry for my English... :)
Added
btnItemConfigForm.Update();
under
var itemConfigBtnText = btnItemConfigForm.Text;
btnItemConfigForm.Text = "Waiting...";
This updates the button Control before moving on to initialising and showing the form.

Triggering Button.PerformClick() on a hidden button

I'm working on a C# WinForms application in which I have a number of processes that are all managed by a 'master' application.
In this master application, each process is visualized by its own FlowLayoutPanel which contains a number of buttons for various function. I call these panels the 'process blocks'.
However, when many of these processes are made, not all blocks easily fit on the screen. For this reason I am implementing a 'compact mode', which hides all the buttons of all the process blocks, leaving only their name, their status and the start/stop button visible. I then assign a ContextMenuStrip to each process block, in which I show all the buttons listed as a ToolStripMenuItem so I can access all the functions of the process block that way. I am clearing these ContextMenuStrips dynamically and add the items when the menu is opened.
I do this by iterating over all the child controls of the FlowLayoutPanel, see if they are of type Button, and if so, I add them to the ContextMenuStrip. See the code snippet below:
private void PanelCmsOpened(object sender, EventArgs e) {
try {
ContextMenuStrip cMenuStrip = (ContextMenuStrip) sender;
// Clear all items from the context menu
cMenuStrip.Items.Clear();
// Loop over all controls in the FlowLayoutPanel
foreach (var c in CPanel.Controls) {
Button btn = c as Button;
if (btn == null) continue; // Not a button, continue
// Get the text from the button
string lbl = btn.Text;
if (string.IsNullOrEmpty(lbl)) {
try {
// The button has no text (only an icon), so we get the tooltip text of the button
lbl = PanelTooltip.GetToolTip(btn);
}
catch {
// We can't get any text to display, so skip this button
continue;
}
}
// Add a new item to the ContextMenuStrip
cMenuStrip.Items.Add(new ToolStripMenuItem(lbl,
btn.BackgroundImage,
(s, ea) => btn.PerformClick() // Perform a click on the button
)
{
Enabled = btn.Enabled
});
}
}
catch (Exception Ex) {
MessageBox.Show("Fout bij openen van context menu: " + Ex.Message, "Fout", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
The problem:
Now this is all working fine, as long as the buttons are visible. However, when going to compact mode, I hide the buttons by setting their Button.Visible property. In that case, nothing happens. I've tried putting a try-catch block around the PerformClick, but no exception is thrown. Just nothing happens. Does anyone know how to make this work for hidden buttons?
PerformClick checks if button available for clicking before performing click. Hidden buttons are considered unavailable. You can simply show button just before performing click, and hide it after clicking:
cMenuStrip.Items.Add(
new ToolStripMenuItem(lbl, btn.BackgroundImage, (s, ea) => {
var size = btn.Size;
btn.Size = Size.Empty; // button still will be invisible
btn.Show(); // make it clickable
btn.PerformClick();
btn.Hide(); // hide again
btn.Size = size; // restore original size
});
NOTE: If you also need to add some visible buttons, then you should handle them separately to avoid flickering
cMenuStrip.Items.Add(new ToolStripMenuItem(
lbl, btn.BackgroundImage, (s, ea) => ClickButton(btn)));
Where ClickButton is a method which performs different logic depending whether button is visible or not:
private void ClickButton(Button button)
{
if (button.Visible)
{
button.PerformClick();
return;
}
var size = button.Size;
button.Size = Size.Empty;
button.Show();
button.PerformClick();
button.Hide();
button.Size = size;
}
The only thing you can't do is setting visible = false;
Other than that yu can use any trick to hide the buttons: You can stack them behind another control, you can move them out of sight in any direction or even move them into a different parent:
To hide them:
panel1.Size = Size.Empty;
button1.Parent = panel1;
//..
To show them again:
button1.Parent = this;
//..
Assuming they sit on the Form.
Note that they will have kept their original locations and sizes; watch for changes in the tab-order and z-order!
If the button is not visible the Click event won't be raised.
One option is to take the code which is in the button click event and add it as a separate method. Then call the method instead of the PerformClick row. This will work though if you do identical things on every button click.
Another options it to make the buttons transparent. This will make them invisible to the client and the PerformClick event will work fine. You can see that in the following link :Drawing a transparent button.
Hope this helps.

Defocusing TextBox by clicking it again

I'm making a settings form, where user can assign custom hotkeys for the application. There's a TextBox, and by clicking it with mouse, it focuses and waits for one keypress and then defocuses (by focusing another label):
private void txtKey_KeyDown(object sender, KeyEventArgs e)
{
e.SuppressKeyPress = true;
}
private void txtKey_PreviewKeyDown(object sender, PreviewKeyDownEventArgs e)
{
TextBox textBox = (TextBox)sender;
textBox.Text = e.KeyCode.ToString();
label1.Focus();
}
Is there a way to defocus focused TextBox (and cancel the key assinging process), by either clicking it again with mouse, or by clicking the GroupBox around it? I can't figure out how to check if TextBox was already focused when clicked (because when clicked, it gets focused before I can test if it's focused). Of course I can add a button "Cancel" next to the TextBox, but that's not what I want.
There is no Click-event for GroupBox, so I can't defocus TextBox by clicking GroupBox around it. Or can I somehow?
You can set/remove the Focus with
Keyboard.Focus = null;
You can also register to the following event:
public event MouseButtonEventHandler PreviewMouseLeftButtonDown
This event fires every time you click on the TextBox, thus you can set the Focus there if you want to.
For Winforms there is a way as well. I'm not proficient in it, but here would be a way:
Make a textBox (e.g. named textBoxFocus) that lies outside your window. Size it 1, 1 and move it to -10,-10 for example. Then you can register to the Click event and write
textBoxFocus.Focus();
It's a bit of a roundabout way, but should achieve what you want.
Thanks to private_meta for getting me to right direction (in comments)! I set the flag with click event, and before setting the flag, testing if flag is set. So first click does not find the flag, but second will. And flag is cleared within textbox Enter-event (which fires before Click-event). Now every other click focuses and every other defocuses textbox, as I wanted.
private void txtKey_Enter(object sender, EventArgs e)
{
TextBox textBox = (TextBox)sender;
textBox.Tag = null;
}
private void txtKey_Click(object sender, EventArgs e)
{
TextBox textBox = (TextBox)sender;
if (textBox.Tag != null) label1.Focus();
textBox.Tag = "clicked";
}
One of the simple way is that, you may use a bool flag here.
Algorithm:
By default, the bool value is 0;
If(Textbox Selected && flag = 0)
Do your task; and flag = 1;
I hope I could satisfy your query and you can follow this algorithm.

Tooltip on form load

I have a login form. The form has 2 textboxes, and 3 buttons. one button says "Students."
What I want to do is to show a tooltip on this button when the form opens. I dont want to have to go to the button and hover on it in order for it to display. I want the form to load, show the tooltip, and then the tooltip should disappear after 5 seconds. this is what I have tried so far:
private void Form1_Load(object sender, EventArgs e)
{
toolTip.IsBalloon = true;
toolTip.ToolTipIcon = ToolTipIcon.Info;
toolTip.ShowAlways = true;
toolTip.UseFading = true;
toolTip.UseAnimation = true;
toolTip.ToolTipTitle = "Student Mode";
toolTip.Show("You don't have to log in if you are a student. Just click here to go to the questions.", btnStudent);
}
The form's Load event is mis-used far too often. It is here, the event fires before the window becomes visible. Your tooltip is therefore not visible either.
Move your code to a Shown event handler instead. Do favor overriding OnShown() btw, it doesn't make sense for a class to listen to its own events.
protected override void OnShown(EventArgs e) {
base.OnShown(e);
// Your code here
//...
}

Categories