//
-// NOT COMPLETE
-
using System.Collections;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.Drawing;
+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 focused_control;
private Control unvalidated_control;
+
+ // 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;
#region Public Constructors
public ContainerControl() {
active_control = null;
- focused_control = null;
unvalidated_control = null;
ControlRemoved += new ControlEventHandler(OnControlRemoved);
#if NET_2_0
}
set {
- if ((active_control==value) || (value==null)) {
+ if (value==null || (active_control == value)) {
return;
}
-
- if (!Contains(value) && this != value) {
- throw new ArgumentException("Not a child control");
- }
-
- if (value == this) {
-
+ if (!Contains(value)) {
+ throw new ArgumentException("Cannot activate invisible or disabled control.");
}
// Fire the enter and leave events if possible
Form form = FindForm ();
if (form != null) {
- Control active = form.ActiveControl;
- Control common_container = GetCommonContainer (form.ActiveControl, value);
+ Control active = GetMostDeeplyNestedActiveControl (form);
+ Control common_container = GetCommonContainer (active, value);
ArrayList chain = new ArrayList ();
+ ArrayList validation_chain = new ArrayList ();
Control walk = active;
- // Generate the leave messages
+ // 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 container 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_container;
+
+ // Generate the leave messages
while (walk != common_container) {
+ if (walk == value) {
+ root = value;
+ fire_enter = false;
+ break;
+ }
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;
}
- walk = value;
- while (walk != common_container) {
- chain.Add (walk);
- walk = walk.Parent;
+ for (int i = 0; i < validation_chain.Count; i ++) {
+ if (!ValidateControl ((Control)validation_chain[i])) {
+ value = (Control)validation_chain[i];
+ fire_enter = true;
+ }
}
- for (int i = chain.Count - 1; i >= 0; i--) {
- walk = (Control) chain [i];
- walk.FireEnter ();
+ if (fire_enter) {
+ walk = value;
+ while (walk != root) {
+ chain.Add (walk);
+ walk = walk.Parent;
+ }
+
+ for (int i = chain.Count - 1; i >= 0; i--) {
+ walk = (Control) chain [i];
+ walk.FireEnter ();
+ }
+ }
+
+ walk = this;
+ while (walk != null) {
+ if (walk.Parent is ContainerControl)
+ ((ContainerControl)walk.Parent).active_control = walk;
+ walk = walk.Parent;
}
}
active_control = value;
-
if (this is Form)
CheckAcceptButton();
ScrollControlIntoView(active_control);
// Let the control know it's selected
-// value.is_selected = true;
-// if (value.IsHandleCreated) {
-// XplatUI.SetFocus (value.window.Handle);
-// }
+ SendControlFocus (value);
+ }
+ }
- SelectChild (value);
+ private bool ValidateControl (Control c)
+ {
+ CancelEventArgs e = new CancelEventArgs ();
+
+ c.FireValidating (e);
+
+ if (e.Cancel)
+ return false;
+
+ c.FireValidated ();
+ return true;
+ }
+
+ private Control GetMostDeeplyNestedActiveControl (ContainerControl container)
+ {
+ Control active = container.ActiveControl;
+ while (active is ContainerControl) {
+ if (((ContainerControl)active).ActiveControl == null)
+ break;
+ active = ((ContainerControl)active).ActiveControl;
}
+ return active;
}
// Just in a separate method to make debugging a little easier,
return null;
}
+ internal void SendControlFocus (Control c)
+ {
+ if (c.IsHandleCreated) {
+ XplatUI.SetFocus (c.window.Handle);
+ }
+ }
+
#if NET_2_0
+ [Browsable (false)]
+ [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
+ [EditorBrowsable (EditorBrowsableState.Advanced)]
+ [Localizable (true)]
public SizeF AutoScaleDimensions {
get {
return auto_scale_dimensions;
}
}
- public SizeF AutoScaleFactor {
+ protected SizeF AutoScaleFactor {
get {
if (auto_scale_dimensions.IsEmpty) {
return new SizeF(1f, 1f);
}
- return new SizeF(CurrentAutoScaleDimensions.Width / auto_scale_dimensions.Width,
+ return new SizeF(CurrentAutoScaleDimensions.Width / auto_scale_dimensions.Width,
CurrentAutoScaleDimensions.Height / auto_scale_dimensions.Height);
}
}
[MonoTODO("Call scaling method")]
- public virtual AutoScaleMode AutoScaleMode {
+ [Browsable (false)]
+ [EditorBrowsable (EditorBrowsableState.Advanced)]
+ [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
+ public AutoScaleMode AutoScaleMode {
get {
return auto_scale_mode;
}
#if NET_2_0
[MonoTODO("Revisit when System.Drawing.GDI.WindowsGraphics.GetTextMetrics is done or come up with other cross-plat avg. font width calc method")]
+ [Browsable (false)]
+ [EditorBrowsable (EditorBrowsableState.Advanced)]
public SizeF CurrentAutoScaleDimensions {
get {
switch(auto_scale_mode) {
get {
Control parent;
- parent = this.parent;
+ parent = this.Parent;
while (parent != null) {
if (parent is Form) {
return (Form)parent;
}
- parent = parent.parent;
+ parent = parent.Parent;
}
return null;
base.Dispose(disposing);
}
- // LAMESPEC This used to be documented, but it's not in code
+ // LAMESPEC This used to be documented, but it's not in code
// and no longer listed in MSDN2
// [EditorBrowsable (EditorBrowsableState.Advanced)]
// protected override void OnControlRemoved(ControlEventArgs e) {
return true;
}
break;
- }
+ }
}
c = GetNextControl(c, true);
if (c != null) {
// This is stupid. I want to be able to call c.ProcessMnemonic directly
- if (c.ProcessControlMnemonic(charCode)) {
+ if (c.CanSelect && c.ProcessControlMnemonic(charCode)) {
return(true);
}
continue;
wrapped = true;
}
} while (c != active_control);
-
+
return false;
}
return SelectNextControl(active_control, forward, true, true, false);
}
- protected override void Select(bool directed, bool forward) {
-
- int index;
- bool result;
-
- if (!directed) {
- // Select this control
- Select(this);
- return;
+ protected override void Select(bool directed, bool forward)
+ {
+ if (Parent != null) {
+ IContainerControl parent = Parent.GetContainerControl ();
+ if (parent != null) {
+ parent.ActiveControl = this;
+ }
}
- if (parent == null) {
- return;
+ if (directed && auto_select_child) {
+ SelectNextControl (null, forward, true, true, false);
}
-
- // FIXME - this thing is doing the wrong stuff, needs to be similar to SelectNextControl
-
- index = parent.child_controls.IndexOf(this);
- result = false;
-
- do {
- if (forward) {
- if ((index+1) < parent.child_controls.Count) {
- index++;
- } else {
- index = 0;
- }
- } else {
- if (index>0) {
- index++;
- } else {
- index = parent.child_controls.Count-1;
- }
- }
- result = Select(parent.child_controls[index]);
- } while (!result && parent.child_controls[index] != this);
}
protected virtual void UpdateDefaultButton() {
[EditorBrowsable (EditorBrowsableState.Advanced)]
protected override void WndProc(ref Message m) {
switch ((Msg) m.Msg) {
- case Msg.WM_LBUTTONDOWN:
- OnMouseDown (new MouseEventArgs (FromParamToMouseButtons ((int) m.WParam.ToInt32()),
- mouse_clicks, LowOrder ((int) m.LParam.ToInt32 ()),
- HighOrder ((int) m.LParam.ToInt32 ()), 0));
- return;
- case Msg.WM_SETFOCUS:
- if (active_control == null)
- SelectNextControl (null, true, true, true, false);
- break;
- case Msg.WM_KILLFOCUS:
+
+ case Msg.WM_SETFOCUS:
+ if (active_control != null)
+ Select (active_control);
+ else
+ base.WndProc (ref m);
+#if false
+ else
+ SelectNextControl (null, true, true, true, false);
+#endif
break;
+
+ default:
+ base.WndProc(ref m);
+ break;
}
- base.WndProc(ref m);
}
#endregion // Protected Instance Methods
// do nothing here, only called if it is a Form
}
#endregion // Internal Methods
+
+#if NET_2_0
+ protected override void OnParentChanged (EventArgs e)
+ {
+ base.OnParentChanged (e);
+ }
+
+ [EditorBrowsable (EditorBrowsableState.Advanced)]
+ protected override void OnFontChanged (EventArgs e)
+ {
+ base.OnFontChanged (e);
+ }
+
+ protected override void OnLayout (LayoutEventArgs levent)
+ {
+ base.OnLayout (levent);
+ }
+
+ AutoValidate auto_validate = AutoValidate.Inherit;
+
+ [Browsable (false)]
+ [AmbientValue (AutoValidate.Inherit)]
+ [EditorBrowsable (EditorBrowsableState.Never)]
+ public virtual AutoValidate AutoValidate {
+ get {
+ return auto_validate;
+ }
+
+ [MonoTODO("Currently does nothing with the setting")]
+ set {
+ if (auto_validate != value){
+ auto_validate = value;
+ OnAutoValidateChanged (new EventArgs ());
+ }
+ }
+ }
+
+ static object OnValidateChanged = new object ();
+
+ protected virtual void OnAutoValidateChanged (EventArgs e)
+ {
+ EventHandler eh = (EventHandler) (Events [OnValidateChanged]);
+ if (eh != null)
+ eh (this, e);
+ }
+
+ [Browsable (false)]
+ [EditorBrowsable (EditorBrowsableState.Never)]
+ public event EventHandler AutoValidateChanged {
+ add { Events.AddHandler (OnValidateChanged, value); }
+ remove { Events.RemoveHandler (OnValidateChanged, value); }
+ }
+#endif
}
}