Одна проблема, которую я нахожу, заключается в том, что вы получаете доступ к элементам управления как без Invoke, так и с Invoke:
ctrl.Enabled = isEnable;
ctrl.Invoke((MethodInvoker)(() => ctrl.Enabled = isEnable));
Поскольку требуется Invoke
, поскольку мы должны обращаться к дескриптору управления только из потока, который его создал, и еслимы не уверены, работаем ли мы в этом потоке, мы должны использовать InvokeRequired
, а если он возвращает true
, мы должны использовать Invoke
.
Чтобы охватить оба случая, мы можем разложить код следующим образом:
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
}
}
ОБНОВЛЕНИЕ
Еще немного подумать, переключив включенное состояние нескольких элементов управления в этом случаелогически это атомарная операция.В решении, которое я предложил выше, один вызов Enable()
приведет к многочисленным переключениям контекста и блокировке потоков.Чтобы технически сделать операцию Enable()
«более атомарной», лучше запускать Enable()
исключительно в потоке пользовательского интерфейса:
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 ...
}
}