Reputation: 491
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
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
}
}
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