internal void SetActiveControl(Control value)
{
using (_ = new Focusing())
{
if (active_control == value && (value == null || value.Focused))
{
return;
}
if (value != null && !Contains(value))
{
throw new ArgumentException("Cannot activate invisible or disabled control.");
}
if (value == null)
{
Control previous_active_control = active_control;
active_control = null;
using (_ = new Focusing(false))
previous_active_control?.FireLeave();
return;
}
// Fire the enter and leave events if possible
Form form = FindForm();
Control active = GetMostDeeplyNestedActiveControl(form == null ? this : form);
Control common_ancestor = GetCommonContainer(active, value);
ArrayList chain = new ArrayList();
ArrayList validation_chain = new ArrayList();
Control walk = active;
// we split this up into three steps:
// 1. walk up the tree (if we need to) to our root, firing leave events.
// 2. validate.
// 3. walk down the tree (if we need to), firing enter events.
// "our root" is either the common ancestor of the current active
// control and the new active control, or the current active control,
// or the new active control. That is, it's either one of these three
// configurations:
// (1) root (2) new (3) current
// / \ / \ / \
// ... ... ... ... ... ...
// / \ / \
// current new current new
// note (3) doesn't require any upward walking, and no leave events are generated.
// (2) doesn't require any downward walking, and no enter events are generated.
// as we walk up the tree, we generate a list of all controls which cause
// validation. After firing the leave events, we invoke (in order starting from
// the most deeply nested) their Validating event. If one sets CancelEventArgs.Cancel
// to true, we ignore the control the user wanted to set ActiveControl to, and use
// the Validating control instead.
bool fire_enter = true;
Control root = common_ancestor;
active_control = value;
// Generate the leave messages
while (walk != common_ancestor && walk != null)
{
if (walk == value)
{
root = value;
fire_enter = false;
break;
}
using (_ = new Focusing(false))
walk.FireLeave();
/* clear our idea of the active control as we go back up */
if (walk is ContainerControl)
{
((ContainerControl)walk).active_control = null;
}
if (walk.CausesValidation)
{
validation_chain.Add(walk);
}
walk = walk.Parent;
}
// Validation can be postponed due to all the controls
// in the enter chain not causing validation. If we don't have any
// enter chain, it means that the selected control is a child and then
// we need to validate the controls anyway
bool postpone_validation;
Control topmost_under_root = null; // topmost under root, in the *enter* chain
if (value == root)
{
postpone_validation = false;
}
else
{
postpone_validation = true;
walk = value;
while (walk != root && walk != null)
{
if (walk.CausesValidation)
{
postpone_validation = false;
}
topmost_under_root = walk;
walk = walk.Parent;
}
}
Control failed_validation_control = PerformValidation(form == null ? this : form, postpone_validation,
validation_chain, topmost_under_root);
if (failed_validation_control != null)
{
active_control = value = failed_validation_control;
fire_enter = true;
}
if (fire_enter)
{
walk = value;
while (walk != root && walk != null)
{
chain.Add(walk);
walk = walk.Parent;
}
if (root != null && walk == root && !(root is ContainerControl))
{
chain.Add(walk);
}
for (int i = chain.Count - 1; i >= 0; i--)
{
walk = (Control)chain [i];
using (_ = new Focusing(false))
walk.FireEnter();
}
}
walk = this;
Control ctl = this;
while (walk != null)
{
if (walk.Parent is ContainerControl)
{
((ContainerControl)walk.Parent).active_control = ctl;
ctl = walk.Parent;
}
walk = walk.Parent;
}
if (this is Form)
{
CheckAcceptButton();
}
// Scroll control into view
ScrollControlIntoView(active_control);
walk = this;
ctl = this;
while (walk != null)
{
if (walk.Parent is ContainerControl)
{
ctl = walk.Parent;
}
walk = walk.Parent;
}
// Let the control know it's selected
if (ctl.ContainsFocus && active_control != null &&
(!(active_control is UserControl) || (active_control is UserControl && !((UserControl)active_control).HasFocusableChild())))
{
SendControlFocus(active_control);
}
}
}