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 ();
83 Control active = GetMostDeeplyNestedActiveControl (form);
84 Control common_container = GetCommonContainer (active, value);
85 ArrayList chain = new ArrayList ();
86 ArrayList validation_chain = new ArrayList ();
87 Control walk = active;
89 // we split this up into three steps:
90 // 1. walk up the tree (if we need to) to our root, firing leave events.
92 // 3. walk down the tree (if we need to), firing enter events.
94 // "our root" is either the common container of the current active
95 // control and the new active control, or the current active control,
96 // or the new active control. That is, it's either one of these three
99 // (1) root (2) new (3) current
101 // ... ... ... ... ... ...
103 // current new current new
106 // note (3) doesn't require any upward walking, and no leave events are generated.
107 // (2) doesn't require any downward walking, and no enter events are generated.
109 // as we walk up the tree, we generate a list of all controls which cause
110 // validation. After firing the leave events, we invoke (in order starting from
111 // the most deeply nested) their Validating event. If one sets CancelEventArgs.Cancel
112 // to true, we ignore the control the user wanted to set ActiveControl to, and use
113 // the Validating control instead.
115 bool fire_enter = true;
116 Control root = common_container;
118 // Generate the leave messages
119 while (walk != common_container) {
126 /* clear our idea of the active control as we go back up */
127 if (walk is ContainerControl)
128 ((ContainerControl)walk).active_control = null;
130 if (walk.CausesValidation)
131 validation_chain.Add (walk);
136 for (int i = 0; i < validation_chain.Count; i ++) {
137 if (!ValidateControl ((Control)validation_chain[i])) {
138 value = (Control)validation_chain[i];
145 while (walk != root) {
150 for (int i = chain.Count - 1; i >= 0; i--) {
151 walk = (Control) chain [i];
158 while (walk != null) {
159 if (walk.Parent is ContainerControl) {
160 ((ContainerControl) walk.Parent).active_control = ctl;
167 active_control = value;
172 // Scroll control into view
173 ScrollControlIntoView(active_control);
175 // Let the control know it's selected
176 SendControlFocus (value);
180 private bool ValidateControl (Control c)
182 CancelEventArgs e = new CancelEventArgs ();
184 c.FireValidating (e);
193 private Control GetMostDeeplyNestedActiveControl (ContainerControl container)
195 Control active = container.ActiveControl;
196 while (active is ContainerControl) {
197 if (((ContainerControl)active).ActiveControl == null)
199 active = ((ContainerControl)active).ActiveControl;
204 // Just in a separate method to make debugging a little easier,
205 // should eventually be rolled into ActiveControl setter
206 private Control GetCommonContainer (Control active_control, Control value)
208 Control new_container = null;
209 Control prev_container = active_control;
211 while (prev_container != null) {
212 new_container = value.Parent;
213 while (new_container != null) {
214 if (new_container == prev_container)
215 return new_container;
216 new_container = new_container.Parent;
219 prev_container = prev_container.Parent;
225 internal void SendControlFocus (Control c)
227 if (c.IsHandleCreated) {
228 XplatUI.SetFocus (c.window.Handle);
234 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
235 [EditorBrowsable (EditorBrowsableState.Advanced)]
237 public SizeF AutoScaleDimensions {
239 return auto_scale_dimensions;
243 auto_scale_dimensions = value;
247 protected SizeF AutoScaleFactor {
249 if (auto_scale_dimensions.IsEmpty) {
250 return new SizeF(1f, 1f);
252 return new SizeF(CurrentAutoScaleDimensions.Width / auto_scale_dimensions.Width,
253 CurrentAutoScaleDimensions.Height / auto_scale_dimensions.Height);
258 [MonoTODO("Call scaling method")]
260 [EditorBrowsable (EditorBrowsableState.Advanced)]
261 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
262 public AutoScaleMode AutoScaleMode {
264 return auto_scale_mode;
267 if (auto_scale_mode != value) {
268 auto_scale_mode = value;
277 public override BindingContext BindingContext {
279 if (base.BindingContext == null) {
280 base.BindingContext = new BindingContext();
282 return base.BindingContext;
286 base.BindingContext = value;
291 [MonoTODO("Revisit when System.Drawing.GDI.WindowsGraphics.GetTextMetrics is done or come up with other cross-plat avg. font width calc method")]
293 [EditorBrowsable (EditorBrowsableState.Advanced)]
294 public SizeF CurrentAutoScaleDimensions {
296 switch(auto_scale_mode) {
297 case AutoScaleMode.Dpi: {
302 bmp = new Bitmap(1, 1, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
303 g = Graphics.FromImage(bmp);
304 size = new SizeF(g.DpiX, g.DpiY);
310 case AutoScaleMode.Font: {
311 // http://msdn2.microsoft.com/en-us/library/system.windows.forms.containercontrol.currentautoscaledimensions(VS.80).aspx
312 // Implement System.Drawing.GDI.WindowsGraphics.GetTextMetrics first...
317 return auto_scale_dimensions;
323 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
324 public Form ParentForm {
328 parent = this.Parent;
330 while (parent != null) {
331 if (parent is Form) {
334 parent = parent.Parent;
340 #endregion // Public Instance Properties
342 #region Protected Instance Methods
343 protected override CreateParams CreateParams {
345 return base.CreateParams;
348 #endregion // Public Instance Methods
350 #region Public Instance Methods
352 static bool ValidateWarned;
353 public bool Validate() {
354 //throw new NotImplementedException();
355 if (!ValidateWarned) {
356 Console.WriteLine("ContainerControl.Validate is not yet implemented");
357 ValidateWarned = true;
362 bool IContainerControl.ActivateControl(Control control) {
363 return Select(control);
365 #endregion // Public Instance Methods
367 #region Protected Instance Methods
368 [EditorBrowsable (EditorBrowsableState.Advanced)]
369 protected override void AdjustFormScrollbars(bool displayScrollbars) {
370 base.AdjustFormScrollbars(displayScrollbars);
373 protected override void Dispose(bool disposing) {
374 base.Dispose(disposing);
377 // LAMESPEC This used to be documented, but it's not in code
378 // and no longer listed in MSDN2
379 // [EditorBrowsable (EditorBrowsableState.Advanced)]
380 // protected override void OnControlRemoved(ControlEventArgs e) {
381 private void OnControlRemoved(object sender, ControlEventArgs e) {
382 if (e.Control == this.unvalidated_control) {
383 this.unvalidated_control = null;
386 if (e.Control == this.active_control) {
387 this.unvalidated_control = null;
390 // base.OnControlRemoved(e);
393 protected override void OnCreateControl() {
394 base.OnCreateControl();
395 // MS seems to call this here, it gets the whole databinding process started
396 OnBindingContextChanged (EventArgs.Empty);
400 protected override bool ProcessCmdKey (ref Message msg, Keys keyData)
402 if (ToolStripManager.ProcessCmdKey (ref msg, keyData) == true)
405 return base.ProcessCmdKey (ref msg, keyData);
409 [EditorBrowsable (EditorBrowsableState.Advanced)]
410 protected override bool ProcessDialogChar(char charCode) {
412 if (ProcessMnemonic(charCode)) {
416 return base.ProcessDialogChar(charCode);
419 protected override bool ProcessDialogKey(Keys keyData) {
423 key = keyData & Keys.KeyCode;
428 if ((keyData & (Keys.Alt | Keys.Control)) == Keys.None) {
429 if (ProcessTabKey ((Control.ModifierKeys & Keys.Shift) == 0)) {
450 if (SelectNextControl(active_control, forward, false, false, true)) {
458 return base.ProcessDialogKey(keyData);
461 protected override bool ProcessMnemonic(char charCode) {
469 c = GetNextControl(c, true);
471 // This is stupid. I want to be able to call c.ProcessMnemonic directly
472 if (c.CanSelect && c.ProcessControlMnemonic(charCode)) {
482 } while (c != active_control);
487 protected virtual bool ProcessTabKey(bool forward) {
488 return SelectNextControl(active_control, forward, true, true, false);
491 protected override void Select(bool directed, bool forward)
493 if (Parent != null) {
494 IContainerControl parent = Parent.GetContainerControl ();
495 if (parent != null) {
496 parent.ActiveControl = this;
500 if (directed && auto_select_child) {
501 SelectNextControl (null, forward, true, true, false);
505 protected virtual void UpdateDefaultButton() {
509 [EditorBrowsable (EditorBrowsableState.Advanced)]
510 protected override void WndProc(ref Message m) {
511 switch ((Msg) m.Msg) {
513 case Msg.WM_SETFOCUS:
514 if (active_control != null)
515 Select (active_control);
517 base.WndProc (ref m);
520 SelectNextControl (null, true, true, true, false);
529 #endregion // Protected Instance Methods
531 #region Internal Methods
532 internal void ChildControlRemoved (Control control)
534 if (control == active_control) {
535 SelectNextControl (this, true, true, true, true);
536 if (control == active_control) {
537 active_control = null;
541 internal virtual void CheckAcceptButton()
543 // do nothing here, only called if it is a Form
545 #endregion // Internal Methods
548 protected override void OnParentChanged (EventArgs e)
550 base.OnParentChanged (e);
553 [EditorBrowsable (EditorBrowsableState.Advanced)]
554 protected override void OnFontChanged (EventArgs e)
556 base.OnFontChanged (e);
559 protected override void OnLayout (LayoutEventArgs levent)
561 base.OnLayout (levent);
564 AutoValidate auto_validate = AutoValidate.Inherit;
567 [AmbientValue (AutoValidate.Inherit)]
568 [EditorBrowsable (EditorBrowsableState.Never)]
569 public virtual AutoValidate AutoValidate {
571 return auto_validate;
574 [MonoTODO("Currently does nothing with the setting")]
576 if (auto_validate != value){
577 auto_validate = value;
578 OnAutoValidateChanged (new EventArgs ());
583 static object OnValidateChanged = new object ();
585 protected virtual void OnAutoValidateChanged (EventArgs e)
587 EventHandler eh = (EventHandler) (Events [OnValidateChanged]);
593 [EditorBrowsable (EditorBrowsableState.Never)]
594 public event EventHandler AutoValidateChanged {
595 add { Events.AddHandler (OnValidateChanged, value); }
596 remove { Events.RemoveHandler (OnValidateChanged, value); }