-1

Background Information

My main Form instance is called 'MainView' and contains the following:

  • A panel called contentPanel that contains various input controls (including childControl, which starts off disabled).
  • A BackgroundWorker control.

backgroundWorker_DoWork(3-arg) is called when the Submit button is clicked. contentPanel is disabled so the user can't make changes, spam buttons, etc while their submission is being processed. If the submission is successful certain UI changes need to also occur (more on this below in the Problem section).

contentPanel relevant code

private void btnSubmit_Click(object sender, EventArgs e)
{
    ...

    Type[] parameterTypes = new Type[] { suiteRel.GetType() };

    MethodInfo method = this.GetType().GetMethod("btnSubmit_Click", parameterTypes);
    mainView.backgroundWorker_DoWork(this, method, myData);
}

/// <summary>
/// Commits changes to the database.
/// 
/// Note: This method will be executed from a non-UI thread.  As such 
/// accessing or modifying UI controls must be done via an Invoke() call.
/// </summary>
public void btnSubmit_Click(Object myData)
{
    ...

    if (success)
    {
        this.Invoke(new Action(
            () => {
                childControl.Enabled = true;
            }
        ));
    }
}

BackgroundWorker relevant code

/// <summary>
/// Starts another thread to perform a potentially long-running task (e.g. validation, database commits, etc).
/// </summary>
/// <param name="reference">
/// A reference to the control instance that owns the method to be called.
/// </param>
/// <param name="method">
/// The method to be called that will perform the work.
/// </param>
/// <param name="parameters">
/// Parameters to pass to the method when it is called.
/// </param>
public void backgroundWorker_DoWork(object reference, MethodInfo method, params object[] parameters)
{
    List<object> methodAndArgs = new List<object>();
    methodAndArgs.Add(reference);
    methodAndArgs.Add(method);
    methodAndArgs.Add(parameters);

    contentPanel.Enabled = false;
    this.UseWaitCursor = true;
    backgroundWorker.RunWorkerAsync(methodAndArgs);
}

/// <summary>
/// Starting point of a worker thread (i.e. not on the UI thread).
/// </summary>
/// <param name="sender">
/// The object that initiated the work request.
/// </param>
/// <param name="e">
/// The arguments that dictate what work to do.  In particular, e.Argument 
/// contains the parameters passed to the public backgroundWorker_DoWork().
/// </param>
private void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
    List<object> methodAndArgs = (List<object>)e.Argument;
    object reference = methodAndArgs[0];
    MethodInfo method = (MethodInfo)methodAndArgs[1];
    object[] parameters = (object[])methodAndArgs[2];

    // call method with its parameters.
    // a.k.a. "method(param1, param2, ...);"
    method.Invoke(reference, parameters);
}

/// <summary>
/// Called on the UI thread after backgroundWorker_DoWork() completes.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
    contentPanel.Enabled = true;
    this.UseWaitCursor = false;
}

Problem

When 'method' is executed in backgroundWorker_DoWork(2-arg), I set the Enabled property for a child control within contentPanel to true. The intention is that the child control will be enabled when contentPanel is enabled when backgroundWorker_RunWorkerCompleted() is called. However, the child control remains disabled after backgroundWorker_RunWorkerCompleted() is called.

I understand that it is not possible to have a child control that is enabled while its parent is disabled (e.g. what this question is asking for), and this is not what I'm trying to do. I want the child control to be enabled when its parent (contentPanel) is enabled (similar to how other child controls that were enabled before the Submit button was clicked are re-enabled when contentPanel is enabled in backgroundWorker_RunWorkerCompleted()).

Community
  • 1
  • 1
Erik W
  • 1
  • 3
  • 1
    That's some weird spaghetti code you have, hard to figure out why you are doing what you are doing. I guess the simple question is, is `success` ever true? You didn't document that variable very well. – LarsTech Sep 23 '15 at 21:17
  • @LarsTech Yes, `success` is usually true. I've use breakpoints to verify that `childControl.Enabled = true;` does get executed. I've chosen this architecture because (1) I have a long-running submission process that when executed on the UI thread causes the app to hang until submission completes, and (2) I have multiple panels in my app that all need to behave the same on submission. – Erik W Sep 24 '15 at 11:55

2 Answers2

0

Enabled is an ambient property. So if you set a child control Enabled = false, then even if you set the parent control Enabled = true you will still need to do the same for the child control if it was explicitly set at some point.

To have one control become enabled when another control becomes enabled, then use the EnableChanged event. E.g.

Control c1 = ...;
Control c2 = ...;
c1.EnabledChanged += delegate {
    if (c1.Enabled)
        c2.Enabled = true;
};

An ambient property is a control property that, if not set, is retrieved from the parent control. For example, a Button will have the same BackColor as its parent Form by default. For more information about ambient properties, see the AmbientProperties class or the Control class overview. https://msdn.microsoft.com/en-us/library/system.windows.forms.control.font%28v=vs.110%29.aspx

Loathing
  • 5,109
  • 3
  • 24
  • 35
  • I'm not sure I understand. You mentioned that `Enabled` is an ambient property, can you explain in more detail? I've never heard of ambient properties and can't find much documentation. – Erik W Sep 24 '15 at 12:39
  • Also, to be clear I am setting `childControl.Enabled = true;`, but it isn't being respected when the parent (`contentPanel`) is enabled (e.g. `childControl` remains disabled). Thanks for your suggestion about the EnabledChanged event. – Erik W Sep 24 '15 at 12:45
  • @ErikW Set a breakpoint and debug your code. It's likely you are setting the `Enabled` on a different control, or something else that is easy to fix. Edited the answer with more info on ambient properties. – Loathing Sep 24 '15 at 16:00
0

I discovered that some code buried in a call made from btnSubmit_Click() was disabling childControl. The call stack looks something like this:

btnSubmit_Click()
combobox_SelectionChangeCommitted()
clearForm()
setEnabled()

setEnabled() then sets the Enabled property of childControl to the Enabled property of combobox. Since contentPanel is disabled, combobox also reports as disabled (its Enabled property is set to true, but retrieving the Enabled property value returns false since contentPanel is disabled), which results in childControl.Enabled being assigned false.

private void setEnabled(bool enabled)
{
    ...
    combobox.Enabled = enabled;
    childControl.Enabled = combobox.Enabled;
    ...
}

Thank you to @Loathing for your expertise.

Erik W
  • 1
  • 3