2007-03-19 Chris Toshok <toshok@ximian.com>
[mono-project.git] / mcs / class / Managed.Windows.Forms / System.Windows.Forms / TabControl.cs
blobd67a68ead3eb5960445e6d3e342017734340eda7
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:
8 //
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-2005 Novell, Inc.
22 // Authors:
23 // Jackson Harper (jackson@ximian.com)
26 using System;
27 using System.Collections;
28 using System.ComponentModel;
29 using System.ComponentModel.Design;
30 using System.Drawing;
33 namespace System.Windows.Forms {
34 [DefaultEvent("SelectedIndexChanged")]
35 [DefaultProperty("TabPages")]
36 [Designer("System.Windows.Forms.Design.TabControlDesigner, " + Consts.AssemblySystem_Design, "System.ComponentModel.Design.IDesigner")]
37 public class TabControl : Control {
38 #region Fields
39 private int selected_index = -1;
40 private TabAlignment alignment;
41 private TabAppearance appearance;
42 private TabDrawMode draw_mode;
43 private bool multiline;
44 private ImageList image_list;
45 private Size item_size = Size.Empty;
46 private Point padding;
47 private int row_count = 0;
48 private bool hottrack;
49 private TabPageCollection tab_pages;
50 private bool show_tool_tips;
51 private TabSizeMode size_mode;
52 private bool show_slider = false;
53 private ButtonState right_slider_state;
54 private ButtonState left_slider_state;
55 private int slider_pos = 0;
56 #endregion // Fields
58 #region Public Constructors
59 public TabControl ()
61 tab_pages = new TabPageCollection (this);
62 SetStyle (ControlStyles.UserPaint, false);
63 padding = ThemeEngine.Current.TabControlDefaultPadding;
64 item_size = ThemeEngine.Current.TabControlDefaultItemSize;
66 MouseDown += new MouseEventHandler (MouseDownHandler);
67 MouseUp += new MouseEventHandler (MouseUpHandler);
68 SizeChanged += new EventHandler (SizeChangedHandler);
71 #endregion // Public Constructors
73 #region Public Instance Properties
74 [DefaultValue(TabAlignment.Top)]
75 [Localizable(true)]
76 [RefreshProperties(RefreshProperties.All)]
77 public TabAlignment Alignment {
78 get { return alignment; }
79 set {
80 if (alignment == value)
81 return;
82 alignment = value;
83 if (alignment == TabAlignment.Left || alignment == TabAlignment.Right)
84 multiline = true;
85 Redraw ();
89 [DefaultValue(TabAppearance.Normal)]
90 [Localizable(true)]
91 public TabAppearance Appearance {
92 get { return appearance; }
93 set {
94 if (appearance == value)
95 return;
96 appearance = value;
97 Redraw ();
101 [Browsable(false)]
102 [EditorBrowsable(EditorBrowsableState.Never)]
103 public override Color BackColor {
104 get { return ThemeEngine.Current.ColorControl; }
105 set { /* nothing happens on set on MS */ }
108 [Browsable(false)]
109 [EditorBrowsable(EditorBrowsableState.Never)]
110 public override Image BackgroundImage {
111 get { return base.BackgroundImage; }
112 set { base.BackgroundImage = value; }
115 public override Rectangle DisplayRectangle {
116 get {
117 return ThemeEngine.Current.GetTabControlDisplayRectangle (this);
121 [DefaultValue(TabDrawMode.Normal)]
122 public TabDrawMode DrawMode {
123 get { return draw_mode; }
124 set {
125 if (draw_mode == value)
126 return;
127 draw_mode = value;
128 Redraw ();
132 [Browsable(false)]
133 [EditorBrowsable(EditorBrowsableState.Never)]
134 public override Color ForeColor {
135 get { return base.ForeColor; }
136 set { base.ForeColor = value; }
139 [DefaultValue(false)]
140 public bool HotTrack {
141 get { return hottrack; }
142 set {
143 if (hottrack == value)
144 return;
145 hottrack = value;
146 Redraw ();
150 [DefaultValue(null)]
151 public ImageList ImageList {
152 get { return image_list; }
153 set { image_list = value; }
156 [Localizable(true)]
157 public Size ItemSize {
158 get {
159 return item_size;
161 set {
162 if (value.Height < 0 || value.Width < 0)
163 throw new ArgumentException ("'" + value + "' is not a valid value for 'ItemSize'.");
164 item_size = value;
165 Redraw ();
169 [DefaultValue(false)]
170 public bool Multiline {
171 get { return multiline; }
172 set {
173 if (multiline == value)
174 return;
175 multiline = value;
176 if (!multiline && alignment == TabAlignment.Left || alignment == TabAlignment.Right)
177 alignment = TabAlignment.Top;
178 Redraw ();
182 [Localizable(true)]
183 public new Point Padding {
184 get { return padding; }
185 set {
186 if (value.X < 0 || value.Y < 0)
187 throw new ArgumentException ("'" + value + "' is not a valid value for 'Padding'.");
188 if (padding == value)
189 return;
190 padding = value;
191 Redraw ();
196 [Browsable(false)]
197 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
198 public int RowCount {
199 get { return row_count; }
202 [DefaultValue(-1)]
203 [Browsable(false)]
204 public int SelectedIndex {
205 get { return selected_index; }
206 set {
208 if (value < -1) {
209 #if NET_2_0
210 throw new ArgumentOutOfRangeException ("SelectedIndex", "Value of '" + value + "' is valid for 'SelectedIndex'. " +
211 "'SelectedIndex' must be greater than or equal to -1.");
212 #else
213 throw new ArgumentException ("'" + value + "' is not a valid value for 'value'. " +
214 "'value' must be greater than or equal to -1.");
215 #endif
217 if (!this.IsHandleCreated) {
218 if (selected_index != value) {
219 selected_index = value;
220 #if !NET_2_0
221 OnSelectedIndexChanged (EventArgs.Empty);
222 #endif
224 return;
227 if (value >= TabCount) {
228 if (value != selected_index)
229 OnSelectedIndexChanged (EventArgs.Empty);
230 return;
233 if (value == selected_index) {
234 if (selected_index > -1)
235 Invalidate(GetTabRect (selected_index));
236 return;
239 #if NET_2_0
240 TabControlCancelEventArgs ret = new TabControlCancelEventArgs (SelectedTab, selected_index, false, TabControlAction.Deselecting);
241 OnDeselecting (ret);
242 if (ret.Cancel)
243 return;
245 #endif
246 int old_index = selected_index;
247 int new_index = value;
249 selected_index = new_index;
251 #if NET_2_0
252 ret = new TabControlCancelEventArgs (SelectedTab, selected_index, false, TabControlAction.Selecting);
253 OnSelecting (ret);
254 if (ret.Cancel) {
255 selected_index = old_index;
256 return;
258 #endif
260 SuspendLayout ();
262 Rectangle invalid = Rectangle.Empty;
263 bool refresh = false;
265 if (new_index != -1 && show_slider && new_index < slider_pos) {
266 slider_pos = new_index;
267 refresh = true;
270 if (new_index != -1) {
271 int le = TabPages[new_index].TabBounds.Right;
272 int re = ThemeEngine.Current.GetTabControlLeftScrollRect (this).Left;
273 if (show_slider && le > re) {
274 int i = 0;
276 for (i = new_index; i < TabPages.Count; i++) {
277 if (TabPages [i].TabBounds.Right > re)
278 break;
280 slider_pos = i;
281 refresh = true;
285 if (old_index != -1 && new_index != -1) {
286 if (!refresh)
287 invalid = GetTabRect (old_index);
288 ((TabPage) Controls[old_index]).SetVisible (false);
291 TabPage selected = null;
293 if (new_index != -1) {
294 selected = (TabPage) Controls[new_index];
295 invalid = Rectangle.Union (invalid, GetTabRect (new_index));
296 selected.SetVisible (true);
299 OnSelectedIndexChanged (EventArgs.Empty);
301 ResumeLayout ();
303 if (refresh) {
304 SizeTabs ();
305 Refresh ();
306 } else if (new_index != -1 && selected.Row != BottomRow) {
307 DropRow (TabPages[new_index].Row);
308 // calculating what to invalidate here seems to be slower then just
309 // refreshing the whole thing
310 SizeTabs ();
311 Refresh ();
312 } else {
313 SizeTabs ();
314 // The lines are drawn on the edges of the tabs so the invalid area should
315 // needs to include the extra pixels of line width.
316 if (appearance == TabAppearance.Normal) {
317 invalid.Inflate (6, 4);
319 Invalidate (invalid);
324 [Browsable(false)]
325 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
326 public TabPage SelectedTab {
327 get {
328 if (selected_index == -1)
329 return null;
330 return tab_pages [selected_index];
332 set {
333 int index = IndexForTabPage (value);
334 if (index == selected_index)
335 return;
336 SelectedIndex = index;
340 [DefaultValue(false)]
341 [Localizable(true)]
342 public bool ShowToolTips {
343 get { return show_tool_tips; }
344 set {
345 if (show_tool_tips == value)
346 return;
347 show_tool_tips = value;
348 Redraw ();
352 [DefaultValue(TabSizeMode.Normal)]
353 [RefreshProperties(RefreshProperties.Repaint)]
354 public TabSizeMode SizeMode {
355 get { return size_mode; }
356 set {
357 if (size_mode == value)
358 return;
359 size_mode = value;
360 Redraw ();
364 [Browsable(false)]
365 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
366 public int TabCount {
367 get {
368 return tab_pages.Count;
372 [DefaultValue(null)]
373 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
374 [MergableProperty(false)]
375 public TabPageCollection TabPages {
376 get { return tab_pages; }
379 [Browsable(false)]
380 [Bindable(false)]
381 [EditorBrowsable(EditorBrowsableState.Never)]
382 public override string Text {
383 get { return base.Text; }
384 set { base.Text = value; }
387 #endregion // Public Instance Properties
389 #region Internal Properties
390 internal bool ShowSlider {
391 get { return show_slider; }
392 set { show_slider = value; }
395 internal int SliderPos {
396 get { return slider_pos; }
399 internal ButtonState RightSliderState {
400 get { return right_slider_state; }
403 internal ButtonState LeftSliderState {
404 get { return left_slider_state; }
407 #endregion // Internal Properties
409 #region Protected Instance Properties
410 protected override CreateParams CreateParams {
411 get {
412 CreateParams c = base.CreateParams;
413 return c;
417 protected override Size DefaultSize {
418 get { return new Size (200, 100); }
421 #endregion // Protected Instance Properties
423 #region Public Instance Methods
424 public Rectangle GetTabRect (int index)
426 TabPage page = GetTab (index);
427 return page.TabBounds;
430 public Control GetControl (int index)
432 return GetTab (index);
435 #if NET_2_0
436 public void SelectTab (int index)
438 if (index < 0 || index > this.tab_pages.Count - 1)
439 throw new ArgumentOutOfRangeException ("index");
441 SelectedIndex = index;
443 #endif
445 public override string ToString ()
447 string res = String.Concat (base.ToString (),
448 ", TabPages.Count: ",
449 TabCount);
450 if (TabCount > 0)
451 res = String.Concat (res, ", TabPages[0]: ",
452 TabPages [0]);
453 return res;
456 #endregion // Public Instance Methods
458 #region Protected Instance Methods
459 protected override Control.ControlCollection CreateControlsInstance ()
461 return new TabControl.ControlCollection (this);
464 protected void UpdateTabSelection (bool uiselected)
466 ResizeTabPages ();
469 protected override void CreateHandle ()
471 base.CreateHandle ();
472 selected_index = (selected_index >= TabCount ? (TabCount > 0 ? 0 : -1) : selected_index);
474 if (TabCount > 0) {
475 if (selected_index > -1)
476 this.SelectedTab.SetVisible(true);
477 else
478 tab_pages[0].SetVisible(true);
480 ResizeTabPages ();
483 protected override void OnHandleCreated (EventArgs e)
485 base.OnHandleCreated (e);
488 protected override void OnHandleDestroyed (EventArgs e)
490 base.OnHandleDestroyed (e);
493 protected virtual void OnDrawItem (DrawItemEventArgs e)
495 if (DrawMode != TabDrawMode.OwnerDrawFixed)
496 return;
498 DrawItemEventHandler eh = (DrawItemEventHandler)(Events [DrawItemEvent]);
499 if (eh != null)
500 eh (this, e);
503 internal void OnDrawItemInternal (DrawItemEventArgs e)
505 OnDrawItem (e);
508 protected override void OnFontChanged (EventArgs e)
510 base.OnFontChanged (e);
511 ResizeTabPages ();
514 protected override void OnResize (EventArgs e)
516 base.OnResize (e);
519 protected override void OnStyleChanged (EventArgs e)
521 base.OnStyleChanged (e);
524 protected override bool ProcessKeyPreview (ref Message m)
526 return base.ProcessKeyPreview (ref m);
529 protected override void OnKeyDown (KeyEventArgs e)
531 if (e.KeyCode == Keys.Tab && (e.KeyData & Keys.Control) != 0) {
532 if ((e.KeyData & Keys.Shift) == 0)
533 SelectedIndex = (SelectedIndex + 1) % TabCount;
534 else
535 SelectedIndex = (SelectedIndex - 1) % TabCount;
536 e.Handled = true;
537 } else if (e.KeyCode == Keys.Home) {
538 SelectedIndex = 0;
539 e.Handled = true;
540 } else if (e.KeyCode == Keys.End) {
541 SelectedIndex = TabCount - 1;
542 e.Handled = true;
543 } else if (e.KeyCode == Keys.Left && SelectedIndex > 0) {
544 SelectedIndex--;
545 e.Handled = true;
546 } else if (e.KeyCode == Keys.Right && SelectedIndex < TabCount - 1) {
547 SelectedIndex++;
548 e.Handled = true;
551 base.OnKeyDown (e);
554 protected override bool IsInputKey (Keys key)
556 switch (key & Keys.KeyCode) {
557 case Keys.Home:
558 case Keys.End:
559 case Keys.Left:
560 case Keys.Right:
561 return true;
563 return base.IsInputKey (key);
566 protected override void Dispose (bool disposing)
568 base.Dispose (disposing);
571 protected void RemoveAll ()
573 Controls.Clear ();
576 protected virtual object [] GetItems ()
578 TabPage [] pages = new TabPage [Controls.Count];
579 Controls.CopyTo (pages, 0);
580 return pages;
583 protected virtual object [] GetItems (Type type)
585 object [] pages = (object []) Array.CreateInstance (type, Controls.Count);
586 Controls.CopyTo (pages, 0);
587 return pages;
590 protected string GetToolTipText (object item)
592 TabPage page = (TabPage) item;
593 return page.ToolTipText;
596 protected override void WndProc (ref Message m)
598 switch ((Msg)m.Msg) {
599 case Msg.WM_SETFOCUS:
600 if (selected_index == -1 && this.TabCount > 0)
601 this.SelectedIndex = 0;
602 if (selected_index != -1)
603 Invalidate(GetTabRect(selected_index));
604 base.WndProc (ref m);
605 break;
606 case Msg.WM_KILLFOCUS:
607 if (selected_index != -1)
608 Invalidate(GetTabRect(selected_index));
609 base.WndProc (ref m);
610 break;
611 default:
612 base.WndProc (ref m);
613 break;
617 #if NET_2_0
618 protected virtual void OnDeselecting (TabControlCancelEventArgs e)
620 TabControlCancelEventHandler eh = (TabControlCancelEventHandler) (Events[DeselectingEvent]);
621 if (eh != null)
622 eh (this, e);
624 if (!e.Cancel)
625 OnDeselected (new TabControlEventArgs (SelectedTab, selected_index, TabControlAction.Deselected));
628 protected virtual void OnDeselected (TabControlEventArgs e)
630 TabControlEventHandler eh = (TabControlEventHandler) (Events[DeselectedEvent]);
631 if (eh != null)
632 eh (this, e);
634 if (this.SelectedTab != null)
635 this.SelectedTab.FireLeave ();
638 protected virtual void OnSelecting (TabControlCancelEventArgs e)
640 TabControlCancelEventHandler eh = (TabControlCancelEventHandler) (Events[SelectingEvent]);
641 if (eh != null)
642 eh (this, e);
644 if (!e.Cancel)
645 OnSelected (new TabControlEventArgs (SelectedTab, selected_index, TabControlAction.Selected));
648 protected virtual void OnSelected (TabControlEventArgs e)
650 TabControlEventHandler eh = (TabControlEventHandler)(Events[SelectedEvent]);
651 if (eh != null)
652 eh (this, e);
654 if (this.SelectedTab != null)
655 this.SelectedTab.FireEnter ();
657 #endif
659 protected virtual void OnSelectedIndexChanged (EventArgs e)
661 EventHandler eh = (EventHandler)(Events [SelectedIndexChangedEvent]);
662 if (eh != null)
663 eh (this, e);
666 internal override void OnPaintInternal (PaintEventArgs pe)
668 Draw (pe.Graphics, pe.ClipRectangle);
669 pe.Handled = true;
671 #endregion // Protected Instance Methods
673 #region Internal & Private Methods
674 private bool CanScrollRight {
675 get {
676 return (slider_pos < TabCount - 1);
680 private bool CanScrollLeft {
681 get { return slider_pos > 0; }
684 private void MouseDownHandler (object sender, MouseEventArgs e)
686 if (ShowSlider) {
687 Rectangle right = ThemeEngine.Current.GetTabControlRightScrollRect (this);
688 Rectangle left = ThemeEngine.Current.GetTabControlLeftScrollRect (this);
689 if (right.Contains (e.X, e.Y)) {
690 right_slider_state = ButtonState.Pushed;
691 if (CanScrollRight) {
692 slider_pos++;
693 SizeTabs ();
695 Invalidate (new Rectangle (0, 0, Width, DisplayRectangle.Top));
696 } else {
697 Invalidate (right);
699 return;
700 } else if (left.Contains (e.X, e.Y)) {
701 left_slider_state = ButtonState.Pushed;
702 if (CanScrollLeft) {
703 slider_pos--;
704 SizeTabs ();
706 Invalidate (new Rectangle (0, 0, Width, DisplayRectangle.Top));
707 } else {
708 Invalidate (left);
710 return;
714 int count = Controls.Count;
715 for (int i = SliderPos; i < count; i++) {
716 if (!GetTabRect (i).Contains (e.X, e.Y))
717 continue;
718 SelectedIndex = i;
719 break;
723 private void MouseUpHandler (object sender, MouseEventArgs e)
725 if (ShowSlider && (left_slider_state == ButtonState.Pushed || right_slider_state == ButtonState.Pushed)) {
726 Rectangle invalid;
727 if (left_slider_state == ButtonState.Pushed)
728 invalid = ThemeEngine.Current.GetTabControlLeftScrollRect (this);
729 else
730 invalid = ThemeEngine.Current.GetTabControlRightScrollRect (this);
731 left_slider_state = ButtonState.Normal;
732 right_slider_state = ButtonState.Normal;
734 Invalidate (invalid);
738 private void SizeChangedHandler (object sender, EventArgs e)
740 Redraw ();
743 internal void UpdateTabpage (TabPage page)
748 internal int IndexForTabPage (TabPage page)
750 for (int i = 0; i < tab_pages.Count; i++) {
751 if (page == tab_pages [i])
752 return i;
754 return -1;
757 private void ResizeTabPages ()
759 CalcTabRows ();
760 SizeTabs ();
761 Rectangle r = DisplayRectangle;
762 foreach (TabPage page in Controls) {
763 page.Bounds = r;
767 private int MinimumTabWidth {
768 get {
769 return ThemeEngine.Current.TabControlMinimumTabWidth;
773 private Size TabSpacing {
774 get {
775 return ThemeEngine.Current.TabControlGetSpacing (this);
779 private void CalcTabRows ()
781 switch (Alignment) {
782 case TabAlignment.Right:
783 case TabAlignment.Left:
784 CalcTabRows (Height);
785 break;
786 default:
787 CalcTabRows (Width);
788 break;
792 private void CalcTabRows (int row_width)
794 int xpos = 4;
795 Size spacing = TabSpacing;
797 if (TabPages.Count > 0)
798 row_count = 1;
799 show_slider = false;
801 for (int i = 0; i < TabPages.Count; i++) {
802 TabPage page = TabPages [i];
803 int width;
805 page.Row = 1;
807 if (SizeMode == TabSizeMode.Fixed) {
808 width = item_size.Width;
809 } else {
810 width = (int) DeviceContext.MeasureString (page.Text, Font).Width + (Padding.X * 2);
811 if (ImageList != null && page.ImageIndex >= 0 && page.ImageIndex < ImageList.Images.Count) {
812 width += ImageList.ImageSize.Width + 2;
813 item_size.Height = ImageList.ImageSize.Height + 3;
817 if (i == SelectedIndex)
818 width += 8;
819 if (width < MinimumTabWidth)
820 width = MinimumTabWidth;
822 if (xpos + width > row_width && multiline) {
823 xpos = 4;
824 for (int j = 0; j < i; j++) {
825 TabPages [j].Row++;
827 row_count++;
828 } else if (xpos + width > row_width) {
829 show_slider = true;
832 xpos += width + 1 + spacing.Width;
835 if (SelectedIndex != -1 && TabPages [SelectedIndex].Row != BottomRow)
836 DropRow (TabPages [SelectedIndex].Row);
839 private int BottomRow {
840 get {
841 switch (Alignment) {
842 case TabAlignment.Right:
843 case TabAlignment.Bottom:
844 return row_count;
845 default:
846 return 1;
851 private int Direction
853 get {
854 switch (Alignment) {
855 case TabAlignment.Right:
856 case TabAlignment.Bottom:
857 return -1;
858 default:
859 return 1;
864 private void DropRow (int row)
866 int bottom = BottomRow;
867 int direction = Direction;
869 foreach (TabPage page in TabPages) {
870 if (page.Row == row) {
871 page.Row = bottom;
872 } else if (direction == 1 && page.Row < row) {
873 page.Row += direction;
874 } else if (direction == -1 && page.Row > row) {
875 page.Row += direction;
880 private int CalcYPos ()
882 if (Alignment == TabAlignment.Bottom) {
883 Rectangle r = ThemeEngine.Current.GetTabControlDisplayRectangle (this);
884 return r.Bottom + 3;
886 return 1;
889 private int CalcXPos ()
891 if (Alignment == TabAlignment.Right) {
892 Rectangle r = ThemeEngine.Current.GetTabControlDisplayRectangle (this);
893 return r.Right + 4;
895 return 4;
899 private void SizeTabs ()
901 switch (Alignment) {
902 case TabAlignment.Right:
903 case TabAlignment.Left:
904 SizeTabsV (Height);
905 break;
906 default:
907 SizeTabs (Width);
908 break;
912 private void SizeTabsV (int row_width)
914 int ypos = 1;
915 int prev_row = 1;
916 Size spacing = TabSpacing;
917 int xpos = CalcXPos ();
918 int begin_prev = 0;
920 if (TabPages.Count == 0)
921 return;
923 prev_row = TabPages [0].Row;
925 for (int i = 0; i < TabPages.Count; i++) {
926 TabPage page = TabPages [i];
927 int width;
929 if (SizeMode == TabSizeMode.Fixed) {
930 width = item_size.Width;
931 } else {
932 width = (int) DeviceContext.MeasureString (page.Text, Font).Width + (Padding.X * 2);
933 if (ImageList != null && page.ImageIndex >= 0 && page.ImageIndex < ImageList.Images.Count) {
934 width += ImageList.ImageSize.Width + 2;
935 item_size.Height = ImageList.ImageSize.Height + 3;
939 if (width < MinimumTabWidth)
940 width = MinimumTabWidth;
941 if (page.Row != prev_row)
942 ypos = 1;
944 page.TabBounds = new Rectangle (xpos + (row_count - page.Row) * ((item_size.Height - 2) + spacing.Width),
945 ypos, item_size.Height - 2, width);
947 if (page.Row != prev_row) {
948 if (SizeMode == TabSizeMode.FillToRight && !ShowSlider) {
949 FillRowV (begin_prev, i - 1, ((row_width - TabPages [i - 1].TabBounds.Bottom) / (i - begin_prev)), spacing);
951 begin_prev = i;
954 ypos += width + spacing.Width;
955 prev_row = page.Row;
958 if (SizeMode == TabSizeMode.FillToRight && !ShowSlider) {
959 FillRowV (begin_prev, TabPages.Count - 1,
960 ((row_width - TabPages [TabPages.Count - 1].TabBounds.Bottom) / (TabPages.Count - begin_prev)), spacing);
963 if (SelectedIndex != -1) {
964 ExpandSelected (TabPages [SelectedIndex], 2, row_width - 1);
968 private void SizeTabs (int row_width)
970 int ypos = CalcYPos ();
971 int prev_row = 1;
972 Size spacing = TabSpacing;
973 int xpos = 4;
974 int begin_prev = 0;
976 if (TabPages.Count == 0)
977 return;
979 prev_row = TabPages [0].Row;
981 // Reset the slider position if the slider isn't needed
982 // anymore (ie window size was increased so all tabs are visible)
983 if (!show_slider)
984 slider_pos = 0;
986 for (int i = slider_pos; i < TabPages.Count; i++) {
987 TabPage page = TabPages [i];
988 int width;
990 if (SizeMode == TabSizeMode.Fixed) {
991 width = item_size.Width;
992 } else {
993 width = (int) DeviceContext.MeasureString (page.Text, Font).Width + (Padding.X * 2);
994 if (ImageList != null && page.ImageIndex >= 0 && page.ImageIndex < ImageList.Images.Count) {
995 width += ImageList.ImageSize.Width + 2;
996 item_size.Height = ImageList.ImageSize.Height + 3;
1000 if (width < MinimumTabWidth)
1001 width = MinimumTabWidth;
1002 if (page.Row != prev_row)
1003 xpos = 4;
1005 page.TabBounds = new Rectangle (xpos,
1006 ypos + (row_count - page.Row) * (item_size.Height + spacing.Height),
1007 width, item_size.Height);
1009 if (page.Row != prev_row) {
1010 if (SizeMode == TabSizeMode.FillToRight && !ShowSlider) {
1011 FillRow (begin_prev, i - 1, ((row_width - TabPages [i - 1].TabBounds.Right) / (i - begin_prev)), spacing);
1013 begin_prev = i;
1016 xpos += width + 1 + spacing.Width;
1017 prev_row = page.Row;
1020 if (SizeMode == TabSizeMode.FillToRight && !ShowSlider) {
1021 FillRow (begin_prev, TabPages.Count - 1,
1022 ((row_width - TabPages [TabPages.Count - 1].TabBounds.Right) / (TabPages.Count - begin_prev)), spacing);
1025 if (SelectedIndex != -1) {
1026 ExpandSelected (TabPages [SelectedIndex], 2, row_width - 1);
1030 private void FillRow (int start, int end, int amount, Size spacing)
1032 int xpos = TabPages [start].TabBounds.Left;
1033 for (int i = start; i <= end; i++) {
1034 TabPage page = TabPages [i];
1035 int left = xpos;
1036 int width = (i == end ? Width - left - 3 : page.TabBounds.Width + amount);
1038 page.TabBounds = new Rectangle (left, page.TabBounds.Top,
1039 width, page.TabBounds.Height);
1040 xpos = page.TabBounds.Right + 1 + spacing.Width;
1044 private void FillRowV (int start, int end, int amount, Size spacing)
1046 int ypos = TabPages [start].TabBounds.Top;
1047 for (int i = start; i <= end; i++) {
1048 TabPage page = TabPages [i];
1049 int top = ypos;
1050 int height = (i == end ? Height - top - 5 : page.TabBounds.Height + amount);
1052 page.TabBounds = new Rectangle (page.TabBounds.Left, top,
1053 page.TabBounds.Width, height);
1054 ypos = page.TabBounds.Bottom + 1;
1058 private void ExpandSelected (TabPage page, int left_edge, int right_edge)
1060 if (Appearance != TabAppearance.Normal)
1061 return;
1063 if (Alignment == TabAlignment.Top || Alignment == TabAlignment.Bottom) {
1064 int l = page.TabBounds.Left - 4;
1065 int r = page.TabBounds.Right + 4;
1066 int y = page.TabBounds.Y;
1067 int h = page.TabBounds.Height + 3;
1069 if (l < left_edge)
1070 l = left_edge;
1071 if (r > right_edge && SizeMode != TabSizeMode.Normal)
1072 r = right_edge;
1073 if (Alignment == TabAlignment.Top)
1074 y -= 1;
1075 if (Alignment == TabAlignment.Bottom)
1076 y -= 2;
1078 page.TabBounds = new Rectangle (l, y, r - l, h);
1079 } else {
1080 int l = page.TabBounds.Left - 3;
1081 int r = page.TabBounds.Right + 3;
1082 int t = page.TabBounds.Top - 3;
1083 int b = page.TabBounds.Bottom + 3;
1085 if (t < left_edge)
1086 t = left_edge;
1087 if (b > right_edge)
1088 b = right_edge;
1090 page.TabBounds = new Rectangle (l, t, r - l, b - t);
1094 private void Draw (Graphics dc, Rectangle clip)
1096 ThemeEngine.Current.DrawTabControl (dc, clip, this);
1099 private TabPage GetTab (int index)
1101 return Controls [index] as TabPage;
1104 private void SetTab (int index, TabPage value)
1106 ((IList) Controls).Insert (index, value);
1107 Redraw ();
1110 internal void Redraw ()
1112 if (!IsHandleCreated)
1113 return;
1115 ResizeTabPages ();
1116 Refresh ();
1119 #endregion // Internal & Private Methods
1121 #region Events
1122 [Browsable(false)]
1123 [EditorBrowsable(EditorBrowsableState.Never)]
1124 public new event EventHandler BackColorChanged {
1125 add { base.BackColorChanged += value; }
1126 remove { base.BackColorChanged -= value; }
1129 [Browsable(false)]
1130 [EditorBrowsable(EditorBrowsableState.Never)]
1131 public new event EventHandler BackgroundImageChanged {
1132 add { base.BackgroundImageChanged += value; }
1133 remove { base.BackgroundImageChanged -= value; }
1136 [Browsable(false)]
1137 [EditorBrowsable(EditorBrowsableState.Never)]
1138 public new event EventHandler ForeColorChanged {
1139 add { base.ForeColorChanged += value; }
1140 remove { base.ForeColorChanged -= value; }
1143 [Browsable(false)]
1144 [EditorBrowsable(EditorBrowsableState.Never)]
1145 public new event PaintEventHandler Paint {
1146 add { base.Paint += value; }
1147 remove { base.Paint -= value; }
1150 [Browsable(false)]
1151 [EditorBrowsable(EditorBrowsableState.Never)]
1152 public new event EventHandler TextChanged {
1153 add { base.TextChanged += value; }
1154 remove { base.TextChanged -= value; }
1157 static object DrawItemEvent = new object ();
1158 static object SelectedIndexChangedEvent = new object ();
1160 public event DrawItemEventHandler DrawItem {
1161 add { Events.AddHandler (DrawItemEvent, value); }
1162 remove { Events.RemoveHandler (DrawItemEvent, value); }
1165 public event EventHandler SelectedIndexChanged {
1166 add { Events.AddHandler (SelectedIndexChangedEvent, value); }
1167 remove { Events.RemoveHandler (SelectedIndexChangedEvent, value); }
1170 #if NET_2_0
1171 static object SelectedEvent = new object ();
1173 public event TabControlEventHandler Selected {
1174 add { Events.AddHandler (SelectedEvent, value); }
1175 remove { Events.RemoveHandler (SelectedEvent, value); }
1178 static object DeselectedEvent = new object ();
1180 public event TabControlEventHandler Deselected
1182 add { Events.AddHandler (DeselectedEvent, value); }
1183 remove { Events.RemoveHandler (DeselectedEvent, value); }
1186 static object SelectingEvent = new object ();
1188 public event TabControlCancelEventHandler Selecting
1190 add { Events.AddHandler (SelectingEvent, value); }
1191 remove { Events.RemoveHandler (SelectingEvent, value); }
1194 static object DeselectingEvent = new object ();
1196 public event TabControlCancelEventHandler Deselecting
1198 add { Events.AddHandler (DeselectingEvent, value); }
1199 remove { Events.RemoveHandler (DeselectingEvent, value); }
1201 #endif
1202 #endregion // Events
1205 #region Class TaControl.ControlCollection
1206 public new class ControlCollection : System.Windows.Forms.Control.ControlCollection {
1208 private TabControl owner;
1210 public ControlCollection (TabControl owner) : base (owner)
1212 this.owner = owner;
1215 public override void Add (Control value)
1217 TabPage page = value as TabPage;
1218 if (page == null)
1219 throw new ArgumentException ("Cannot add " +
1220 value.GetType ().Name + " to TabControl. " +
1221 "Only TabPages can be directly added to TabControls.");
1223 page.SetVisible (false);
1224 base.Add (value);
1225 if (owner.TabCount == 1 && owner.selected_index < 0)
1226 owner.SelectedIndex = 0;
1227 owner.Redraw ();
1230 public override void Remove (Control value)
1232 TabPage page = value as TabPage;
1233 if (page != null) {
1234 int index = owner.IndexForTabPage (page);
1235 if (index < owner.SelectedIndex || owner.SelectedIndex == Count - 1)
1236 owner.SelectedIndex--;
1238 base.Remove (value);
1241 #endregion // Class TabControl.ControlCollection
1243 #region Class TabPage.TabPageCollection
1244 public class TabPageCollection : IList, ICollection, IEnumerable {
1246 private TabControl owner;
1248 public TabPageCollection (TabControl owner)
1250 if (owner == null)
1251 throw new ArgumentNullException ("Value cannot be null.");
1252 this.owner = owner;
1255 [Browsable(false)]
1256 public int Count {
1257 get { return owner.Controls.Count; }
1260 public bool IsReadOnly {
1261 get { return false; }
1264 public virtual TabPage this [int index] {
1265 get {
1266 return owner.GetTab (index);
1268 set {
1269 owner.SetTab (index, value);
1273 bool ICollection.IsSynchronized {
1274 get { return false; }
1277 object ICollection.SyncRoot {
1278 get { return this; }
1281 bool IList.IsFixedSize {
1282 get { return false; }
1285 object IList.this [int index] {
1286 get {
1287 return owner.GetTab (index);
1289 set {
1290 owner.SetTab (index, (TabPage) value);
1294 public void Add (TabPage page)
1296 if (page == null)
1297 throw new ArgumentNullException ("Value cannot be null.");
1298 owner.Controls.Add (page);
1301 public void AddRange (TabPage [] pages)
1303 if (pages == null)
1304 throw new ArgumentNullException ("Value cannot be null.");
1305 owner.Controls.AddRange (pages);
1308 public virtual void Clear ()
1310 owner.Controls.Clear ();
1313 public bool Contains (TabPage page)
1315 if (page == null)
1316 throw new ArgumentNullException ("Value cannot be null.");
1317 return owner.Controls.Contains (page);
1320 public IEnumerator GetEnumerator ()
1322 return owner.Controls.GetEnumerator ();
1325 public int IndexOf (TabPage page)
1327 return owner.Controls.IndexOf (page);
1330 public void Remove (TabPage page)
1332 owner.Controls.Remove (page);
1335 public void RemoveAt (int index)
1337 owner.Controls.RemoveAt (index);
1340 void ICollection.CopyTo (Array dest, int index)
1342 owner.Controls.CopyTo (dest, index);
1345 int IList.Add (object value)
1347 TabPage page = value as TabPage;
1348 if (value == null)
1349 throw new ArgumentException ("value");
1350 owner.Controls.Add (page);
1351 return owner.Controls.IndexOf (page);
1354 bool IList.Contains (object value)
1356 TabPage page = value as TabPage;
1357 if (page == null)
1358 return false;
1359 return Contains (page);
1362 int IList.IndexOf (object value)
1364 TabPage page = value as TabPage;
1365 if (page == null)
1366 return -1;
1367 return IndexOf ((TabPage) page);
1370 void IList.Insert (int index, object value)
1372 throw new NotSupportedException ();
1375 void IList.Remove (object value)
1377 TabPage page = value as TabPage;
1378 if (page == null)
1379 return;
1380 Remove ((TabPage) value);
1383 #endregion // Class TabPage.TabPageCollection