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;
-#endif
+ private bool is_auto_scaling;
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.None;
-#endif
+ auto_scale_mode = AutoScaleMode.Inherit;
}
#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)]
return auto_scale_mode;
}
set {
+ if (this is Form)
+ (this as Form).AutoScale = false;
+
if (auto_scale_mode != value) {
auto_scale_mode = value;
PerformAutoScale ();
}
-
- if (this is Form)
- (this as Form).AutoScale = false;
}
}
-#endif // NET_2_0
[Browsable (false)]
public override BindingContext BindingContext {
}
}
-#if NET_2_0
[Browsable (false)]
[EditorBrowsable (EditorBrowsableState.Advanced)]
public SizeF CurrentAutoScaleDimensions {
case AutoScaleMode.Font:
Size s = TextRenderer.MeasureText ("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890", Font);
- int width = (int)((float)s.Width / 62f);
+ int width = (int)Math.Round ((float)s.Width / 62f);
return new SizeF (width, s.Height);
}
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
- public void PerformAutoScale ()
+ internal void PerformAutoScale (bool called_by_scale)
{
- if (layout_suspended > 0) {
+ if ((AutoScaleMode == AutoScaleMode.Inherit) && !called_by_scale)
+ return;
+
+ if ((layout_suspended > 0) && !called_by_scale) {
auto_scale_pending = true;
return;
}
// PerformDelayedAutoScale after ResumeLayout
auto_scale_pending = false;
- if (AutoScaleFactor != new SizeF(1F, 1F)) {
+ SizeF factor = AutoScaleFactor;
+ if (AutoScaleMode == AutoScaleMode.Inherit) {
+ ContainerControl cc = FindContainer (this.Parent);
+ if (cc != null)
+ factor = cc.AutoScaleFactor;
+ }
+ if (factor != new SizeF (1F, 1F)) {
+ is_auto_scaling = true;
SuspendLayout ();
- Scale (AutoScaleFactor);
+ Scale (factor);
ResumeLayout (false);
+ is_auto_scaling = false;
}
auto_scale_dimensions = CurrentAutoScaleDimensions;
}
+ public void PerformAutoScale ()
+ {
+ PerformAutoScale (false);
+ }
+
internal void PerformDelayedAutoScale ()
{
if (auto_scale_pending)
PerformAutoScale ();
}
-#endif
- [MonoTODO]
+ internal bool IsAutoScaling {
+ get { return is_auto_scaling; }
+ }
+
+ [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) {
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)
{
bool validate_result = true;
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
}
}