1 // Permission is hereby granted, free of charge, to any person obtaining
2 // a copy of this software and associated documentation files (the
3 // "Software"), to deal in the Software without restriction, including
4 // without limitation the rights to use, copy, modify, merge, publish,
5 // distribute, sublicense, and/or sell copies of the Software, and to
6 // permit persons to whom the Software is furnished to do so, subject to
7 // the following conditions:
9 // The above copyright notice and this permission notice shall be
10 // included in all copies or substantial portions of the Software.
12 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
13 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
14 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
15 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
16 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
17 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
18 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20 // Copyright (c) 2004 Novell, Inc.
23 // Peter Bartok pbartok@novell.com
28 using System.Collections;
29 using System.ComponentModel;
30 using System.ComponentModel.Design;
32 using System.Runtime.InteropServices;
34 namespace System.Windows.Forms {
36 [ClassInterface (ClassInterfaceType.AutoDispatch)]
39 public class ContainerControl : ScrollableControl, IContainerControl {
40 private Control active_control;
41 private Control unvalidated_control;
43 // This is an internal hack that allows some container controls
44 // to not auto select their child when they are activated
45 internal bool auto_select_child = true;
47 private SizeF auto_scale_dimensions;
48 private AutoScaleMode auto_scale_mode;
51 #region Public Constructors
52 public ContainerControl() {
53 active_control = null;
54 unvalidated_control = null;
55 ControlRemoved += new ControlEventHandler(OnControlRemoved);
57 auto_scale_dimensions = SizeF.Empty;
58 auto_scale_mode = AutoScaleMode.None;
61 #endregion // Public Constructors
63 #region Public Instance Properties
65 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
66 public Control ActiveControl {
68 return active_control;
72 if (value==null || (active_control == value && active_control.Focused)) {
76 if (!Contains(value)) {
77 throw new ArgumentException("Cannot activate invisible or disabled control.");
80 // Fire the enter and leave events if possible
81 Form form = FindForm ();
82 Control active = GetMostDeeplyNestedActiveControl (form == null ? this : form);
83 Control common_ancestor = GetCommonContainer (active, value);
84 ArrayList chain = new ArrayList ();
85 ArrayList validation_chain = new ArrayList ();
86 Control walk = active;
88 // we split this up into three steps:
89 // 1. walk up the tree (if we need to) to our root, firing leave events.
91 // 3. walk down the tree (if we need to), firing enter events.
93 // "our root" is either the common ancestor of the current active
94 // control and the new active control, or the current active control,
95 // or the new active control. That is, it's either one of these three
98 // (1) root (2) new (3) current
100 // ... ... ... ... ... ...
102 // current new current new
105 // note (3) doesn't require any upward walking, and no leave events are generated.
106 // (2) doesn't require any downward walking, and no enter events are generated.
108 // as we walk up the tree, we generate a list of all controls which cause
109 // validation. After firing the leave events, we invoke (in order starting from
110 // the most deeply nested) their Validating event. If one sets CancelEventArgs.Cancel
111 // to true, we ignore the control the user wanted to set ActiveControl to, and use
112 // the Validating control instead.
114 bool fire_enter = true;
115 Control root = common_ancestor;
117 active_control = value;
119 // Generate the leave messages
120 while (walk != common_ancestor) {
127 /* clear our idea of the active control as we go back up */
128 if (walk is ContainerControl)
129 ((ContainerControl)walk).active_control = null;
131 if (walk.CausesValidation)
132 validation_chain.Add (walk);
137 for (int i = 0; i < validation_chain.Count; i ++) {
138 if (!ValidateControl ((Control)validation_chain[i])) {
139 active_control = value = (Control)validation_chain[i];
146 while (walk != root) {
151 if (root != null && walk == root && !(root is ContainerControl))
154 for (int i = chain.Count - 1; i >= 0; i--) {
155 walk = (Control) chain [i];
162 while (walk != null) {
163 if (walk.Parent is ContainerControl) {
164 ((ContainerControl) walk.Parent).active_control = ctl;
173 // Scroll control into view
174 ScrollControlIntoView(active_control);
176 // Let the control know it's selected
177 SendControlFocus (active_control);
181 private bool ValidateControl (Control c)
183 CancelEventArgs e = new CancelEventArgs ();
185 c.FireValidating (e);
194 private Control GetMostDeeplyNestedActiveControl (ContainerControl container)
196 Control active = container.ActiveControl;
197 while (active is ContainerControl) {
198 if (((ContainerControl)active).ActiveControl == null)
200 active = ((ContainerControl)active).ActiveControl;
205 // Just in a separate method to make debugging a little easier,
206 // should eventually be rolled into ActiveControl setter
207 private Control GetCommonContainer (Control active_control, Control value)
209 Control new_container = null;
210 Control prev_container = active_control;
212 while (prev_container != null) {
213 new_container = value.Parent;
214 while (new_container != null) {
215 if (new_container == prev_container)
216 return new_container;
217 new_container = new_container.Parent;
220 prev_container = prev_container.Parent;
226 internal void SendControlFocus (Control c)
228 if (c.IsHandleCreated) {
229 XplatUI.SetFocus (c.window.Handle);
235 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
236 [EditorBrowsable (EditorBrowsableState.Advanced)]
238 public SizeF AutoScaleDimensions {
240 return auto_scale_dimensions;
244 if (auto_scale_dimensions != value) {
245 auto_scale_dimensions = value;
247 if (AutoScaleFactor != new SizeF (1, 1))
253 protected SizeF AutoScaleFactor {
255 if (auto_scale_dimensions.IsEmpty) {
256 return new SizeF(1f, 1f);
258 return new SizeF(CurrentAutoScaleDimensions.Width / auto_scale_dimensions.Width,
259 CurrentAutoScaleDimensions.Height / auto_scale_dimensions.Height);
265 [EditorBrowsable (EditorBrowsableState.Advanced)]
266 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
267 public AutoScaleMode AutoScaleMode {
269 return auto_scale_mode;
272 if (auto_scale_mode != value) {
273 auto_scale_mode = value;
275 auto_scale_dimensions = CurrentAutoScaleDimensions;
282 public override BindingContext BindingContext {
284 if (base.BindingContext == null) {
285 base.BindingContext = new BindingContext();
287 return base.BindingContext;
291 base.BindingContext = value;
297 [EditorBrowsable (EditorBrowsableState.Advanced)]
298 public SizeF CurrentAutoScaleDimensions {
300 switch(auto_scale_mode) {
301 case AutoScaleMode.Dpi:
302 return TextRenderer.GetDpi ();
304 case AutoScaleMode.Font:
305 Size s = TextRenderer.MeasureText ("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890", Font);
306 int width = (int)((float)s.Width / 62f);
308 return new SizeF (width, s.Height);
311 return auto_scale_dimensions;
317 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
318 public Form ParentForm {
322 parent = this.Parent;
324 while (parent != null) {
325 if (parent is Form) {
328 parent = parent.Parent;
334 #endregion // Public Instance Properties
336 #region Protected Instance Methods
337 protected override CreateParams CreateParams {
339 return base.CreateParams;
342 #endregion // Public Instance Methods
344 #region Public Instance Methods
346 public void PerformAutoScale ()
349 Scale (AutoScaleFactor);
350 ResumeLayout (false);
352 auto_scale_dimensions = CurrentAutoScaleDimensions;
357 static bool ValidateWarned;
358 public bool Validate() {
359 //throw new NotImplementedException();
360 if (!ValidateWarned) {
361 Console.WriteLine("ContainerControl.Validate is not yet implemented");
362 ValidateWarned = true;
367 bool IContainerControl.ActivateControl(Control control) {
368 return Select(control);
370 #endregion // Public Instance Methods
372 #region Protected Instance Methods
373 [EditorBrowsable (EditorBrowsableState.Advanced)]
374 protected override void AdjustFormScrollbars(bool displayScrollbars) {
375 base.AdjustFormScrollbars(displayScrollbars);
378 protected override void Dispose(bool disposing) {
379 base.Dispose(disposing);
382 // LAMESPEC This used to be documented, but it's not in code
383 // and no longer listed in MSDN2
384 // [EditorBrowsable (EditorBrowsableState.Advanced)]
385 // protected override void OnControlRemoved(ControlEventArgs e) {
386 private void OnControlRemoved(object sender, ControlEventArgs e) {
387 if (e.Control == this.unvalidated_control) {
388 this.unvalidated_control = null;
391 if (e.Control == this.active_control) {
392 this.unvalidated_control = null;
395 // base.OnControlRemoved(e);
398 protected override void OnCreateControl() {
399 base.OnCreateControl();
400 // MS seems to call this here, it gets the whole databinding process started
401 OnBindingContextChanged (EventArgs.Empty);
405 protected override bool ProcessCmdKey (ref Message msg, Keys keyData)
407 if (ToolStripManager.ProcessCmdKey (ref msg, keyData) == true)
410 return base.ProcessCmdKey (ref msg, keyData);
414 [EditorBrowsable (EditorBrowsableState.Advanced)]
415 protected override bool ProcessDialogChar(char charCode) {
417 if (ProcessMnemonic(charCode)) {
421 return base.ProcessDialogChar(charCode);
424 protected override bool ProcessDialogKey(Keys keyData) {
428 key = keyData & Keys.KeyCode;
433 if ((keyData & (Keys.Alt | Keys.Control)) == Keys.None) {
434 if (ProcessTabKey ((Control.ModifierKeys & Keys.Shift) == 0)) {
455 if (SelectNextControl(active_control, forward, false, false, true)) {
463 return base.ProcessDialogKey(keyData);
466 protected override bool ProcessMnemonic(char charCode) {
474 c = GetNextControl(c, true);
476 // This is stupid. I want to be able to call c.ProcessMnemonic directly
477 if (c.ProcessControlMnemonic(charCode)) {
487 } while (c != active_control);
492 protected virtual bool ProcessTabKey(bool forward) {
493 return SelectNextControl(active_control, forward, true, true, false);
496 protected override void Select(bool directed, bool forward)
498 if (Parent != null) {
499 IContainerControl parent = Parent.GetContainerControl ();
500 if (parent != null) {
501 parent.ActiveControl = this;
505 if (directed && auto_select_child) {
506 SelectNextControl (null, forward, true, true, false);
510 protected virtual void UpdateDefaultButton() {
514 [EditorBrowsable (EditorBrowsableState.Advanced)]
515 protected override void WndProc(ref Message m) {
516 switch ((Msg) m.Msg) {
518 case Msg.WM_SETFOCUS:
519 if (active_control != null)
520 Select (active_control);
522 base.WndProc (ref m);
525 SelectNextControl (null, true, true, true, false);
534 #endregion // Protected Instance Methods
536 #region Internal Methods
537 internal void ChildControlRemoved (Control control)
539 if (control == active_control) {
540 SelectNextControl (this, true, true, true, true);
541 if (control == active_control) {
542 active_control = null;
546 internal virtual void CheckAcceptButton()
548 // do nothing here, only called if it is a Form
550 #endregion // Internal Methods
553 protected override void OnParentChanged (EventArgs e)
555 base.OnParentChanged (e);
558 [EditorBrowsable (EditorBrowsableState.Advanced)]
559 protected override void OnFontChanged (EventArgs e)
561 base.OnFontChanged (e);
563 if (AutoScaleMode == AutoScaleMode.Font)
567 protected override void OnLayout (LayoutEventArgs levent)
569 base.OnLayout (levent);
572 AutoValidate auto_validate = AutoValidate.Inherit;
575 [AmbientValue (AutoValidate.Inherit)]
576 [EditorBrowsable (EditorBrowsableState.Never)]
577 public virtual AutoValidate AutoValidate {
579 return auto_validate;
582 [MonoTODO("Currently does nothing with the setting")]
584 if (auto_validate != value){
585 auto_validate = value;
586 OnAutoValidateChanged (new EventArgs ());
591 internal bool ShouldSerializeAutoValidate ()
593 return this.AutoValidate != AutoValidate.Inherit;
596 static object OnValidateChanged = new object ();
598 protected virtual void OnAutoValidateChanged (EventArgs e)
600 EventHandler eh = (EventHandler) (Events [OnValidateChanged]);
606 [EditorBrowsable (EditorBrowsableState.Never)]
607 public event EventHandler AutoValidateChanged {
608 add { Events.AddHandler (OnValidateChanged, value); }
609 remove { Events.RemoveHandler (OnValidateChanged, value); }