Arda Nalbant
Arda Nalbant

Reputation: 491

Cannot use invoke for specific Control

I want to create a method that disable the whole UserControl. But if i a disable the whole Layout it seems very bad(disabled grids, labes looks so gray). So i decided catch controls in layout and disable them manually. but invoke method only if i disable the layout not for controls. How can this method in UserControls ?

   public static void Enable(this Control con, bool isEnable)
            {
                if (con != null)
                {
                        foreach (Control ctrl in con.Controls)
                        {    
                            var a = ctrl.GetType().Name;
                            var b = ctrl.Name;

                            if (ctrl is TfSearchButton)
                            {
                                ctrl.Enabled = isEnable;
                                ctrl.Invoke((MethodInvoker)(() => ctrl.Enabled = isEnable));
                            }
                            if (ctrl is TfNumericEdit)
                            {
                                ctrl.Enabled = isEnable;
                                ctrl.Invoke((MethodInvoker)(() => ctrl.Enabled = isEnable));
                            }
                            if (ctrl is TfCheckEdit)
                            {
                                ctrl.Enabled = isEnable;
                                ctrl.Invoke((MethodInvoker)(() => ctrl.Enabled = isEnable));
                            }
                            if (ctrl is TfComboEdit)
                            {
                                ctrl.Enabled = isEnable;
                                //ctrl.Invoke((MethodInvoker)(() => ctrl.Enabled = isEnable));
                            }
                            if (ctrl is TfTextEdit)
                            {
                                ctrl.Enabled = isEnable;
                                ctrl.Invoke((MethodInvoker)(() => ctrl.Enabled = isEnable));
                            }
                            if (ctrl is TfRadioGroup)
                            {
                                ctrl.Enabled = isEnable;
                                ctrl.Invoke((MethodInvoker)(() => ctrl.Enabled = isEnable));
                            }
                            if (ctrl is TfDateEdit)
                            {
                                ctrl.Enabled = isEnable;
                                ctrl.Invoke((MethodInvoker)(() => ctrl.Enabled = isEnable));
                            }
                            if (ctrl is TfLookUpEdit)
                            {
                                ctrl.Enabled = isEnable;
                                ctrl.Invoke((MethodInvoker)(() => ctrl.Enabled = isEnable));
                            }
                            if (ctrl is TfGrid)
                            {
                                GridView view = ((TfGrid)ctrl).MainView as GridView;
                                view.OptionsBehavior.Editable = isEnable;
                                ctrl.Invoke((MethodInvoker)(() => view.OptionsBehavior.Editable = isEnable));
                            }
                            if (ctrl.GetType().BaseType.Name is "TfControlBase" || ctrl is TfLayoutControl)
                            {
                                Enable(ctrl, isEnable);
                            }

                        }
                }
            }

Upvotes: 0

Views: 107

Answers (1)

felix-b
felix-b

Reputation: 8498

One problem I find is that you access controls both without Invoke and with Invoke:

ctrl.Enabled = isEnable;
ctrl.Invoke((MethodInvoker)(() => ctrl.Enabled = isEnable));

Since Invoke is required because we must access control handle only from the thread that created it, and if we're not sure whether we're running on that thread, we must use InvokeRequired and if it returns true, we must use Invoke.

To cover both cases, we can factor the code like this:

private static void InvokeControl(Control ctrl, Action<Control> action)
{
    if (ctrl.InvokeRequired)
    {
        ctrl.Invoke(() => action(ctrl));
    }
    else
    {
        action(ctrl);
    }
}

public static void Enable(this Control con, bool isEnable)
{
    if (con == null)
    {
        return;
    }
    foreach (Control ctrl in con.Controls)
    {    
        if (ctrl is TfSearchButton)
        {
            InvokeControl(ctrl, c => c.Enabled = isEnable);
        }
        //... implement the rest of the cases in similar way
    }
}

UPDATE

Giving it some more thought, switching enabled state of multiple controls in this case is, logically, an atomic operation. In solution I suggested above, one call to Enable() would result in numerous context switches and thread blocking. To make the Enable() operation "a more atomic" technically, it's better to run Enable() entirely on the UI thread:

public static void Enable(this Control con, bool isEnable)
{
    if (con == null)
    {
        return;
    }

    if (con.InvokeRequired) // returns false if we're on the UI thread
    {
        // if we're not on the UI thread, enqueue a new call to Enable()
        // the call will be dequeued and executed by the UI thread
        con.BeginInvoke(() => Enable(con, isEnable));
        return; 
    }

    // if we got to this point, we're running on the UI thread

    foreach (Control ctrl in con.Controls)
    {    
        // since this code always runs on UI thread,
        // there is no need to use Invoke/BeginInvoke

        if (ctrl is TfSearchButton)
        {
            ctrl.Enabled = isEnable;
        }
        // ... the rest of the cases ...
    }
}

Upvotes: 1

Related Questions