//
-// 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 unvalidated_control;
}
set {
- if (value==null || (active_control == value && active_control.has_focus) || !Contains(value)) {
+ if (value==null || (active_control == value)) {
return;
}
- if (!Contains(value) && this != value) {
- throw new ArgumentException("Not a child control");
+ 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 ();
- if (walk.CausesValidation && !ValidateControl (walk))
- return;
+ /* 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;
}
}
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,
// should eventually be rolled into ActiveControl setter
private Control GetCommonContainer (Control active_control, Control value)
}
#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) {
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;
- }
+ }
}
wrapped = true;
}
} while (c != active_control);
-
+
return false;
}
[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)
- Select (active_control);
- else
- 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
}
}