using System.Runtime.InteropServices;
namespace System.Windows.Forms {
-#if NET_2_0
[ClassInterface (ClassInterfaceType.AutoDispatch)]
[ComVisible (true)]
-#endif
public class ContainerControl : ScrollableControl, IContainerControl {
private Control active_control;
private Control unvalidated_control;
+ private ArrayList pending_validation_chain;
// This is an internal hack that allows some container controls
// to not auto select their child when they are activated
internal bool auto_select_child = true;
-#if NET_2_0
private SizeF auto_scale_dimensions;
private AutoScaleMode auto_scale_mode;
private bool auto_scale_mode_set;
private bool auto_scale_pending;
private bool is_auto_scaling;
-#endif
internal bool validation_failed; //track whether validation was cancelled by a validating control
active_control = null;
unvalidated_control = null;
ControlRemoved += new ControlEventHandler(OnControlRemoved);
-#if NET_2_0
auto_scale_dimensions = SizeF.Empty;
auto_scale_mode = AutoScaleMode.Inherit;
-#endif
}
#endregion // Public Constructors
active_control = value;
// Generate the leave messages
- while (walk != common_ancestor) {
+ while (walk != common_ancestor && walk != null) {
if (walk == value) {
root = value;
fire_enter = false;
walk = walk.Parent;
}
- validation_failed = false;
- for (int i = 0; i < validation_chain.Count; i ++) {
- if (!ValidateControl ((Control)validation_chain[i])) {
- active_control = value = (Control)validation_chain[i];
- fire_enter = true;
- validation_failed = true;
+ // 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) {
+ while (walk != root && walk != null) {
chain.Add (walk);
walk = walk.Parent;
}
// 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
- SendControlFocus (active_control);
+ if (ctl.InternalContainsFocus)
+ SendControlFocus (active_control);
}
}
+ // Return the control where validation failed, and null otherwise
+ // @topmost_under_root is the control under the root in the enter chain, if any
+ //
+ // The process of validation happens as described:
+ //
+ // 1. Iterate over the nodes in the enter chain (walk down), looking for any node
+ // causing validation. If we can't find any, don't validate the current validation chain, but postpone it,
+ // saving it in the top_container.pending_validation_chain field, since we need to keep track of it later.
+ // If we have a previous pending_validation_chain, add the new nodes, making sure they are not repeated
+ // (this is computed in ActiveControl and we receive if as the postpone_validation parameter).
+ //
+ // 2. If we found at least one node causing validation in the enter chain, try to validate the elements
+ // in pending_validation_chain, if any. Then continue with the ones receives as parameters.
+ //
+ // 3. Return null if all the validation performed successfully, and return the control where the validation
+ // failed otherwise.
+ //
+ private Control PerformValidation (ContainerControl top_container, bool postpone_validation, ArrayList validation_chain,
+ Control topmost_under_root)
+ {
+ validation_failed = false;
+
+ if (postpone_validation) {
+ AddValidationChain (top_container, validation_chain);
+ return null;
+ }
+
+ // if not null, pending chain has always one element or more
+ if (top_container.pending_validation_chain != null) {
+ // if the topmost node in the enter chain is exactly the topmost
+ // int the validation chain, remove it, as .net does
+ int last_idx = top_container.pending_validation_chain.Count - 1;
+ if (topmost_under_root == top_container.pending_validation_chain [last_idx])
+ top_container.pending_validation_chain.RemoveAt (last_idx);
+
+ AddValidationChain (top_container, validation_chain);
+ validation_chain = top_container.pending_validation_chain;
+ top_container.pending_validation_chain = null;
+ }
+
+ for (int i = 0; i < validation_chain.Count; i ++) {
+ if (!ValidateControl ((Control)validation_chain[i])) {
+ validation_failed = true;
+ return (Control)validation_chain[i];
+ }
+ }
+
+ return null;
+ }
+
+ // Add the elements in validation_chain to the pending validation chain stored in top_container
+ private void AddValidationChain (ContainerControl top_container, ArrayList validation_chain)
+ {
+ if (validation_chain.Count == 0)
+ return;
+
+ if (top_container.pending_validation_chain == null || top_container.pending_validation_chain.Count == 0) {
+ top_container.pending_validation_chain = validation_chain;
+ return;
+ }
+
+ foreach (Control c in validation_chain)
+ if (!top_container.pending_validation_chain.Contains (c))
+ top_container.pending_validation_chain.Add (c);
+ }
+
private bool ValidateControl (Control c)
{
CancelEventArgs e = new CancelEventArgs ();
}
}
-#if NET_2_0
[Browsable (false)]
[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
[EditorBrowsable (EditorBrowsableState.Advanced)]
}
}
}
-#endif // NET_2_0
[Browsable (false)]
public override BindingContext BindingContext {
}
}
-#if NET_2_0
[Browsable (false)]
[EditorBrowsable (EditorBrowsableState.Advanced)]
public SizeF CurrentAutoScaleDimensions {
return auto_scale_dimensions;
}
}
-#endif
[Browsable (false)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
#endregion // Public Instance Properties
#region Protected Instance Methods
+ protected override bool CanEnableIme {
+ get { return false; }
+ }
protected override CreateParams CreateParams {
get {
return base.CreateParams;
#endregion // Public Instance Methods
#region Public Instance Methods
-#if NET_2_0
internal void PerformAutoScale (bool called_by_scale)
{
if ((AutoScaleMode == AutoScaleMode.Inherit) && !called_by_scale)
internal bool IsAutoScaling {
get { return is_auto_scaling; }
}
-#endif
- [MonoTODO]
+ [MonoTODO ("Stub, not implemented")]
static bool ValidateWarned;
public bool Validate() {
//throw new NotImplementedException();
return true;
}
-#if NET_2_0
public bool Validate (bool checkAutoValidate)
{
if ((checkAutoValidate && (AutoValidate != AutoValidate.Disable)) || !checkAutoValidate)
return true;
}
-#endif
bool IContainerControl.ActivateControl(Control control) {
return Select(control);
OnBindingContextChanged (EventArgs.Empty);
}
-#if NET_2_0
protected override bool ProcessCmdKey (ref Message msg, Keys keyData)
{
if (ToolStripManager.ProcessCmdKey (ref msg, keyData) == true)
return base.ProcessCmdKey (ref msg, keyData);
}
-#endif
[EditorBrowsable (EditorBrowsableState.Advanced)]
protected override bool ProcessDialogChar(char charCode) {
wrapped = false;
c = active_control;
-#if NET_2_0
- System.Collections.Generic.List<MenuStrip> strips = new System.Collections.Generic.List<MenuStrip> ();
-#endif
-
do {
c = GetNextControl(c, true);
-#if NET_2_0
- if (c is MenuStrip)
- strips.Add ((MenuStrip)c);
-#endif
if (c != null) {
// This is stupid. I want to be able to call c.ProcessMnemonic directly
if (c.ProcessControlMnemonic(charCode)) {
}
} while (c != active_control);
-#if NET_2_0
- // No one has an explicit mnemonic for this key.
- // Let MenuStrips have a chance at implicit mnemonics.
- foreach (MenuStrip ms in strips)
- if (ms.ProcessImplicitMnemonic (charCode))
- return true;
-#endif
-
return false;
}
Select (active_control);
else
base.WndProc (ref m);
-#if false
- else
- SelectNextControl (null, true, true, true, false);
-#endif
break;
default:
#region Internal Methods
internal void ChildControlRemoved (Control control)
{
+ ContainerControl top_container = FindForm ();
+ if (top_container == null)
+ top_container = this;
+
+ // Remove controls -as well as any sub control- that was in the pending validation chain
+ ArrayList pending_validation_chain = top_container.pending_validation_chain;
+ if (pending_validation_chain != null) {
+ RemoveChildrenFromValidation (pending_validation_chain, control);
+
+ if (pending_validation_chain.Count == 0)
+ top_container.pending_validation_chain = null;
+ }
+
if (control == active_control || control.Contains (active_control)) {
SelectNextControl (this, true, true, true, true);
if (control == active_control || control.Contains (active_control)) {
}
}
}
+
+ // Check that this control (or any child) is included in the pending validation chain
+ bool RemoveChildrenFromValidation (ArrayList validation_chain, Control c)
+ {
+ if (RemoveFromValidationChain (validation_chain, c))
+ return true;
+
+ foreach (Control child in c.Controls)
+ if (RemoveChildrenFromValidation (validation_chain, child))
+ return true;
+
+ return false;
+ }
+
+ // Remove the top most control in the pending validation chain, as well as any children there,
+ // taking advantage of the fact that the chain is in reverse order of the control's hierarchy
+ bool RemoveFromValidationChain (ArrayList validation_chain, Control c)
+ {
+ int idx = validation_chain.IndexOf (c);
+ if (idx > -1) {
+ pending_validation_chain.RemoveAt (idx--);
+ return true;
+ }
+
+ return false;
+ }
+
internal virtual void CheckAcceptButton()
{
// do nothing here, only called if it is a Form
}
-
-#if NET_2_0
private bool ValidateNestedControls (Control c, ValidationConstraints constraints, bool recurse)
-
-#else
- // Used by Form to validate its children on the 1.1 Profile
- internal bool ValidateNestedControls (Control c, bool recurse)
-#endif
{
bool validate_result = true;
if (!c.CausesValidation)
validate_result = true;
-#if NET_2_0
else if (!ValidateThisControl (c, constraints))
validate_result = true;
-#endif
else if (!ValidateControl (c))
validate_result = false;
if (recurse)
- foreach (Control control in c.Controls) {
- if (!ValidateNestedControls (control,
-#if NET_2_0
- constraints,
-#endif
- recurse))
- return false;
- }
+ foreach (Control control in c.Controls)
+ if (!ValidateNestedControls (control, constraints, recurse))
+ return false;
return validate_result;
}
-#if NET_2_0
+
private bool ValidateThisControl (Control c, ValidationConstraints constraints)
{
if (constraints == ValidationConstraints.None)
return true;
}
-#endif
#endregion // Internal Methods
-#if NET_2_0
protected override void OnParentChanged (EventArgs e)
{
base.OnParentChanged (e);
add { Events.AddHandler (OnValidateChanged, value); }
remove { Events.RemoveHandler (OnValidateChanged, value); }
}
-#endif
}
}