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-2005 Novell, Inc.
23 // Jackson Harper (jackson@ximian.com)
27 using System.Collections;
28 using System.ComponentModel;
29 using System.ComponentModel.Design;
31 using System.Runtime.InteropServices;
33 namespace System.Windows.Forms {
35 [ComVisibleAttribute (true)]
36 [ClassInterfaceAttribute (ClassInterfaceType.AutoDispatch)]
38 [DefaultEvent("SelectedIndexChanged")]
39 [DefaultProperty("TabPages")]
40 [Designer("System.Windows.Forms.Design.TabControlDesigner, " + Consts.AssemblySystem_Design, "System.ComponentModel.Design.IDesigner")]
41 public class TabControl : Control {
43 private int selected_index = -1;
44 private TabAlignment alignment;
45 private TabAppearance appearance;
46 private TabDrawMode draw_mode;
47 private bool multiline;
48 private ImageList image_list;
49 private Size item_size = Size.Empty;
50 private Point padding;
51 private int row_count = 0;
52 private bool hottrack;
53 private TabPageCollection tab_pages;
54 private bool show_tool_tips;
55 private TabSizeMode size_mode;
56 private bool show_slider = false;
57 private ButtonState right_slider_state;
58 private ButtonState left_slider_state;
59 private int slider_pos = 0;
61 private bool rightToLeftLayout;
65 #region Public Constructors
68 tab_pages = new TabPageCollection (this);
69 SetStyle (ControlStyles.UserPaint, false);
70 padding = ThemeEngine.Current.TabControlDefaultPadding;
71 item_size = ThemeEngine.Current.TabControlDefaultItemSize;
73 MouseDown += new MouseEventHandler (MouseDownHandler);
74 MouseUp += new MouseEventHandler (MouseUpHandler);
75 SizeChanged += new EventHandler (SizeChangedHandler);
78 #endregion // Public Constructors
80 #region Public Instance Properties
81 [DefaultValue(TabAlignment.Top)]
83 [RefreshProperties(RefreshProperties.All)]
84 public TabAlignment Alignment {
85 get { return alignment; }
87 if (alignment == value)
90 if (alignment == TabAlignment.Left || alignment == TabAlignment.Right)
96 [DefaultValue(TabAppearance.Normal)]
98 public TabAppearance Appearance {
99 get { return appearance; }
101 if (appearance == value)
109 [EditorBrowsable(EditorBrowsableState.Never)]
110 public override Color BackColor {
111 get { return ThemeEngine.Current.ColorControl; }
112 set { /* nothing happens on set on MS */ }
116 [EditorBrowsable(EditorBrowsableState.Never)]
117 public override Image BackgroundImage {
118 get { return base.BackgroundImage; }
119 set { base.BackgroundImage = value; }
124 [EditorBrowsable (EditorBrowsableState.Never)]
125 public override ImageLayout BackgroundImageLayout {
126 get { return base.BackgroundImageLayout; }
127 set { base.BackgroundImageLayout = value; }
131 public override Rectangle DisplayRectangle {
133 return ThemeEngine.Current.TabControlGetDisplayRectangle (this);
138 [EditorBrowsable (EditorBrowsableState.Never)]
139 protected override bool DoubleBuffered {
140 get { return base.DoubleBuffered; }
141 set { base.DoubleBuffered = value; }
145 [DefaultValue(TabDrawMode.Normal)]
146 public TabDrawMode DrawMode {
147 get { return draw_mode; }
149 if (draw_mode == value)
157 [EditorBrowsable(EditorBrowsableState.Never)]
158 public override Color ForeColor {
159 get { return base.ForeColor; }
160 set { base.ForeColor = value; }
163 [DefaultValue(false)]
164 public bool HotTrack {
165 get { return hottrack; }
167 if (hottrack == value)
175 [RefreshProperties (RefreshProperties.Repaint)]
178 public ImageList ImageList {
179 get { return image_list; }
180 set { image_list = value; }
184 public Size ItemSize {
189 if (value.Height < 0 || value.Width < 0)
190 throw new ArgumentException ("'" + value + "' is not a valid value for 'ItemSize'.");
196 [DefaultValue(false)]
197 public bool Multiline {
198 get { return multiline; }
200 if (multiline == value)
203 if (!multiline && alignment == TabAlignment.Left || alignment == TabAlignment.Right)
204 alignment = TabAlignment.Top;
210 public new Point Padding {
211 get { return padding; }
213 if (value.X < 0 || value.Y < 0)
214 throw new ArgumentException ("'" + value + "' is not a valid value for 'Padding'.");
215 if (padding == value)
224 [MonoTODO ("Saves the value and raises event, but needs actual implementation call")]
226 [DefaultValue (false)]
227 public virtual bool RightToLeftLayout {
228 get { return this.rightToLeftLayout; }
230 if (value != this.rightToLeftLayout) {
231 this.rightToLeftLayout = value;
232 this.OnRightToLeftLayoutChanged (EventArgs.Empty);
239 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
240 public int RowCount {
241 get { return row_count; }
246 public int SelectedIndex {
247 get { return selected_index; }
252 throw new ArgumentOutOfRangeException ("SelectedIndex", "Value of '" + value + "' is valid for 'SelectedIndex'. " +
253 "'SelectedIndex' must be greater than or equal to -1.");
255 throw new ArgumentException ("'" + value + "' is not a valid value for 'value'. " +
256 "'value' must be greater than or equal to -1.");
259 if (!this.IsHandleCreated) {
260 if (selected_index != value) {
261 selected_index = value;
263 OnSelectedIndexChanged (EventArgs.Empty);
269 if (value >= TabCount) {
270 if (value != selected_index)
271 OnSelectedIndexChanged (EventArgs.Empty);
275 if (value == selected_index) {
276 if (selected_index > -1)
277 Invalidate(GetTabRect (selected_index));
282 TabControlCancelEventArgs ret = new TabControlCancelEventArgs (SelectedTab, selected_index, false, TabControlAction.Deselecting);
288 int old_index = selected_index;
289 int new_index = value;
291 selected_index = new_index;
294 ret = new TabControlCancelEventArgs (SelectedTab, selected_index, false, TabControlAction.Selecting);
297 selected_index = old_index;
304 Rectangle invalid = Rectangle.Empty;
305 bool refresh = false;
307 if (new_index != -1 && show_slider && new_index < slider_pos) {
308 slider_pos = new_index;
312 if (new_index != -1) {
313 int le = TabPages[new_index].TabBounds.Right;
314 int re = ThemeEngine.Current.TabControlGetLeftScrollRect (this).Left;
315 if (show_slider && le > re) {
317 for (i = 0; i < new_index - 1; i++) {
318 if (TabPages [i].TabBounds.Left < 0) // tab scrolled off the visible area, ignore
321 if (TabPages [new_index].TabBounds.Right - TabPages[i].TabBounds.Right < re) {
331 if (old_index != -1 && new_index != -1) {
333 invalid = GetTabRect (old_index);
334 ((TabPage) Controls[old_index]).SetVisible (false);
337 TabPage selected = null;
339 if (new_index != -1) {
340 selected = (TabPage) Controls[new_index];
341 invalid = Rectangle.Union (invalid, GetTabRect (new_index));
342 selected.SetVisible (true);
345 OnSelectedIndexChanged (EventArgs.Empty);
352 } else if (new_index != -1 && selected.Row != BottomRow) {
353 DropRow (TabPages[new_index].Row);
354 // calculating what to invalidate here seems to be slower then just
355 // refreshing the whole thing
360 // The lines are drawn on the edges of the tabs so the invalid area should
361 // needs to include the extra pixels of line width.
362 if (appearance == TabAppearance.Normal) {
363 invalid.Inflate (6, 4);
365 Invalidate (invalid);
371 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
372 public TabPage SelectedTab {
374 if (selected_index == -1)
376 return tab_pages [selected_index];
379 int index = IndexForTabPage (value);
380 if (index == selected_index)
382 SelectedIndex = index;
386 [DefaultValue(false)]
388 public bool ShowToolTips {
389 get { return show_tool_tips; }
391 if (show_tool_tips == value)
393 show_tool_tips = value;
398 [DefaultValue(TabSizeMode.Normal)]
399 [RefreshProperties(RefreshProperties.Repaint)]
400 public TabSizeMode SizeMode {
401 get { return size_mode; }
403 if (size_mode == value)
411 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
412 public int TabCount {
414 return tab_pages.Count;
419 [Editor ("System.Windows.Forms.Design.TabPageCollectionEditor, " + Consts.AssemblySystem_Design, typeof (System.Drawing.Design.UITypeEditor))]
423 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
424 [MergableProperty(false)]
425 public TabPageCollection TabPages {
426 get { return tab_pages; }
431 [EditorBrowsable(EditorBrowsableState.Never)]
432 public override string Text {
433 get { return base.Text; }
434 set { base.Text = value; }
436 #endregion // Public Instance Properties
438 #region Internal Properties
439 internal bool ShowSlider {
440 get { return show_slider; }
441 set { show_slider = value; }
444 internal int SliderPos {
445 get { return slider_pos; }
448 internal ButtonState RightSliderState {
449 get { return right_slider_state; }
452 internal ButtonState LeftSliderState {
453 get { return left_slider_state; }
456 #endregion // Internal Properties
458 #region Protected Instance Properties
459 protected override CreateParams CreateParams {
461 CreateParams c = base.CreateParams;
466 protected override Size DefaultSize {
467 get { return new Size (200, 100); }
470 #endregion // Protected Instance Properties
472 #region Public Instance Methods
473 public Rectangle GetTabRect (int index)
475 TabPage page = GetTab (index);
476 return page.TabBounds;
479 public Control GetControl (int index)
481 return GetTab (index);
485 public void SelectTab (TabPage tabPage)
488 throw new ArgumentNullException ("tabPage");
490 SelectTab (this.tab_pages [tabPage]);
493 public void SelectTab (string tabPageName)
495 if (tabPageName == null)
496 throw new ArgumentNullException ("tabPageName");
498 SelectTab (this.tab_pages [tabPageName]);
501 public void SelectTab (int index)
503 if (index < 0 || index > this.tab_pages.Count - 1)
504 throw new ArgumentOutOfRangeException ("index");
506 SelectedIndex = index;
509 public void DeselectTab (TabPage tabPage)
512 throw new ArgumentNullException ("tabPage");
514 DeselectTab (this.tab_pages [tabPage]);
517 public void DeselectTab (string tabPageName)
519 if (tabPageName == null)
520 throw new ArgumentNullException ("tabPageName");
522 DeselectTab (this.tab_pages [tabPageName]);
525 public void DeselectTab (int index)
527 if (index == SelectedIndex) {
528 if (index >= 0 && index < this.tab_pages.Count - 1)
529 SelectedIndex = ++index;
537 public override string ToString ()
539 string res = String.Concat (base.ToString (),
540 ", TabPages.Count: ",
543 res = String.Concat (res, ", TabPages[0]: ",
548 #endregion // Public Instance Methods
550 #region Protected Instance Methods
553 protected override Control.ControlCollection CreateControlsInstance ()
555 return new TabControl.ControlCollection (this);
558 protected override void CreateHandle ()
560 base.CreateHandle ();
561 selected_index = (selected_index >= TabCount ? (TabCount > 0 ? 0 : -1) : selected_index);
564 if (selected_index > -1)
565 this.SelectedTab.SetVisible(true);
567 tab_pages[0].SetVisible(true);
572 protected override void OnHandleCreated (EventArgs e)
574 base.OnHandleCreated (e);
577 protected override void OnHandleDestroyed (EventArgs e)
579 base.OnHandleDestroyed (e);
582 protected override void Dispose (bool disposing)
584 base.Dispose (disposing);
590 protected virtual void OnDrawItem (DrawItemEventArgs e)
592 if (DrawMode != TabDrawMode.OwnerDrawFixed)
595 DrawItemEventHandler eh = (DrawItemEventHandler)(Events [DrawItemEvent]);
600 internal void OnDrawItemInternal (DrawItemEventArgs e)
605 protected override void OnFontChanged (EventArgs e)
607 base.OnFontChanged (e);
611 protected override void OnResize (EventArgs e)
616 protected override void OnStyleChanged (EventArgs e)
618 base.OnStyleChanged (e);
621 protected virtual void OnSelectedIndexChanged (EventArgs e)
623 EventHandler eh = (EventHandler) (Events[SelectedIndexChangedEvent]);
628 internal override void OnPaintInternal (PaintEventArgs pe)
630 Draw (pe.Graphics, pe.ClipRectangle);
635 protected override void OnEnter (EventArgs e)
638 if (SelectedTab != null)
639 SelectedTab.FireEnter ();
642 protected override void OnLeave (EventArgs e)
644 if (SelectedTab != null)
645 SelectedTab.FireLeave ();
649 [EditorBrowsable (EditorBrowsableState.Advanced)]
650 protected virtual void OnRightToLeftLayoutChanged (EventArgs e)
652 EventHandler eh = (EventHandler) (Events[RightToLeftLayoutChangedEvent]);
657 [EditorBrowsable (EditorBrowsableState.Never)]
658 protected override void ScaleCore (float dx, float dy)
660 base.ScaleCore (dx, dy);
663 protected virtual void OnDeselecting (TabControlCancelEventArgs e)
665 TabControlCancelEventHandler eh = (TabControlCancelEventHandler) (Events[DeselectingEvent]);
670 OnDeselected (new TabControlEventArgs (SelectedTab, selected_index, TabControlAction.Deselected));
673 protected virtual void OnDeselected (TabControlEventArgs e)
675 TabControlEventHandler eh = (TabControlEventHandler) (Events[DeselectedEvent]);
679 if (this.SelectedTab != null)
680 this.SelectedTab.FireLeave ();
683 protected virtual void OnSelecting (TabControlCancelEventArgs e)
685 TabControlCancelEventHandler eh = (TabControlCancelEventHandler) (Events[SelectingEvent]);
690 OnSelected (new TabControlEventArgs (SelectedTab, selected_index, TabControlAction.Selected));
693 protected virtual void OnSelected (TabControlEventArgs e)
695 TabControlEventHandler eh = (TabControlEventHandler) (Events[SelectedEvent]);
699 if (this.SelectedTab != null)
700 this.SelectedTab.FireEnter ();
707 protected override bool ProcessKeyPreview (ref Message m)
709 return base.ProcessKeyPreview (ref m);
712 protected override void OnKeyDown (KeyEventArgs ke)
714 if (ke.KeyCode == Keys.Tab && (ke.KeyData & Keys.Control) != 0) {
715 if ((ke.KeyData & Keys.Shift) == 0)
716 SelectedIndex = (SelectedIndex + 1) % TabCount;
718 SelectedIndex = (SelectedIndex + TabCount - 1) % TabCount;
720 } else if (ke.KeyCode == Keys.Home) {
723 } else if (ke.KeyCode == Keys.End) {
724 SelectedIndex = TabCount - 1;
726 } else if (ke.KeyCode == Keys.Left && SelectedIndex > 0) {
729 } else if (ke.KeyCode == Keys.Right && SelectedIndex < TabCount - 1) {
737 protected override bool IsInputKey (Keys keyData)
739 switch (keyData & Keys.KeyCode) {
746 return base.IsInputKey (keyData);
750 #region Pages Collection
751 protected void RemoveAll ()
756 protected virtual object [] GetItems ()
758 TabPage [] pages = new TabPage [Controls.Count];
759 Controls.CopyTo (pages, 0);
763 protected virtual object [] GetItems (Type baseType)
765 object[] pages = (object[])Array.CreateInstance (baseType, Controls.Count);
766 Controls.CopyTo (pages, 0);
771 protected void UpdateTabSelection (bool updateFocus)
776 protected string GetToolTipText (object item)
778 TabPage page = (TabPage) item;
779 return page.ToolTipText;
782 protected override void WndProc (ref Message m)
784 switch ((Msg)m.Msg) {
785 case Msg.WM_SETFOCUS:
786 if (selected_index == -1 && this.TabCount > 0)
787 this.SelectedIndex = 0;
788 if (selected_index != -1)
789 Invalidate(GetTabRect(selected_index));
790 base.WndProc (ref m);
792 case Msg.WM_KILLFOCUS:
793 if (selected_index != -1)
794 Invalidate(GetTabRect(selected_index));
795 base.WndProc (ref m);
798 base.WndProc (ref m);
803 #endregion // Protected Instance Methods
805 #region Internal & Private Methods
806 private bool CanScrollRight {
808 return (slider_pos < TabCount - 1);
812 private bool CanScrollLeft {
813 get { return slider_pos > 0; }
816 private void MouseDownHandler (object sender, MouseEventArgs e)
819 Rectangle right = ThemeEngine.Current.TabControlGetRightScrollRect (this);
820 Rectangle left = ThemeEngine.Current.TabControlGetLeftScrollRect (this);
821 if (right.Contains (e.X, e.Y)) {
822 right_slider_state = ButtonState.Pushed;
823 if (CanScrollRight) {
827 switch (this.Alignment) {
828 case TabAlignment.Top:
829 Invalidate (new Rectangle (0, 0, Width, ItemSize.Height));
831 case TabAlignment.Bottom:
832 Invalidate (new Rectangle (0, DisplayRectangle.Bottom, Width, Height - DisplayRectangle.Bottom));
834 case TabAlignment.Left:
835 Invalidate (new Rectangle (0, 0, DisplayRectangle.Left, Height));
837 case TabAlignment.Right:
838 Invalidate (new Rectangle (DisplayRectangle.Right, 0, Width - DisplayRectangle.Right, Height));
846 } else if (left.Contains (e.X, e.Y)) {
847 left_slider_state = ButtonState.Pushed;
852 switch (this.Alignment) {
853 case TabAlignment.Top:
854 Invalidate (new Rectangle (0, 0, Width, ItemSize.Height));
856 case TabAlignment.Bottom:
857 Invalidate (new Rectangle (0, DisplayRectangle.Bottom, Width, Height - DisplayRectangle.Bottom));
859 case TabAlignment.Left:
860 Invalidate (new Rectangle (0, 0, DisplayRectangle.Left, Height));
862 case TabAlignment.Right:
863 Invalidate (new Rectangle (DisplayRectangle.Right, 0, Width - DisplayRectangle.Right, Height));
873 int count = Controls.Count;
874 for (int i = SliderPos; i < count; i++) {
875 if (!GetTabRect (i).Contains (e.X, e.Y))
882 private void MouseUpHandler (object sender, MouseEventArgs e)
884 if (ShowSlider && (left_slider_state == ButtonState.Pushed || right_slider_state == ButtonState.Pushed)) {
886 if (left_slider_state == ButtonState.Pushed)
887 invalid = ThemeEngine.Current.TabControlGetLeftScrollRect (this);
889 invalid = ThemeEngine.Current.TabControlGetRightScrollRect (this);
890 left_slider_state = ButtonState.Normal;
891 right_slider_state = ButtonState.Normal;
893 Invalidate (invalid);
897 private void SizeChangedHandler (object sender, EventArgs e)
902 internal int IndexForTabPage (TabPage page)
904 for (int i = 0; i < tab_pages.Count; i++) {
905 if (page == tab_pages [i])
911 private void ResizeTabPages ()
915 Rectangle r = DisplayRectangle;
916 foreach (TabPage page in Controls) {
921 private int MinimumTabWidth {
923 return ThemeEngine.Current.TabControlMinimumTabWidth;
927 private Size TabSpacing {
929 return ThemeEngine.Current.TabControlGetSpacing (this);
933 private void CalcTabRows ()
936 case TabAlignment.Right:
937 case TabAlignment.Left:
938 CalcTabRows (Height);
946 private void CalcTabRows (int row_width)
950 Size spacing = TabSpacing;
952 if (TabPages.Count > 0)
956 int scrollerWidth = ThemeEngine.Current.TabControlScrollerWidth * 2;
958 for (int i = 0; i < TabPages.Count; i++) {
959 TabPage page = TabPages [i];
961 SizeTab (page, i, row_width - scrollerWidth, ref xpos, ref ypos, spacing, 0, ref aux, true);
964 if (SelectedIndex != -1 && TabPages.Count > SelectedIndex && TabPages[SelectedIndex].Row != BottomRow)
965 DropRow (TabPages [SelectedIndex].Row);
968 private int BottomRow {
972 private int Direction
976 case TabAlignment.Right:
977 case TabAlignment.Bottom:
985 private void DropRow (int row)
987 if (Appearance != TabAppearance.Normal)
990 int bottom = BottomRow;
991 int direction = Direction;
993 foreach (TabPage page in TabPages) {
994 if (page.Row == row) {
996 } else if (direction == 1 && page.Row < row) {
997 page.Row += direction;
998 } else if (direction == -1 && page.Row > row) {
999 page.Row += direction;
1004 private int CalcYPos ()
1006 if (Alignment == TabAlignment.Bottom || Alignment == TabAlignment.Left)
1007 return ThemeEngine.Current.TabControlGetPanelRect (this).Bottom;
1009 if (Appearance == TabAppearance.Normal)
1010 return this.ClientRectangle.Y + ThemeEngine.Current.TabControlSelectedDelta.Y;
1012 return this.ClientRectangle.Y;
1016 private int CalcXPos ()
1018 if (Alignment == TabAlignment.Right)
1019 return ThemeEngine.Current.TabControlGetPanelRect (this).Right;
1021 if (Appearance == TabAppearance.Normal)
1022 return this.ClientRectangle.X + ThemeEngine.Current.TabControlSelectedDelta.X;
1024 return this.ClientRectangle.X;
1027 private void SizeTabs ()
1029 switch (Alignment) {
1030 case TabAlignment.Right:
1031 case TabAlignment.Left:
1032 SizeTabs (Height, true);
1035 SizeTabs (Width, false);
1040 private void SizeTabs (int row_width, bool vertical)
1045 Size spacing = TabSpacing;
1048 row_width -= ThemeEngine.Current.TabControlScrollerWidth * 2;
1049 if (TabPages.Count == 0)
1052 prev_row = TabPages [0].Row;
1054 // Reset the slider position if the slider isn't needed
1055 // anymore (ie window size was increased so all tabs are visible)
1059 // set X = -1 for marking tabs that are not visible due to scrolling
1060 for (int i = 0; i < slider_pos; i++) {
1061 TabPage page = TabPages[i];
1062 Rectangle x = page.TabBounds;
1068 for (int i = slider_pos; i < TabPages.Count; i++) {
1069 TabPage page = TabPages[i];
1070 SizeTab (page, i, row_width, ref xpos, ref ypos, spacing, prev_row, ref begin_prev, false);
1071 prev_row = page.Row;
1074 if (SizeMode == TabSizeMode.FillToRight && !ShowSlider) {
1075 FillRow (begin_prev, TabPages.Count - 1,
1076 ((row_width - TabPages [TabPages.Count - 1].TabBounds.Right) / (TabPages.Count - begin_prev)),
1080 if (SelectedIndex != -1) {
1081 ExpandSelected (TabPages [SelectedIndex], 0, row_width - 1);
1085 private void SizeTab (TabPage page, int i, int row_width, ref int xpos, ref int ypos,
1086 Size spacing, int prev_row, ref int begin_prev, bool widthOnly)
1088 int width, height = 0;
1090 if (SizeMode == TabSizeMode.Fixed) {
1091 width = item_size.Width;
1093 width = MeasureStringWidth (DeviceContext, page.Text, Font);
1094 width += (Padding.X * 2) + 2;
1096 if (ImageList != null && page.ImageIndex >= 0 && page.ImageIndex < ImageList.Images.Count) {
1097 width += ImageList.ImageSize.Width + ThemeEngine.Current.TabControlImagePadding.X;
1099 int image_size = ImageList.ImageSize.Height + ThemeEngine.Current.TabControlImagePadding.Y;
1100 if (item_size.Height < image_size)
1101 item_size.Height = image_size;
1105 height = item_size.Height - ThemeEngine.Current.TabControlSelectedDelta.Height; // full height only for selected tab
1107 if (width < MinimumTabWidth)
1108 width = MinimumTabWidth;
1110 if (i == SelectedIndex)
1111 width += ThemeEngine.Current.TabControlSelectedSpacing;
1114 page.TabBounds = new Rectangle (xpos, 0, width, 0);
1115 page.Row = row_count;
1116 if (xpos + width > row_width && multiline) {
1119 } else if (xpos + width > row_width) {
1122 if (i == selected_index && show_slider) {
1123 for (int j = i-1; j >= 0; j--) {
1124 if (TabPages [j].TabBounds.Left < xpos + width - row_width) {
1131 if (page.Row != prev_row) {
1135 switch (Alignment) {
1136 case TabAlignment.Top:
1137 page.TabBounds = new Rectangle (
1139 ypos + (height + spacing.Height) * (row_count - page.Row) + CalcYPos (),
1143 case TabAlignment.Bottom:
1144 page.TabBounds = new Rectangle (
1146 ypos + (height + spacing.Height) * (row_count - page.Row) + CalcYPos (),
1150 case TabAlignment.Left:
1151 if (Appearance == TabAppearance.Normal) {
1152 // tab rows are positioned right to left
1153 page.TabBounds = new Rectangle (
1154 ypos + (height + spacing.Height) * (row_count - page.Row) + CalcXPos (),
1159 // tab rows are positioned left to right
1160 page.TabBounds = new Rectangle (
1161 ypos + (height + spacing.Height) * (page.Row - 1) + CalcXPos (),
1168 case TabAlignment.Right:
1169 if (Appearance == TabAppearance.Normal) {
1170 // tab rows are positioned left to right
1171 page.TabBounds = new Rectangle (
1172 ypos + (height + spacing.Height) * (page.Row - 1) + CalcXPos (),
1177 // tab rows are positioned right to left
1178 page.TabBounds = new Rectangle (
1179 ypos + (height + spacing.Height) * (row_count - page.Row) + CalcXPos (),
1188 if (page.Row != prev_row) {
1189 if (SizeMode == TabSizeMode.FillToRight && !ShowSlider) {
1190 FillRow (begin_prev, i - 1, ((row_width - TabPages [i - 1].TabBounds.Right) / (i - begin_prev)), spacing);
1196 xpos += width + spacing.Width + ThemeEngine.Current.TabControlColSpacing;
1199 private void FillRow (int start, int end, int amount, Size spacing, bool vertical)
1202 FillRowV (start, end, amount, spacing);
1204 FillRow (start, end, amount, spacing);
1207 private void FillRow (int start, int end, int amount, Size spacing)
1209 int xpos = TabPages [start].TabBounds.Left;
1210 for (int i = start; i <= end; i++) {
1211 TabPage page = TabPages [i];
1213 int width = (i == end ? Width - left - 3 : page.TabBounds.Width + amount);
1215 page.TabBounds = new Rectangle (left, page.TabBounds.Top,
1216 width, page.TabBounds.Height);
1217 xpos = page.TabBounds.Right + 1 + spacing.Width;
1221 private void FillRowV (int start, int end, int amount, Size spacing)
1223 int ypos = TabPages [start].TabBounds.Top;
1224 for (int i = start; i <= end; i++) {
1225 TabPage page = TabPages [i];
1227 int height = (i == end ? Height - top - 5 : page.TabBounds.Height + amount);
1229 page.TabBounds = new Rectangle (page.TabBounds.Left, top,
1230 page.TabBounds.Width, height);
1231 ypos = page.TabBounds.Bottom + 1;
1235 private void ExpandSelected (TabPage page, int left_edge, int right_edge)
1237 if (Appearance != TabAppearance.Normal)
1240 Rectangle r = page.TabBounds;
1241 switch (Alignment) {
1242 case TabAlignment.Top:
1243 case TabAlignment.Left:
1244 r.Y -= ThemeEngine.Current.TabControlSelectedDelta.Y;
1245 r.X -= ThemeEngine.Current.TabControlSelectedDelta.X;
1247 case TabAlignment.Bottom:
1248 r.Y -= ThemeEngine.Current.TabControlSelectedDelta.Y;
1249 r.X -= ThemeEngine.Current.TabControlSelectedDelta.X;
1251 case TabAlignment.Right:
1252 r.Y -= ThemeEngine.Current.TabControlSelectedDelta.Y;
1253 r.X += ThemeEngine.Current.TabControlSelectedDelta.X;
1257 r.Width += ThemeEngine.Current.TabControlSelectedDelta.Width;
1258 r.Height += ThemeEngine.Current.TabControlSelectedDelta.Height;
1259 if (r.Left < left_edge)
1261 if (r.Right > right_edge && SizeMode != TabSizeMode.Normal)
1262 r.Width = right_edge - r.X;
1266 private void Draw (Graphics dc, Rectangle clip)
1268 ThemeEngine.Current.DrawTabControl (dc, clip, this);
1271 private TabPage GetTab (int index)
1273 return Controls [index] as TabPage;
1276 private void SetTab (int index, TabPage value)
1278 if (!tab_pages.Contains (value)) {
1279 this.Controls.Add (value);
1281 this.Controls.RemoveAt (index);
1282 this.Controls.SetChildIndex (value, index);
1286 private void InsertTab (int index, TabPage value)
1288 if (!tab_pages.Contains (value)) {
1289 this.Controls.Add (value);
1291 this.Controls.SetChildIndex (value, index);
1295 internal void Redraw ()
1297 if (!IsHandleCreated)
1304 private int MeasureStringWidth (Graphics graphics, string text, Font font)
1306 if (text == String.Empty)
1308 StringFormat format = new StringFormat();
1309 RectangleF rect = new RectangleF(0, 0, 1000, 1000);
1310 CharacterRange[] ranges = { new CharacterRange(0, text.Length) };
1311 Region[] regions = new Region[1];
1313 format.SetMeasurableCharacterRanges(ranges);
1314 format.FormatFlags = StringFormatFlags.NoClip;
1315 format.FormatFlags |= StringFormatFlags.NoWrap;
1316 regions = graphics.MeasureCharacterRanges(text + "I", font, rect, format);
1317 rect = regions[0].GetBounds(graphics);
1319 return (int)(rect.Width);
1322 #endregion // Internal & Private Methods
1326 [EditorBrowsable(EditorBrowsableState.Never)]
1327 public new event EventHandler BackColorChanged {
1328 add { base.BackColorChanged += value; }
1329 remove { base.BackColorChanged -= value; }
1333 [EditorBrowsable(EditorBrowsableState.Never)]
1334 public new event EventHandler BackgroundImageChanged {
1335 add { base.BackgroundImageChanged += value; }
1336 remove { base.BackgroundImageChanged -= value; }
1341 [EditorBrowsable (EditorBrowsableState.Never)]
1342 public new event EventHandler BackgroundImageLayoutChanged
1344 add { base.BackgroundImageLayoutChanged += value; }
1345 remove { base.BackgroundImageLayoutChanged -= value; }
1350 [EditorBrowsable(EditorBrowsableState.Never)]
1351 public new event EventHandler ForeColorChanged {
1352 add { base.ForeColorChanged += value; }
1353 remove { base.ForeColorChanged -= value; }
1357 [EditorBrowsable(EditorBrowsableState.Never)]
1358 public new event PaintEventHandler Paint {
1359 add { base.Paint += value; }
1360 remove { base.Paint -= value; }
1364 [EditorBrowsable(EditorBrowsableState.Never)]
1365 public new event EventHandler TextChanged {
1366 add { base.TextChanged += value; }
1367 remove { base.TextChanged -= value; }
1370 static object DrawItemEvent = new object ();
1371 static object SelectedIndexChangedEvent = new object ();
1373 public event DrawItemEventHandler DrawItem {
1374 add { Events.AddHandler (DrawItemEvent, value); }
1375 remove { Events.RemoveHandler (DrawItemEvent, value); }
1378 public event EventHandler SelectedIndexChanged {
1379 add { Events.AddHandler (SelectedIndexChangedEvent, value); }
1380 remove { Events.RemoveHandler (SelectedIndexChangedEvent, value); }
1384 static object SelectedEvent = new object ();
1386 public event TabControlEventHandler Selected {
1387 add { Events.AddHandler (SelectedEvent, value); }
1388 remove { Events.RemoveHandler (SelectedEvent, value); }
1391 static object DeselectedEvent = new object ();
1393 public event TabControlEventHandler Deselected
1395 add { Events.AddHandler (DeselectedEvent, value); }
1396 remove { Events.RemoveHandler (DeselectedEvent, value); }
1399 static object SelectingEvent = new object ();
1401 public event TabControlCancelEventHandler Selecting
1403 add { Events.AddHandler (SelectingEvent, value); }
1404 remove { Events.RemoveHandler (SelectingEvent, value); }
1407 static object DeselectingEvent = new object ();
1409 public event TabControlCancelEventHandler Deselecting
1411 add { Events.AddHandler (DeselectingEvent, value); }
1412 remove { Events.RemoveHandler (DeselectingEvent, value); }
1415 static object RightToLeftLayoutChangedEvent = new object ();
1416 public event EventHandler RightToLeftLayoutChanged
1418 add { Events.AddHandler (RightToLeftLayoutChangedEvent, value); }
1419 remove { Events.RemoveHandler (RightToLeftLayoutChangedEvent, value); }
1422 #endregion // Events
1425 #region Class TaControl.ControlCollection
1427 [ComVisible (false)]
1429 public new class ControlCollection : System.Windows.Forms.Control.ControlCollection {
1431 private TabControl owner;
1433 public ControlCollection (TabControl owner) : base (owner)
1438 public override void Add (Control value)
1440 TabPage page = value as TabPage;
1442 throw new ArgumentException ("Cannot add " +
1443 value.GetType ().Name + " to TabControl. " +
1444 "Only TabPages can be directly added to TabControls.");
1446 page.SetVisible (false);
1448 if (owner.TabCount == 1 && owner.selected_index < 0)
1449 owner.SelectedIndex = 0;
1453 public override void Remove (Control value)
1455 TabPage page = value as TabPage;
1457 int index = owner.IndexForTabPage (page);
1458 if (index < owner.SelectedIndex || owner.SelectedIndex == Count - 1)
1459 owner.SelectedIndex--;
1461 base.Remove (value);
1464 #endregion // Class TabControl.ControlCollection
1466 #region Class TabPage.TabPageCollection
1467 public class TabPageCollection : IList, ICollection, IEnumerable {
1469 private TabControl owner;
1471 public TabPageCollection (TabControl owner)
1474 throw new ArgumentNullException ("Value cannot be null.");
1480 get { return owner.Controls.Count; }
1483 public bool IsReadOnly {
1484 get { return false; }
1487 public virtual TabPage this [int index] {
1489 return owner.GetTab (index);
1492 owner.SetTab (index, value);
1496 public virtual TabPage this [string key] {
1498 if (string.IsNullOrEmpty (key))
1501 int index = this.IndexOfKey (key);
1502 if (index < 0 || index >= this.Count)
1510 internal int this[TabPage tabPage] {
1512 if (tabPage == null)
1515 for (int i = 0; i < this.Count; i++)
1516 if (this[i].Equals (tabPage))
1523 bool ICollection.IsSynchronized {
1524 get { return false; }
1527 object ICollection.SyncRoot {
1528 get { return this; }
1531 bool IList.IsFixedSize {
1532 get { return false; }
1535 object IList.this [int index] {
1537 return owner.GetTab (index);
1540 owner.SetTab (index, (TabPage) value);
1544 public void Add (TabPage value)
1547 throw new ArgumentNullException ("Value cannot be null.");
1548 owner.Controls.Add (value);
1552 public void Add (string text)
1554 TabPage page = new TabPage (text);
1558 public void Add (string key, string text)
1560 TabPage page = new TabPage (text);
1565 public void Add (string key, string text, int imageIndex)
1567 TabPage page = new TabPage (text);
1569 page.ImageIndex = imageIndex;
1573 // .Net sets the ImageKey, but does not show the image when this is used
1574 public void Add (string key, string text, string imageKey)
1576 TabPage page = new TabPage (text);
1578 page.ImageKey = imageKey;
1583 public void AddRange (TabPage [] pages)
1586 throw new ArgumentNullException ("Value cannot be null.");
1587 owner.Controls.AddRange (pages);
1590 public virtual void Clear ()
1592 owner.Controls.Clear ();
1595 public bool Contains (TabPage page)
1598 throw new ArgumentNullException ("Value cannot be null.");
1599 return owner.Controls.Contains (page);
1603 public virtual bool ContainsKey (string key)
1605 int index = this.IndexOfKey (key);
1606 return (index >= 0 && index < this.Count);
1610 public IEnumerator GetEnumerator ()
1612 return owner.Controls.GetEnumerator ();
1615 public int IndexOf (TabPage page)
1617 return owner.Controls.IndexOf (page);
1621 public virtual int IndexOfKey(string key)
1623 if (string.IsNullOrEmpty (key))
1626 for (int i = 0; i < this.Count; i++) {
1627 if (string.Compare (this[i].Name, key, true,
1628 System.Globalization.CultureInfo.InvariantCulture) == 0) {
1637 public void Remove (TabPage value)
1639 owner.Controls.Remove (value);
1642 public void RemoveAt (int index)
1644 owner.Controls.RemoveAt (index);
1648 public virtual void RemoveByKey (string key)
1650 int index = this.IndexOfKey (key);
1651 if (index >= 0 && index < this.Count)
1652 this.RemoveAt (index);
1656 void ICollection.CopyTo (Array dest, int index)
1658 owner.Controls.CopyTo (dest, index);
1661 int IList.Add (object value)
1663 TabPage page = value as TabPage;
1665 throw new ArgumentException ("value");
1666 owner.Controls.Add (page);
1667 return owner.Controls.IndexOf (page);
1670 bool IList.Contains (object value)
1672 TabPage page = value as TabPage;
1675 return Contains (page);
1678 int IList.IndexOf (object value)
1680 TabPage page = value as TabPage;
1683 return IndexOf ((TabPage) page);
1686 void IList.Insert (int index, object value)
1688 throw new NotSupportedException ();
1692 public void Insert (int index, string text)
1694 owner.InsertTab (index, new TabPage (text));
1697 public void Insert (int index, TabPage tabPage)
1699 owner.InsertTab (index, tabPage);
1702 public void Insert (int index, string key, string text)
1704 TabPage page = new TabPage(text);
1706 owner.InsertTab (index, page);
1709 public void Insert (int index, string key, string text, int imageIndex)
1711 TabPage page = new TabPage(text);
1713 owner.InsertTab (index, page);
1714 page.ImageIndex = imageIndex;
1717 public void Insert (int index, string key, string text, string imageKey)
1719 TabPage page = new TabPage(text);
1721 owner.InsertTab (index, page);
1722 page.ImageKey = imageKey;
1725 void IList.Remove (object value)
1727 TabPage page = value as TabPage;
1730 Remove ((TabPage) value);
1733 #endregion // Class TabPage.TabPageCollection