Search code examples
c#winformsbuttoncontextmenu

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?


Solution

  • 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;
    }