Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Control.PerformClick() isn't doing anything, what am I missing?

Tags:

c#

winforms

I've reviewed the MSDN doc and a couple SO answers, and all signs point to this working. At this point, I think I've either completely misunderstood what to expect or I've missed one line of code I need.

In short, I've got a WinForms app with a button, and I want another function to "click" that button at one point in the code. Here's the relevant bits:

// form.Designer.cs

this.btnAddBranch.Click += new System.EventHandler(this.btn_add_Click);

// form.cs

// using statements
public partial class EditClient : Form
{
   // ...
   public TestClick()
   {
      //btnAddBranch.PerformClick(); <-- would like to know why this fails ...
      btn_add_Click(this, EventArgs.Empty);
   }
   private void btn_add_Click(object sender, EventArgs e)
   {
      MessageBox.Show("You clicked it!");
   }
 }

The commented line for btnAddBranch.PerformClick() is what I was hoping would do the equivalent of the line below it. But it doesn't, it doesn't seem to do anything when TestClick() is called. If I do the uncommented line, it works fine.

Am I missing something, or am I totally misunderstanding something?

like image 445
Nathan Loding Avatar asked Jun 17 '13 19:06

Nathan Loding


2 Answers

Your problem is that TestClick() is your form constructor. There are no Controls to call PerformClick() on until the Form Constructor is complete. If you really want to call the code that early then do something like the following.

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
        //Do not call methods on controls here, the controls are not yet initialized
    }

    private void TestClick()
    {
        btn_add.PerformClick();
    }

    private void btn_add_Click(object sender, EventArgs e)
    {
        MessageBox.Show("You Clicked it");
    }

    private void Form1_Load(object sender, EventArgs e)
    {
        TestClick();
    }
}

Calling your PerformClick() anywhere other than the form constructor will create the desired results.

like image 158
Evan L Avatar answered Nov 15 '22 22:11

Evan L


Sorry, I've updated my answer to correct it. I initially thought it was because you were not calling Button.PerformClick() after Form.InitializeComponent() (from the Form.Designer.cs auto-generated code), but I was corrected that this still does not work.

It seems that the Form is not sufficiently created in the constructor to allow Button.PerformClick(). I theorized that this may due to the fact that the Modal message loop wasn't fully created yet, but after looking at Button.PerformClick's code in Reflector, that doesn't seem to be quite the case.

PerformClick's code looks like this:

public void PerformClick()
{
    if (base.CanSelect)
    {
        bool flag;
        bool flag2 = base.ValidateActiveControl(out flag);
        if (!base.ValidationCancelled && (flag2 || flag))
        {
            base.ResetFlagsandPaint();
            this.OnClick(EventArgs.Empty);
        }
    }
}

While looking through, the first failure I notice here is CanSelect will return false because the control is not currently Visible (ShowDialog has not yet been called). Therefore, PerformClick will do nothing as observed. This is by digging down through the CanSelect implementation:

internal virtual bool CanSelectCore()
{
    if ((this.controlStyle & ControlStyles.Selectable) != ControlStyles.Selectable)
    {
        return false;
    }
    for (Control control = this; control != null; control = control.parent)
    {
        if (!control.Enabled || !control.Visible)
        {
            return false;
        }
    }
    return true;
}

In the debugger, you can put a breakpoint in the constructor and see that Button1 will not yet be visible (makes sense).

However, I will suggest that you can accomplish what you want from the constructor, by separating your application logic from the Button's event handler. For example...

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();

        DoSomething();
    }

    private void DoSomething()
    {
        // application logic here... 
        MessageBox.Show("Hello World");
    }

    private void button1_Click(object sender, EventArgs e)
    {
        DoSomething();
    }
}

Or, as the previous answer suggests you can call Button.PerformClick() from the Form.OnLoad method. However, it is probably better to just call the application logic directly from both spots instead of performing button clicks in the UI.

Sorry for the initially incorrect answer. Hope this helps explain.

like image 24
Alan Avatar answered Nov 15 '22 23:11

Alan