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.
Related
I get this very annoying border when pressing tab and the clicking the button
I've tried
foreach (Control x in this.Controls)
{
if (x is Button)
{
Button newbut = (Button)x;
newbut.FlatStyle = FlatStyle.Flat;
newbut.FlatAppearance.BorderColor = Color.FromArgb(0, 255, 255, 255);
newbut.FlatAppearance.BorderSize = 0;
newbut.TabStop = false;
}
}
And I've also tried doing adding an event on Keydown and not allowing to press the tab key but nothing has worked so far.
this is example of the border that keeps popping up
Fixed by generating own button class and editing the designer.cs file
If you do not want custom buttons, you can shift focus to another control once finished, this prevents the rectangle from showing. You could place a dummy control out of view and shift fucus to this.
private void btnDoSomething_Click(object sender, EventArgs e)
{
myDummyControl.Focus();
MessageBox.Show("This still executes even though we have lost focus");
// If you are decoupling your code from the UI, this becomes second nature quickly as it becomes part of your control handling.
}
Also you can stop the control from getting Tab focus my setting it's TabStop property tp false, do this in the buttons Paint event.
If you have many buttons, you can just point them to the same event handler (On_Paint).
private void On_Paint(object sender, PaintEventArgs e)
{
Button thisButton = sender as Button;
thisButton.TabStop = false;
}
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
As the title suggests i am having trouble getting a DragOver event to function correctly. I have over 100 buttons on a form and i want their colour to change when a picturebox is dragged over them. I have set all buttons AllowDrop = true and have included the code below in the method.
private void ShipOver(object sender, DragEventArgs e)
{
e.Effect = DragDropEffects.None;
Button b = (Button)sender;
b.BackColor = Color.Green;
label22.Text = "";
}
I do not see why this will not work. I also have a DragLeave method which simply changes the colour to a different one.
One thing to note is that the item i am dragging over the button is larger than the button itself. Not sure whether this will have an effect.
You need to wire up the events to your method. If all of the buttons are in a single panel, you can do something like this in your form's constructor:
foreach (Button b in panel1.Controls.OfType<Button>()) {
b.DragOver += ShipOver;
}
Same principle applies to the DragLeave event.
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.
I have a button and on click of that i show a popup which has a listbox.
popup named - popComboList
Listbox named - lstComboBoxResult
I am giving a focus to a listbox but at initial on a click of a button the listbox doesn't get focus-(this happens only once at initial, when i first time click button) After the second click it works.
private void bnOpen_Click(object sender, RoutedEventArgs e)
{
if (IsDesignTime)
return;
lstComboBoxResult.Width = tbComboValue.ActualWidth + bnOpen.ActualWidth;
if (!popComboList.IsOpen)
{
SetPopupPosition(popComboList);
popComboList.IsOpen = true;
lstComboBoxResult.Focus();
}
else
{
popComboList.IsOpen = false;
}
}
This is a bit of a guess, but try calling UpdateLayout() after opening the pop-up, but before calling Focus(). It's possible that the listbox is not fully initialized and therefore unable to accept focus until it has become visible for the first time.