C# 无法对特定控件使用invoke

C# 无法对特定控件使用invoke,c#,winforms,C#,Winforms,我想创建一个禁用整个UserControl的方法。但是如果我禁用整个布局,它看起来非常糟糕(禁用的网格,标签看起来非常灰色)。所以我决定在布局中捕捉控件并手动禁用它们。但只有在禁用布局而不是控件时才调用方法。如何在UserControls中使用此方法 public static void Enable(this Control con, bool isEnable) { if (con != null)

我想创建一个禁用整个UserControl的方法。但是如果我禁用整个布局,它看起来非常糟糕(禁用的网格,标签看起来非常灰色)。所以我决定在布局中捕捉控件并手动禁用它们。但只有在禁用布局而不是控件时才调用方法。如何在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);
                            }

                        }
                }
            }

我发现的一个问题是,您在不使用Invoke和使用Invoke的情况下访问控件:

ctrl.Enabled=IsEnabled;
调用((MethodInvoker)(()=>ctrl.Enabled=isEnable));
由于
Invoke
是必需的,因为我们必须仅从创建它的线程访问控制句柄,如果我们不确定是否在该线程上运行,我们必须使用
invokererequired
,如果它返回
true
,我们必须使用
Invoke

为了涵盖这两种情况,我们可以这样考虑代码:

private static void InvokeControl(控件ctrl、操作Action)
{
如果(ctrl.InvokeRequired)
{
调用(()=>操作(ctrl));
}
其他的
{
动作(ctrl);
}
}
公共静态无效启用(此控件为con,bool为Enable)
{
如果(con==null)
{
回来
}
foreach(控件中的控件ctrl)
{    
如果(ctrl为TfSearchButton)
{
InvokeControl(ctrl,c=>c.Enabled=isEnable);
}
//…以类似的方式实施其余案例
}
}
使现代化 仔细考虑一下,在这种情况下,切换多个控件的启用状态在逻辑上是一种原子操作。在我上面建议的解决方案中,一次调用
Enable()
将导致大量上下文切换和线程阻塞。要使
Enable()
操作在技术上“更原子化”,最好完全在UI线程上运行
Enable()

publicstaticvoid启用(此控件为con,bool为Enable)
{
如果(con==null)
{
回来
}
if(con.invokererequired)//如果我们在UI线程上,则返回false
{
//如果我们不在UI线程上,则将一个新调用排队以启用()
//该调用将退出队列并由UI线程执行
con.BeginInvoke(()=>Enable(con,isEnable));
回来
}
//如果我们达到这一点,我们将在UI线程上运行
foreach(控件中的控件ctrl)
{    
//由于此代码始终在UI线程上运行,
//不需要使用Invoke/BeginInvoke
如果(ctrl为TfSearchButton)
{
ctrl.Enabled=IsEnabled;
}
//…其余的案子。。。
}
}

请注意:这是WinForms?我可以在您的代码中看到一个有问题的模式,但可以确定的是,观察到的错误行为是什么?基本上,我无法使用我的方法使我的textbox单选按钮或复选框enabled=false。但是我可以禁用我的整个布局。你的意思是,启用状态没有变化,没有引发异常,应用程序继续工作?是的,我在invoke方法中编写的代码只适用于布局,而不是布局中的控件。不知道为什么我不能禁用布局中的控件。重点是布局中有布局,所以我必须启用特定控件,这非常感谢实现多次调用动作。但只有当我使用begininvoke而不是invoke时,您的代码才能工作。这有什么意义。使用begininvoke更改代码是否安全?主要区别在于Invoke()使用SendMessage()API,该API会阻止消息,直到消息被UI线程处理为止。而BeginInvoke()使用PostMessage()API,它会立即返回。如果在使用Control.Invoke()时观察到死锁,则可能表明Enable()方法(或调用Invoke()的任何其他代码)在多个线程上并发运行。在这种情况下,切换到BeginInvoke()将解决死锁,但它不一定是正确的解决方案:您将从多个线程同时发送一组更新。我认为最好完全在UI线程中运行Enable(),即调用(()=>Enable(…),然后不需要InvokeControl()