|
//------------------------------------------------------------------------------
// <copyright file="TabControl.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
//------------------------------------------------------------------------------
/*
*/
namespace System.Windows.Forms {
using System;
using System.Collections;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.Configuration.Assemblies;
using Microsoft.Win32;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Drawing;
using System.Drawing.Design;
using System.Runtime.InteropServices;
using System.Runtime.Remoting;
using System.Runtime.Serialization.Formatters;
using System.Security;
using System.Security.Permissions;
using System.Windows.Forms.Layout;
using System.Globalization;
/// <include file='doc\TabControl.uex' path='docs/doc[@for="TabControl"]/*' />
/// <devdoc>
/// The TabControl. This control has a lot of the functionality of a TabStrip
/// but manages a list of TabPages which are the 'pages' that appear on each tab.
/// </devdoc>
[
ComVisible(true),
ClassInterface(ClassInterfaceType.AutoDispatch),
DefaultProperty("TabPages"),
DefaultEvent("SelectedIndexChanged"),
Designer("System.Windows.Forms.Design.TabControlDesigner, " + AssemblyRef.SystemDesign),
SRDescription(SR.DescriptionTabControl)
]
public class TabControl : Control {
private static readonly Size DEFAULT_ITEMSIZE = Size.Empty;
private static readonly Point DEFAULT_PADDING = new Point(6, 3);
//properties
private TabPageCollection tabCollection;
private TabAlignment alignment = TabAlignment.Top;
private TabDrawMode drawMode = TabDrawMode.Normal;
private ImageList imageList = null;
private Size itemSize = DEFAULT_ITEMSIZE;
private Point padding = DEFAULT_PADDING;
private TabSizeMode sizeMode = TabSizeMode.Normal;
private TabAppearance appearance = TabAppearance.Normal;
private Rectangle cachedDisplayRect = Rectangle.Empty;
private bool currentlyScaling = false;
private int selectedIndex = -1;
private Size cachedSize = Size.Empty;
private string controlTipText = String.Empty;
private bool handleInTable;
//events
private EventHandler onSelectedIndexChanged;
private DrawItemEventHandler onDrawItem;
//events
private static readonly object EVENT_DESELECTING = new object();
private static readonly object EVENT_DESELECTED = new object();
private static readonly object EVENT_SELECTING = new object();
private static readonly object EVENT_SELECTED = new object();
private static readonly object EVENT_RIGHTTOLEFTLAYOUTCHANGED = new object();
private const int TABCONTROLSTATE_hotTrack = 0x00000001;
private const int TABCONTROLSTATE_multiline = 0x00000002;
private const int TABCONTROLSTATE_showToolTips = 0x00000004;
private const int TABCONTROLSTATE_getTabRectfromItemSize = 0x00000008;
private const int TABCONTROLSTATE_fromCreateHandles = 0x00000010;
private const int TABCONTROLSTATE_UISelection = 0x00000020;
private const int TABCONTROLSTATE_selectFirstControl = 0x00000040;
private const int TABCONTROLSTATE_insertingItem = 0x00000080;
private const int TABCONTROLSTATE_autoSize = 0x00000100;
// PERF: take all the bools and put them into a state variable
private System.Collections.Specialized.BitVector32 tabControlState; // see TABCONTROLSTATE_ consts above
/// <include file='doc\TabControl.uex' path='docs/doc[@for="TabControl.tabBaseReLayoutMessage"]/*' />
/// <devdoc>
/// This message is posted by the control to itself after a TabPage is
/// added to it. On certain occasions, after items are added to a
/// TabControl in quick succession, TCM_ADJUSTRECT calls return the wrong
/// display rectangle. When the message is received, the control calls
/// updateTabSelection() to layout the TabPages correctly.
/// </devdoc>
/// <internalonly/>
private readonly int tabBaseReLayoutMessage = SafeNativeMethods.RegisterWindowMessage(Application.WindowMessagesVersion + "_TabBaseReLayout");
//state
private TabPage[] tabPages;
private int tabPageCount;
private int lastSelection;
private bool rightToLeftLayout = false;
private bool skipUpdateSize;
/// <include file='doc\TabControl.uex' path='docs/doc[@for="TabControl.TabControl"]/*' />
/// <devdoc>
/// Constructs a TabBase object, usually as the base class for a TabStrip or TabControl.
/// </devdoc>
public TabControl()
: base() {
tabControlState = new System.Collections.Specialized.BitVector32(0x00000000);
tabCollection = new TabPageCollection(this);
SetStyle(ControlStyles.UserPaint, false);
}
/// <include file='doc\TabControl.uex' path='docs/doc[@for="TabControl.Alignment"]/*' />
/// <devdoc>
/// Returns on what area of the control the tabs reside on (A TabAlignment value).
/// The possibilities are Top (the default), Bottom, Left, and Right. When alignment
/// is left or right, the Multiline property is ignored and Multiline is implicitly on.
/// If the alignment is anything other than top, TabAppearance.FlatButtons degenerates
/// to TabAppearance.Buttons.
/// </devdoc>
[
SRCategory(SR.CatBehavior),
Localizable(true),
DefaultValue(TabAlignment.Top),
RefreshProperties(RefreshProperties.All),
SRDescription(SR.TabBaseAlignmentDescr)
]
public TabAlignment Alignment {
get {
return alignment;
}
set {
if (this.alignment != value) {
//valid values are 0x0 to 0x3
if (!ClientUtils.IsEnumValid(value, (int)value, (int)TabAlignment.Top, (int)TabAlignment.Right)){
throw new InvalidEnumArgumentException("value", (int)value, typeof(TabAlignment));
}
this.alignment = value;
if (this.alignment == TabAlignment.Left || this.alignment == TabAlignment.Right)
Multiline = true;
RecreateHandle();
}
}
}
/// <include file='doc\TabControl.uex' path='docs/doc[@for="TabControl.Appearance"]/*' />
/// <devdoc>
/// Indicates whether the tabs in the tabstrip look like regular tabs, or if they look
/// like buttons as seen in the Windows 95 taskbar.
/// If the alignment is anything other than top, TabAppearance.FlatButtons degenerates
/// to TabAppearance.Buttons.
/// </devdoc>
[
SRCategory(SR.CatBehavior),
Localizable(true),
DefaultValue(TabAppearance.Normal),
SRDescription(SR.TabBaseAppearanceDescr)
]
public TabAppearance Appearance {
get {
if (appearance == TabAppearance.FlatButtons && alignment != TabAlignment.Top) {
return TabAppearance.Buttons;
}
else {
return appearance;
}
}
set {
if (this.appearance != value) {
//valid values are 0x0 to 0x2
if (!ClientUtils.IsEnumValid(value, (int)value, (int)TabAppearance.Normal, (int)TabAppearance.FlatButtons)){
throw new InvalidEnumArgumentException("value", (int)value, typeof(TabAppearance));
}
this.appearance = value;
RecreateHandle();
//Fire OnStyleChanged(EventArgs.Empty) here since we are no longer calling UpdateStyles( ) but always reCreating the Handle.
OnStyleChanged(EventArgs.Empty);
}
}
}
/// <include file='doc\TabControl.uex' path='docs/doc[@for="TabControl.BackColor"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
public override Color BackColor {
get {
//The tab control can only be rendered in 1 color: System's Control color.
//So, always return this value... otherwise, we're inheriting the forms backcolor
//and passing it on to the pab pages.
return SystemColors.Control;
}
set {
}
}
/// <include file='doc\TabControl.uex' path='docs/doc[@for="TabControl.BackColorChanged"]/*' />
/// <internalonly/>
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
new public event EventHandler BackColorChanged {
add {
base.BackColorChanged += value;
}
remove {
base.BackColorChanged -= value;
}
}
/// <include file='doc\TabControl.uex' path='docs/doc[@for="TabControl.BackgroundImage"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
public override Image BackgroundImage {
get {
return base.BackgroundImage;
}
set {
base.BackgroundImage = value;
}
}
/// <include file='doc\TabControl.uex' path='docs/doc[@for="TabControl.BackgroundImageChanged"]/*' />
/// <internalonly/>
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
new public event EventHandler BackgroundImageChanged {
add {
base.BackgroundImageChanged += value;
}
remove {
base.BackgroundImageChanged -= value;
}
}
/// <include file='doc\TabControl.uex' path='docs/doc[@for="TabControl.BackgroundImageLayout"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
public override ImageLayout BackgroundImageLayout {
get {
return base.BackgroundImageLayout;
}
set {
base.BackgroundImageLayout = value;
}
}
/// <include file='doc\TabControl.uex' path='docs/doc[@for="TabControl.BackgroundImageLayoutChanged"]/*' />
/// <internalonly/>
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
new public event EventHandler BackgroundImageLayoutChanged {
add {
base.BackgroundImageLayoutChanged += value;
}
remove {
base.BackgroundImageLayoutChanged -= value;
}
}
/// <include file='doc\TabControl.uex' path='docs/doc[@for="TabControl.DefaultSize"]/*' />
/// <devdoc>
/// Deriving classes can override this to configure a default size for their control.
/// This is more efficient than setting the size in the control's constructor.
/// </devdoc>
protected override Size DefaultSize {
get {
return new Size(200, 100);
}
}
/// <include file='doc\TabControl.uex' path='docs/doc[@for="TabControl.DoubleBuffered"]/*' />
/// <devdoc>
/// This property is overridden and hidden from statement completion
/// on controls that are based on Win32 Native Controls.
/// </devdoc>
[EditorBrowsable(EditorBrowsableState.Never)]
protected override bool DoubleBuffered {
get {
return base.DoubleBuffered;
}
set {
base.DoubleBuffered = value;
}
}
/// <include file='doc\TabControl.uex' path='docs/doc[@for="TabControl.ForeColor"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
public override Color ForeColor {
get {
return base.ForeColor;
}
set {
base.ForeColor = value;
}
}
/// <include file='doc\TabControl.uex' path='docs/doc[@for="TabControl.ForeColorChanged"]/*' />
/// <internalonly/>
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
new public event EventHandler ForeColorChanged {
add {
base.ForeColorChanged += value;
}
remove {
base.ForeColorChanged -= value;
}
}
/// <include file='doc\TabControl.uex' path='docs/doc[@for="TabControl.CreateParams"]/*' />
/// <devdoc>
/// Returns the parameters needed to create the handle. Inheriting classes
/// can override this to provide extra functionality. They should not,
/// however, forget to call base.getCreateParams() first to get the struct
/// filled up with the basic info.
/// </devdoc>
/// <internalonly/>
protected override CreateParams CreateParams {
[SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)]
get {
CreateParams cp = base.CreateParams;
cp.ClassName = NativeMethods.WC_TABCONTROL;
// set up window styles
//
if (Multiline == true) cp.Style |= NativeMethods.TCS_MULTILINE;
if (drawMode == TabDrawMode.OwnerDrawFixed) cp.Style |= NativeMethods.TCS_OWNERDRAWFIXED;
if (ShowToolTips && !DesignMode) {
cp.Style |= NativeMethods.TCS_TOOLTIPS;
}
if (alignment == TabAlignment.Bottom ||
alignment == TabAlignment.Right)
cp.Style |= NativeMethods.TCS_BOTTOM;
if (alignment == TabAlignment.Left ||
alignment == TabAlignment.Right)
cp.Style |= NativeMethods.TCS_VERTICAL | NativeMethods.TCS_MULTILINE;
if (tabControlState[TABCONTROLSTATE_hotTrack]) {
cp.Style |= NativeMethods.TCS_HOTTRACK;
}
if (appearance == TabAppearance.Normal)
cp.Style |= NativeMethods.TCS_TABS;
else {
cp.Style |= NativeMethods.TCS_BUTTONS;
if (appearance == TabAppearance.FlatButtons && alignment == TabAlignment.Top)
cp.Style |= NativeMethods.TCS_FLATBUTTONS;
}
switch (sizeMode) {
case TabSizeMode.Normal:
cp.Style |= NativeMethods.TCS_RAGGEDRIGHT;
break;
case TabSizeMode.FillToRight:
cp.Style |= NativeMethods.TCS_RIGHTJUSTIFY;
break;
case TabSizeMode.Fixed:
cp.Style |= NativeMethods.TCS_FIXEDWIDTH;
break;
}
if (RightToLeft == RightToLeft.Yes && RightToLeftLayout == true) {
//We want to turn on mirroring for Form explicitly.
cp.ExStyle |= NativeMethods.WS_EX_LAYOUTRTL | NativeMethods.WS_EX_NOINHERITLAYOUT;
//Don't need these styles when mirroring is turned on.
cp.ExStyle &= ~(NativeMethods.WS_EX_RTLREADING | NativeMethods.WS_EX_RIGHT | NativeMethods.WS_EX_LEFTSCROLLBAR);
}
return cp;
}
}
/// <include file='doc\TabControl.uex' path='docs/doc[@for="TabControl.DisplayRectangle"]/*' />
/// <devdoc>
/// The rectangle that represents the Area of the tab strip not
/// taken up by the tabs, borders, or anything else owned by the Tab. This
/// is typically the rectangle you want to use to place the individual
/// children of the tab strip.
/// </devdoc>
public override Rectangle DisplayRectangle {
get {
// Null out cachedDisplayRect whenever we do anything to change it...
//
if (!cachedDisplayRect.IsEmpty)
return cachedDisplayRect;
Rectangle bounds = Bounds;
NativeMethods.RECT rect = NativeMethods.RECT.FromXYWH(bounds.X, bounds.Y, bounds.Width, bounds.Height);
// We force a handle creation here, because otherwise the DisplayRectangle will be wildly inaccurate
//
if (!IsDisposed)
{
// Since this is called thru the OnResize (and Layout) which is triggered by SetExtent if the TabControl is hosted as
// a ActiveX control, so check if this is ActiveX and dont force Handle Creation here as the native code breaks in this case.
// please Refer to VsWhidbey :: 288940
if (!IsActiveX)
{
if (!IsHandleCreated) {
CreateHandle();
}
}
if (IsHandleCreated)
{
SendMessage(NativeMethods.TCM_ADJUSTRECT, 0, ref rect);
}
}
Rectangle r = Rectangle.FromLTRB(rect.left, rect.top, rect.right, rect.bottom);
Point p = this.Location;
r.X -= p.X;
r.Y -= p.Y;
cachedDisplayRect = r;
return r;
}
}
/// <include file='doc\TabControl.uex' path='docs/doc[@for="TabControl.DrawMode"]/*' />
/// <devdoc>
/// The drawing mode of the tabs in the tab strip. This will indicate
/// </devdoc>
[
SRCategory(SR.CatBehavior),
DefaultValue(TabDrawMode.Normal),
SRDescription(SR.TabBaseDrawModeDescr)
]
public TabDrawMode DrawMode {
get {
return drawMode;
}
set {
//valid values are 0x0 to 0x1
if (!ClientUtils.IsEnumValid(value, (int)value, (int)TabDrawMode.Normal, (int)TabDrawMode.OwnerDrawFixed)){
throw new InvalidEnumArgumentException("value", (int)value, typeof(TabDrawMode));
}
if (drawMode != value) {
drawMode = value;
RecreateHandle();
}
}
}
/// <include file='doc\TabControl.uex' path='docs/doc[@for="TabControl.HotTrack"]/*' />
/// <devdoc>
/// Indicates whether the tabs visually change when the mouse passes over them.
/// </devdoc>
[
SRCategory(SR.CatBehavior),
DefaultValue(false),
SRDescription(SR.TabBaseHotTrackDescr)
]
public bool HotTrack {
get {
return tabControlState[TABCONTROLSTATE_hotTrack];
}
set {
if (HotTrack != value) {
tabControlState[TABCONTROLSTATE_hotTrack] = value;
if (IsHandleCreated) {
RecreateHandle();
}
}
}
}
/// <include file='doc\TabControl.uex' path='docs/doc[@for="TabControl.ImageList"]/*' />
/// <devdoc>
/// Returns the imageList the control points at. This is where tabs that have imageIndex
/// set will get there images from.
/// </devdoc>
[
SRCategory(SR.CatAppearance),
RefreshProperties(RefreshProperties.Repaint),
DefaultValue(null),
SRDescription(SR.TabBaseImageListDescr)
]
public ImageList ImageList {
get {
return imageList;
}
set {
if (this.imageList != value) {
EventHandler recreateHandler = new EventHandler(ImageListRecreateHandle);
EventHandler disposedHandler = new EventHandler(DetachImageList);
if (imageList != null) {
imageList.RecreateHandle -= recreateHandler;
imageList.Disposed -= disposedHandler;
}
this.imageList = value;
IntPtr handle = (value != null) ? value.Handle : IntPtr.Zero;
if (IsHandleCreated)
SendMessage(NativeMethods.TCM_SETIMAGELIST, IntPtr.Zero, handle);
// Update the image list in the tab pages.
foreach (TabPage tabPage in TabPages) {
tabPage.ImageIndexer.ImageList = value;
}
if (value != null) {
value.RecreateHandle += recreateHandler;
value.Disposed += disposedHandler;
}
}
}
}
/// <include file='doc\TabControl.uex' path='docs/doc[@for="TabControl.ItemSize"]/*' />
/// <devdoc>
/// By default, tabs will automatically size themselves to fit their icon, if any, and their label.
/// However, the tab size can be explicity set by setting this property.
/// </devdoc>
[
SRCategory(SR.CatBehavior),
Localizable(true),
SRDescription(SR.TabBaseItemSizeDescr)
]
public Size ItemSize {
get {
if (itemSize.IsEmpty) {
// Obtain the current itemsize of the first tab from the winctl control
//
if (IsHandleCreated) {
tabControlState[TABCONTROLSTATE_getTabRectfromItemSize] = true;
return GetTabRect(0).Size;
}
else {
return DEFAULT_ITEMSIZE;
}
}
else {
return itemSize;
}
}
set {
if (value.Width < 0 || value.Height < 0) {
throw new ArgumentOutOfRangeException("ItemSize", SR.GetString(SR.InvalidArgument, "ItemSize", value.ToString()));
}
itemSize = value;
ApplyItemSize();
UpdateSize();
Invalidate();
}
}
/// <devdoc>
/// This private property is set by the TabPageCollection when the user calls "InsertItem".
/// The problem is when InsertItem is called then we add this item to the ControlsCollection (in addition to the TabPageCollection)
/// to keep both the collections is sync. But the controlCollection.Add is overriden to again ADD the item to the TabPageCollection.
/// So we keep this flag in order to aviod repeatd addition (only during insert)
/// When the Add ends ... we reset this flag.
/// </devdoc>
private bool InsertingItem {
get {
return (bool)tabControlState[TABCONTROLSTATE_insertingItem];
}
set {
tabControlState[TABCONTROLSTATE_insertingItem] = value;
}
}
/// <include file='doc\TabControl.uex' path='docs/doc[@for="TabControl.Multiline"]/*' />
/// <devdoc>
/// Indicates if there can be more than one row of tabs. By default [when
/// this property is false], if there are more tabs than available display
/// space, arrows are shown to let the user navigate between the extra
/// tabs, but only one row is shown. If this property is set to true, then
/// Windows spills extra tabs over on to second rows.
/// </devdoc>
[
SRCategory(SR.CatBehavior),
DefaultValue(false),
SRDescription(SR.TabBaseMultilineDescr)
]
public bool Multiline {
get {
return tabControlState[TABCONTROLSTATE_multiline];
}
set {
if (Multiline != value) {
tabControlState[TABCONTROLSTATE_multiline] = value;
if (Multiline == false && (this.alignment == TabAlignment.Left || this.alignment == TabAlignment.Right))
this.alignment = TabAlignment.Top;
RecreateHandle();
}
}
}
/// <include file='doc\TabControl.uex' path='docs/doc[@for="TabControl.Padding"]/*' />
/// <devdoc>
/// The amount of padding around the items in the individual tabs.
/// You can specify both horizontal and vertical padding.
/// </devdoc>
[
SRCategory(SR.CatBehavior),
Localizable(true),
SRDescription(SR.TabBasePaddingDescr)
]
public new Point Padding {
get {
return padding;
}
set {
//do some validation checking here, against min & max GridSize
//
if ( value.X < 0 || value.Y < 0 )
throw new ArgumentOutOfRangeException("Padding", SR.GetString(SR.InvalidArgument, "Padding", value.ToString()));
if (padding != value) {
padding = value;
if (IsHandleCreated) {
RecreateHandle();
}
}
}
}
/// <include file='doc\TabControl.uex' path='docs/doc[@for="TabControl.RightToLeftLayout"]/*' />
/// <devdoc>
/// This is used for international applications where the language
/// is written from RightToLeft. When this property is true,
// and the RightToLeft is true, mirroring will be turned on on the form, and
/// control placement and text will be from right to left.
/// </devdoc>
[
SRCategory(SR.CatAppearance),
Localizable(true),
DefaultValue(false),
SRDescription(SR.ControlRightToLeftLayoutDescr)
]
public virtual bool RightToLeftLayout {
get {
return rightToLeftLayout;
}
set {
if (value != rightToLeftLayout) {
rightToLeftLayout = value;
using(new LayoutTransaction(this, this, PropertyNames.RightToLeftLayout)) {
OnRightToLeftLayoutChanged(EventArgs.Empty);
}
}
}
}
/// <include file='doc\TabControl.uex' path='docs/doc[@for="TabControl.RowCount"]/*' />
/// <devdoc>
/// The number of rows currently being displayed in
/// the tab strip. This is most commonly used when the Multline property
/// is 'true' and you want to know how many rows the tabs are currently
/// taking up.
/// </devdoc>
[
SRCategory(SR.CatAppearance),
Browsable(false),
DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden),
SRDescription(SR.TabBaseRowCountDescr)
]
public int RowCount {
get {
int n;
n = unchecked( (int) (long)SendMessage(NativeMethods.TCM_GETROWCOUNT, 0, 0));
return n;
}
}
/// <include file='doc\TabControl.uex' path='docs/doc[@for="TabControl.SelectedIndex"]/*' />
/// <devdoc>
/// The index of the currently selected tab in the strip, if there
/// is one. If the value is -1, there is currently no selection. If the
/// value is 0 or greater, than the value is the index of the currently
/// selected tab.
/// </devdoc>
[
Browsable(false),
SRCategory(SR.CatBehavior),
DefaultValue(-1),
SRDescription(SR.selectedIndexDescr)
]
public int SelectedIndex {
get {
if (IsHandleCreated) {
int n;
n = unchecked( (int) (long)SendMessage(NativeMethods.TCM_GETCURSEL, 0, 0));
return n;
}
else {
return selectedIndex;
}
}
set {
if (value < -1) {
throw new ArgumentOutOfRangeException("SelectedIndex", SR.GetString(SR.InvalidLowBoundArgumentEx, "SelectedIndex", value.ToString(CultureInfo.CurrentCulture), (-1).ToString(CultureInfo.CurrentCulture)));
}
if (SelectedIndex != value) {
if (IsHandleCreated) {
// Guard Against CreateHandle ..
// And also if we are setting SelectedIndex ourselves from SelectNextTab..
if (!tabControlState[TABCONTROLSTATE_fromCreateHandles] && !tabControlState[TABCONTROLSTATE_selectFirstControl]) {
tabControlState[TABCONTROLSTATE_UISelection] = true;
// Fire Deselecting .. Deselected on currently selected TabPage...
if (WmSelChanging()) {
tabControlState[TABCONTROLSTATE_UISelection] = false;
return;
}
if(ValidationCancelled) {
tabControlState[TABCONTROLSTATE_UISelection] = false;
return;
}
}
SendMessage(NativeMethods.TCM_SETCURSEL, value, 0);
if (!tabControlState[TABCONTROLSTATE_fromCreateHandles] && !tabControlState[TABCONTROLSTATE_selectFirstControl]) {
// Fire Selecting & Selected .. Also if Selecting is Canceled..
// then retuern as we do not change the SelectedIndex...
tabControlState[TABCONTROLSTATE_selectFirstControl] = true;
if (WmSelChange ()) {
tabControlState[TABCONTROLSTATE_UISelection] = false;
tabControlState[TABCONTROLSTATE_selectFirstControl] = false;
return;
}
else
{
tabControlState[TABCONTROLSTATE_selectFirstControl] = false;
}
}
}
else {
selectedIndex = value;
}
}
}
}
/// <include file='doc\TabControl.uex' path='docs/doc[@for="TabControl.SelectedTab"]/*' />
/// <devdoc>
/// The selection to the given tab, provided it .equals a tab in the
/// list. The return value is the index of the tab that was selected,
/// or -1 if no tab was selected.
/// </devdoc>
[
SRCategory(SR.CatAppearance),
Browsable(false),
DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden),
SRDescription(SR.TabControlSelectedTabDescr)
]
public TabPage SelectedTab {
get {
return SelectedTabInternal;
}
set {
SelectedTabInternal = value;
}
}
internal TabPage SelectedTabInternal {
get {
int index = SelectedIndex;
if (index == -1) {
return null;
}
else {
Debug.Assert(0 <= index && index < tabPages.Length, "SelectedIndex returned an invalid index");
return tabPages[index];
}
}
set {
int index = FindTabPage(value);
SelectedIndex = index;
}
}
/// <include file='doc\TabControl.uex' path='docs/doc[@for="TabControl.SizeMode"]/*' />
/// <devdoc>
/// By default, tabs are big enough to display their text, and any space
/// on the right of the strip is left as such. However, you can also
/// set it such that the tabs are stretched to fill out the right extent
/// of the strip, if necessary, or you can set it such that all tabs
/// the same width.
/// </devdoc>
[
SRCategory(SR.CatBehavior),
DefaultValue(TabSizeMode.Normal),
SRDescription(SR.TabBaseSizeModeDescr),
RefreshProperties(RefreshProperties.Repaint)
]
public TabSizeMode SizeMode {
get {
return sizeMode;
}
set {
if (sizeMode == value) return;
//valid values are 0x0 to 0x2
if (!ClientUtils.IsEnumValid(value, (int)value, (int)TabSizeMode.Normal, (int)TabSizeMode.Fixed)){
throw new InvalidEnumArgumentException("value", (int)value, typeof(TabSizeMode));
}
sizeMode = value;
RecreateHandle();
}
}
/// <include file='doc\TabControl.uex' path='docs/doc[@for="TabControl.ShowToolTips"]/*' />
/// <devdoc>
/// Indicates whether tooltips are being shown for tabs that have tooltips set on
/// them.
/// </devdoc>
[
SRCategory(SR.CatBehavior),
DefaultValue(false),
Localizable(true),
SRDescription(SR.TabBaseShowToolTipsDescr)
]
public bool ShowToolTips {
get {
return tabControlState[TABCONTROLSTATE_showToolTips];
}
set {
if (ShowToolTips != value) {
tabControlState[TABCONTROLSTATE_showToolTips] = value;
RecreateHandle();
}
}
}
/// <include file='doc\TabControl.uex' path='docs/doc[@for="TabControl.TabCount"]/*' />
/// <devdoc>
/// Returns the number of tabs in the strip
/// </devdoc>
[
SRCategory(SR.CatAppearance),
Browsable(false),
DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden),
SRDescription(SR.TabBaseTabCountDescr)
]
public int TabCount {
get { return tabPageCount;}
}
/// <include file='doc\TabControl.uex' path='docs/doc[@for="TabControl.TabPages"]/*' />
/// <devdoc>
/// Returns the Collection of TabPages.
/// </devdoc>
[
SRCategory(SR.CatBehavior),
SRDescription(SR.TabControlTabsDescr),
DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden),
Editor("System.Windows.Forms.Design.TabPageCollectionEditor, " + AssemblyRef.SystemDesign, typeof(UITypeEditor)),
MergableProperty(false)
]
public TabPageCollection TabPages {
get {
return tabCollection;
}
}
/// <include file='doc\TabControl.uex' path='docs/doc[@for="TabControl.Text"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never), Bindable(false)]
public override string Text {
get {
return base.Text;
}
set {
base.Text = value;
}
}
/// <include file='doc\TabControl.uex' path='docs/doc[@for="TabControl.TextChanged"]/*' />
/// <internalonly/>
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
new public event EventHandler TextChanged {
add {
base.TextChanged += value;
}
remove {
base.TextChanged -= value;
}
}
/// <include file='doc\TabControl.uex' path='docs/doc[@for="TabControl.DrawItem"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
[SRCategory(SR.CatBehavior), SRDescription(SR.drawItemEventDescr)]
public event DrawItemEventHandler DrawItem {
add {
onDrawItem += value;
}
remove {
onDrawItem -= value;
}
}
/// <include file='doc\Form.uex' path='docs/doc[@for="Form.RightToLeftLayoutChanged"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
[SRCategory(SR.CatPropertyChanged), SRDescription(SR.ControlOnRightToLeftLayoutChangedDescr)]
public event EventHandler RightToLeftLayoutChanged {
add {
Events.AddHandler(EVENT_RIGHTTOLEFTLAYOUTCHANGED, value);
}
remove {
Events.RemoveHandler(EVENT_RIGHTTOLEFTLAYOUTCHANGED, value);
}
}
/// <include file='doc\TabControl.uex' path='docs/doc[@for="TabControl.SelectedIndexChanged"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
[SRCategory(SR.CatBehavior), SRDescription(SR.selectedIndexChangedEventDescr)]
public event EventHandler SelectedIndexChanged {
add {
onSelectedIndexChanged += value;
}
remove {
onSelectedIndexChanged -= value;
}
}
/// <include file='doc\TabControl.uex' path='docs/doc[@for="TabControl.Selecting"]/*' />
/// <devdoc>
/// <para>
/// Occurs before a tabpage is selected as the top tabPage.
/// </para>
/// </devdoc>
[SRCategory(SR.CatAction), SRDescription(SR.TabControlSelectingEventDescr)
]
public event TabControlCancelEventHandler Selecting {
add {
Events.AddHandler(EVENT_SELECTING, value);
}
remove {
Events.RemoveHandler(EVENT_SELECTING, value);
}
}
/// <include file='doc\TabControl.uex' path='docs/doc[@for="TabControl.Selected"]/*' />
/// <devdoc>
/// <para>
/// Occurs after a tabpage is selected as the top tabPage.
/// </para>
/// </devdoc>
[SRCategory(SR.CatAction), SRDescription(SR.TabControlSelectedEventDescr)
]
public event TabControlEventHandler Selected {
add {
Events.AddHandler(EVENT_SELECTED, value);
}
remove {
Events.RemoveHandler(EVENT_SELECTED, value);
}
}
/// <include file='doc\TabControl.uex' path='docs/doc[@for="TabControl.Deselecting"]/*' />
/// <devdoc>
/// <para>
/// Occurs before the visible property of the top tabpage is set to false.
/// </para>
/// </devdoc>
[SRCategory(SR.CatAction), SRDescription(SR.TabControlDeselectingEventDescr)
]
public event TabControlCancelEventHandler Deselecting {
add {
Events.AddHandler(EVENT_DESELECTING, value);
}
remove {
Events.RemoveHandler(EVENT_DESELECTING, value);
}
}
/// <include file='doc\TabControl.uex' path='docs/doc[@for="TabControl.Deselected"]/*' />
/// <devdoc>
/// <para>
/// Occurs after the visible property of the top tabpage is set to false.
/// </para>
/// </devdoc>
[SRCategory(SR.CatAction), SRDescription(SR.TabControlDeselectedEventDescr)
]
public event TabControlEventHandler Deselected {
add {
Events.AddHandler(EVENT_DESELECTED, value);
}
remove {
Events.RemoveHandler(EVENT_DESELECTED, value);
}
}
/// <include file='doc\TabControl.uex' path='docs/doc[@for="TabControl.OnPaint"]/*' />
/// <devdoc>
/// TabControl Onpaint.
/// </devdoc>
/// <internalonly/><hideinheritance/>
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
public new event PaintEventHandler Paint {
add {
base.Paint += value;
}
remove {
base.Paint -= value;
}
}
/// <include file='doc\TabControl.uex' path='docs/doc[@for="TabControl.AddItem"]/*' />
/// <devdoc>
/// </devdoc>
/// <internalonly/>
internal int AddTabPage(TabPage tabPage, NativeMethods.TCITEM_T tcitem) {
int index = AddNativeTabPage(tcitem);
if (index >= 0) {
Insert(index, tabPage);
}
return index;
}
internal int AddNativeTabPage(NativeMethods.TCITEM_T tcitem) {
int index = (int)UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.TCM_INSERTITEM, tabPageCount + 1, tcitem);
UnsafeNativeMethods.PostMessage(new HandleRef(this, Handle), tabBaseReLayoutMessage, IntPtr.Zero, IntPtr.Zero);
return index;
}
/// <include file='doc\TabControl.uex' path='docs/doc[@for="TabControl.ApplySize"]/*' />
/// <devdoc>
/// </devdoc>
/// <internalonly/>
internal void ApplyItemSize() {
if (IsHandleCreated && ShouldSerializeItemSize()) {
SendMessage(NativeMethods.TCM_SETITEMSIZE, 0, (int)NativeMethods.Util.MAKELPARAM(itemSize.Width, itemSize.Height));
}
cachedDisplayRect = Rectangle.Empty;
}
/// <include file='doc\TabControl.uex' path='docs/doc[@for="TabControl.BeginUpdate"]/*' />
/// <devdoc>
/// </devdoc>
/// <internalonly/>
internal void BeginUpdate() {
BeginUpdateInternal();
}
/// <include file='doc\TabControl.uex' path='docs/doc[@for="TabControl.CreateControlsInstance"]/*' />
/// <devdoc>
/// </devdoc>
/// <internalonly/>
protected override Control.ControlCollection CreateControlsInstance() {
return new ControlCollection(this);
}
/// <include file='doc\TabControl.uex' path='docs/doc[@for="TabControl.CreateHandle"]/*' />
/// <devdoc>
/// </devdoc>
/// <internalonly/>
protected override void CreateHandle() {
if (!RecreatingHandle) {
IntPtr userCookie = UnsafeNativeMethods.ThemingScope.Activate();
try {
NativeMethods.INITCOMMONCONTROLSEX icc = new NativeMethods.INITCOMMONCONTROLSEX();
icc.dwICC = NativeMethods.ICC_TAB_CLASSES;
SafeNativeMethods.InitCommonControlsEx(icc);
}
finally {
UnsafeNativeMethods.ThemingScope.Deactivate(userCookie);
}
}
base.CreateHandle();
}
private void DetachImageList(object sender, EventArgs e) {
ImageList = null;
}
/// <include file='doc\TabControl.uex' path='docs/doc[@for="TabControl.DeselectTab"]/*' />
/// <devdoc>
/// Allows the user to specify the index in Tabcontrol.TabPageCollection of the tabpage to be hidden.
/// </devdoc>
public void DeselectTab(int index) {
TabPage t = GetTabPage(index);
if (SelectedTab == t) {
if (0 <= index && index < TabPages.Count -1) {
SelectedTab = GetTabPage(++index);
}
else {
SelectedTab = GetTabPage(0);
}
}
}
/// <include file='doc\TabControl.uex' path='docs/doc[@for="TabControl.DeselectTab1"]/*' />
/// <devdoc>
/// Allows the user to specify the tabpage in Tabcontrol.TabPageCollection to be hidden.
/// </devdoc>
public void DeselectTab(TabPage tabPage) {
if (tabPage == null) {
throw new ArgumentNullException("tabPage");
}
int index = FindTabPage(tabPage);
DeselectTab(index);
}
/// <include file='doc\TabControl.uex' path='docs/doc[@for="TabControl.DeselectTab2"]/*' />
/// <devdoc>
/// Allows the user to specify the name of the tabpage in Tabcontrol.TabPageCollection to be hidden.
/// </devdoc>
public void DeselectTab(string tabPageName) {
if (tabPageName == null) {
throw new ArgumentNullException("tabPageName");
}
TabPage tabPage = TabPages[tabPageName];
DeselectTab(tabPage);
}
/// <include file='doc\TabControl.uex' path='docs/doc[@for="TabControl.Dispose"]/*' />
protected override void Dispose(bool disposing) {
if (disposing) {
if (imageList != null) {
imageList.Disposed -= new EventHandler(this.DetachImageList);
}
}
base.Dispose(disposing);
}
internal void EndUpdate() {
EndUpdate(true);
}
internal void EndUpdate(bool invalidate) {
EndUpdateInternal(invalidate);
}
internal int FindTabPage(TabPage tabPage) {
if (tabPages != null) {
for (int i = 0; i < tabPageCount; i++) {
if (tabPages[i].Equals(tabPage)) {
return i;
}
}
}
return -1;
}
/// <include file='doc\TabControl.uex' path='docs/doc[@for="TabControl.GetControl"]/*' />
/// <devdoc>
/// </devdoc>
/// <internalonly/>
public Control GetControl(int index) {
return(Control) GetTabPage(index);
}
internal TabPage GetTabPage(int index) {
if (index < 0 || index >= tabPageCount) {
throw new ArgumentOutOfRangeException("index", SR.GetString(SR.InvalidArgument, "index", index.ToString(CultureInfo.CurrentCulture)));
}
return tabPages[index];
}
/// <include file='doc\TabControl.uex' path='docs/doc[@for="TabControl.GetItems"]/*' />
/// <devdoc>
/// This has package scope so that TabStrip and TabControl can call it.
/// </devdoc>
/// <internalonly/>
protected virtual object[] GetItems() {
TabPage[] result = new TabPage[tabPageCount];
if (tabPageCount > 0) Array.Copy(tabPages, 0, result, 0, tabPageCount);
return result;
}
/// <include file='doc\TabControl.uex' path='docs/doc[@for="TabControl.GetItems1"]/*' />
/// <devdoc>
/// This has package scope so that TabStrip and TabControl can call it.
/// </devdoc>
/// <internalonly/>
protected virtual object[] GetItems(Type baseType) {
object[] result = (object[]) Array.CreateInstance(baseType, tabPageCount);
if (tabPageCount > 0) Array.Copy(tabPages, 0, result, 0, tabPageCount);
return result;
}
internal TabPage[] GetTabPages() {
return (TabPage[])GetItems();
}
/// <include file='doc\TabControl.uex' path='docs/doc[@for="TabControl.GetTabRect"]/*' />
/// <devdoc>
/// Retrieves the bounding rectangle for the given tab in the tab strip.
/// </devdoc>
public Rectangle GetTabRect(int index) {
if (index < 0 || (index >= tabPageCount && !tabControlState[TABCONTROLSTATE_getTabRectfromItemSize])) {
throw new ArgumentOutOfRangeException("index", SR.GetString(SR.InvalidArgument, "index", index.ToString(CultureInfo.CurrentCulture)));
}
tabControlState[TABCONTROLSTATE_getTabRectfromItemSize] = false ;
NativeMethods.RECT rect = new NativeMethods.RECT();
// normally, we would not want to create the handle for this, but since
// it is dependent on the actual physical display, we simply must.
if (!IsHandleCreated)
CreateHandle();
SendMessage(NativeMethods.TCM_GETITEMRECT, index, ref rect);
return Rectangle.FromLTRB(rect.left, rect.top, rect.right, rect.bottom);
}
/// <include file='doc\TabControl.uex' path='docs/doc[@for="TabControl.GetToolTipText"]/*' />
/// <devdoc>
/// </devdoc>
/// <internalonly/>
protected string GetToolTipText(object item) {
return((TabPage)item).ToolTipText;
}
private void ImageListRecreateHandle(object sender, EventArgs e) {
if (IsHandleCreated)
SendMessage(NativeMethods.TCM_SETIMAGELIST, 0, ImageList.Handle);
}
/// <include file='doc\TabControl.uex' path='docs/doc[@for="TabControl.Insert"]/*' />
/// <devdoc>
/// </devdoc>
/// <internalonly/>
internal void Insert(int index, TabPage tabPage) {
if (tabPages == null) {
tabPages = new TabPage[4];
}
else if (tabPages.Length == tabPageCount) {
TabPage[] newTabPages = new TabPage[tabPageCount * 2];
Array.Copy(tabPages, 0, newTabPages, 0, tabPageCount);
tabPages = newTabPages;
}
if (index < tabPageCount) {
Array.Copy(tabPages, index, tabPages, index + 1, tabPageCount - index);
}
tabPages[index] = tabPage;
tabPageCount++;
cachedDisplayRect = Rectangle.Empty;
ApplyItemSize();
if (Appearance == TabAppearance.FlatButtons) {
Invalidate();
}
}
/// <include file='doc\TabControl.uex' path='docs/doc[@for="TabControl.InsertItem"]/*' />
/// <devdoc>
/// This function is used by the Insert Logic to insert a tabPage in the current TabPage in the TabPageCollection.
/// </devdoc>
/// <internalonly/>
private void InsertItem(int index, TabPage tabPage) {
if (index < 0 || ((tabPages != null) && index > tabPageCount))
throw new ArgumentOutOfRangeException("index", SR.GetString(SR.InvalidArgument, "index", index.ToString(CultureInfo.CurrentCulture)));
if (tabPage == null)
throw new ArgumentNullException("tabPage");
int retIndex;
if (IsHandleCreated) {
NativeMethods.TCITEM_T tcitem = tabPage.GetTCITEM();
retIndex = (int)UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.TCM_INSERTITEM, index, tcitem);
if (retIndex >= 0) Insert(retIndex, tabPage);
}
}
/// <include file='doc\TabControl.uex' path='docs/doc[@for="TabControl.IsInputKey"]/*' />
/// <devdoc>
/// Handling special input keys, such as pgup, pgdown, home, end, etc...
/// </devdoc>
protected override bool IsInputKey(Keys keyData) {
if ((keyData & Keys.Alt) == Keys.Alt) return false;
switch (keyData & Keys.KeyCode) {
case Keys.PageUp:
case Keys.PageDown:
case Keys.Home:
case Keys.End:
return true;
}
return base.IsInputKey(keyData);
}
/// <include file='doc\TabControl.uex' path='docs/doc[@for="TabControl.OnHandleCreated"]/*' />
/// <devdoc>
/// This is a notification that the handle has been created.
/// We do some work here to configure the handle.
/// Overriders should call base.OnHandleCreated()
/// </devdoc>
/// <internalonly/>
protected override void OnHandleCreated(EventArgs e) {
//Add the handle to hashtable for Ids ..
NativeWindow.AddWindowToIDTable(this, this.Handle);
handleInTable = true;
// VSWhidbey 123853: Set the padding BEFORE setting the control's font (as done
// in base.OnHandleCreated()) so that the tab control will honor both the
// horizontal and vertical dimensions of the padding rectangle.
if (!padding.IsEmpty) {
SendMessage(NativeMethods.TCM_SETPADDING, 0, NativeMethods.Util.MAKELPARAM(padding.X, padding.Y));
}
base.OnHandleCreated(e);
cachedDisplayRect = Rectangle.Empty;
ApplyItemSize();
if (imageList != null) {
SendMessage(NativeMethods.TCM_SETIMAGELIST, 0, imageList.Handle);
}
if (ShowToolTips) {
IntPtr tooltipHwnd;
tooltipHwnd = SendMessage(NativeMethods.TCM_GETTOOLTIPS, 0, 0);
if (tooltipHwnd != IntPtr.Zero) {
SafeNativeMethods.SetWindowPos(new HandleRef(this, tooltipHwnd),
NativeMethods.HWND_TOPMOST,
0, 0, 0, 0,
NativeMethods.SWP_NOMOVE | NativeMethods.SWP_NOSIZE |
NativeMethods.SWP_NOACTIVATE);
}
}
// Add the pages
//
foreach(TabPage page in TabPages) {
AddNativeTabPage(page.GetTCITEM());
}
// Resize the pages
//
ResizePages();
if (selectedIndex != -1) {
try {
tabControlState[TABCONTROLSTATE_fromCreateHandles] = true;
SelectedIndex = selectedIndex;
}
finally {
tabControlState[TABCONTROLSTATE_fromCreateHandles] = false;
}
selectedIndex = -1;
}
UpdateTabSelection(false);
}
/// <include file='doc\TabControl.uex' path='docs/doc[@for="TabControl.OnHandleDestroyed"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
protected override void OnHandleDestroyed(EventArgs e) {
if (!Disposing) {
selectedIndex = SelectedIndex;
}
//Remove the Handle from NativewIndow....
// VSWhidbey 539185, Don't try to remove the Handle if we've already done so
if (handleInTable)
{
handleInTable = false;
NativeWindow.RemoveWindowFromIDTable(this.Handle);
}
base.OnHandleDestroyed(e);
}
/// <include file='doc\TabControl.uex' path='docs/doc[@for="TabControl.OnDrawItem"]/*' />
/// <devdoc>
/// Actually goes and fires the OnDrawItem event. Inheriting controls
/// should use this to know when the event is fired [this is preferable to
/// adding an event handler on yourself for this event]. They should,
/// however, remember to call base.onDrawItem(e); to ensure the event is
/// still fired to external listeners
/// </devdoc>
protected virtual void OnDrawItem(DrawItemEventArgs e) {
if (onDrawItem != null) onDrawItem(this, e);
}
/// <include file='doc\TabControl.uex' path='docs/doc[@for="TabControl.OnEnter"]/*' />
/// <devdoc>
/// Actually goes and fires the OnLeave event. Inheriting controls
/// should use this to know when the event is fired [this is preferable to
/// adding an event handler on yourself for this event]. They should,
/// however, remember to call base.OnLeave(e); to ensure the event is
/// still fired to external listeners
/// This listener is overidden so that we can fire SAME ENTER and LEAVE
/// events on the TabPage.
/// TabPage should fire enter when the focus is on the TABPAGE and not when the control
/// within the TabPage gets Focused.
/// Similary the Leave event should fire when the TabControl (and hence the TabPage) looses
/// Focus. To be Backward compatible we have added new bool which can be set to true
/// to the get the NEW SANE ENTER-LEAVE EVENTS ON THE TABPAGE.
/// </devdoc>
protected override void OnEnter(EventArgs e) {
base.OnEnter (e);
if (SelectedTab != null) {
SelectedTab.FireEnter(e);
}
}
/// <include file='doc\TabControl.uex' path='docs/doc[@for="TabControl.OnLeave"]/*' />
/// <devdoc>
/// Actually goes and fires the OnLeave event. Inheriting controls
/// should use this to know when the event is fired [this is preferable to
/// adding an event handler on yourself for this event]. They should,
/// however, remember to call base.OnLeave(e); to ensure the event is
/// still fired to external listeners
/// This listener is overidden so that we can fire SAME ENTER and LEAVE
/// events on the TabPage.
/// TabPage should fire enter when the focus is on the TABPAGE and not when the control
/// within the TabPage gets Focused.
/// Similary the Leave event should fire when the TabControl (and hence the TabPage) looses
/// Focus. To be Backward compatible we have added new bool which can be set to true
/// to the get the NEW SANE ENTER-LEAVE EVENTS ON THE TABPAGE.
/// </devdoc>
protected override void OnLeave(EventArgs e) {
if (SelectedTab != null) {
SelectedTab.FireLeave(e);
}
base.OnLeave(e);
}
/// <include file='doc\TabControl.uex' path='docs/doc[@for="TabControl.OnKeyDown"]/*' />
/// <devdoc>
/// We override this to get tabbing functionality.
/// If overriding this, remember to call base.onKeyDown.
/// </devdoc>
/// <internalonly/>
protected override void OnKeyDown(KeyEventArgs ke) {
if (ke.KeyCode == Keys.Tab && (ke.KeyData & Keys.Control) !=0) {
bool forward = (ke.KeyData & Keys.Shift) == 0;
SelectNextTab(ke, forward);
}
if (ke.KeyCode == Keys.PageDown && (ke.KeyData & Keys.Control) !=0) {
SelectNextTab(ke, true);
}
if (ke.KeyCode == Keys.PageUp && (ke.KeyData & Keys.Control) !=0) {
SelectNextTab(ke, false);
}
base.OnKeyDown(ke);
}
internal override void OnParentHandleRecreated()
{
// Avoid temporarily resizing the TabControl while the parent
// recreates its handle to avoid bug VSWhidbey 543390.
this.skipUpdateSize = true;
try {
base.OnParentHandleRecreated();
}
finally {
this.skipUpdateSize = false;
}
}
/// <include file='doc\TabControl.uex' path='docs/doc[@for="TabControl.OnResize"]/*' />
/// <devdoc>
/// </devdoc>
/// <internalonly/>
protected override void OnResize(EventArgs e) {
base.OnResize(e);
cachedDisplayRect = Rectangle.Empty;
UpdateTabSelection(false);
}
/// <include file='doc\Form.uex' path='docs/doc[@for="Form.OnRightToLeftLayoutChanged"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
[EditorBrowsable(EditorBrowsableState.Advanced)]
protected virtual void OnRightToLeftLayoutChanged(EventArgs e) {
if (GetAnyDisposingInHierarchy()) {
return;
}
if (RightToLeft == RightToLeft.Yes) {
RecreateHandle();
}
EventHandler eh = Events[EVENT_RIGHTTOLEFTLAYOUTCHANGED] as EventHandler;
if (eh != null) {
eh(this, e);
}
}
/// <include file='doc\TabControl.uex' path='docs/doc[@for="TabControl.OnSelectedIndexChanged"]/*' />
/// <devdoc>
/// Actually goes and fires the onSelectedIndexChanged event. Inheriting controls
/// should use this to know when the event is fired [this is preferable to
/// adding an event handler on yourself for this event]. They should,
/// however, remember to call base.onSelectedIndexChanged(e); to ensure the event is
/// still fired to external listeners
/// </devdoc>
protected virtual void OnSelectedIndexChanged(EventArgs e) {
int index = SelectedIndex;
cachedDisplayRect = Rectangle.Empty;
UpdateTabSelection(tabControlState[TABCONTROLSTATE_UISelection]);
tabControlState[TABCONTROLSTATE_UISelection] = false;
if (onSelectedIndexChanged != null) onSelectedIndexChanged(this, e);
}
/// <include file='doc\TabControl.uex' path='docs/doc[@for="TabControl.OnSelecting"]/*' />
/// <devdoc>
/// <para>
/// Raises the <see cref='System.Windows.Forms.TabControl.OnSelecting'/> event.
/// </para>
/// </devdoc>
protected virtual void OnSelecting(TabControlCancelEventArgs e) {
TabControlCancelEventHandler handler = (TabControlCancelEventHandler)Events[EVENT_SELECTING];
if (handler != null) handler(this, e);
}
/// <include file='doc\TabControl.uex' path='docs/doc[@for="TabControl.OnSelected"]/*' />
/// <devdoc>
/// <para>
/// Raises the <see cref='System.Windows.Forms.TabControl.OnSelected'/> event.
/// </para>
/// </devdoc>
protected virtual void OnSelected(TabControlEventArgs e) {
TabControlEventHandler handler = (TabControlEventHandler)Events[EVENT_SELECTED];
if (handler != null) handler(this, e);
// Raise the enter event for this tab.
if (SelectedTab != null) {
SelectedTab.FireEnter(EventArgs.Empty);
}
}
/// <include file='doc\TabControl.uex' path='docs/doc[@for="TabControl.OnDeselecting"]/*' />
/// <devdoc>
/// <para>
/// Raises the <see cref='System.Windows.Forms.TabControl.OnDeselecting'/> event.
/// </para>
/// </devdoc>
protected virtual void OnDeselecting(TabControlCancelEventArgs e) {
TabControlCancelEventHandler handler = (TabControlCancelEventHandler)Events[EVENT_DESELECTING];
if (handler != null) handler(this, e);
}
/// <include file='doc\TabControl.uex' path='docs/doc[@for="TabControl.OnDeselected"]/*' />
/// <devdoc>
/// <para>
/// Raises the <see cref='System.Windows.Forms.TabControl.OnDeselected'/> event.
/// </para>
/// </devdoc>
protected virtual void OnDeselected(TabControlEventArgs e) {
TabControlEventHandler handler = (TabControlEventHandler)Events[EVENT_DESELECTED];
if (handler != null) handler(this, e);
// Raise the Leave event for this tab.
if (SelectedTab != null) {
SelectedTab.FireLeave(EventArgs.Empty);
}
}
/// <include file='doc\TabControl.uex' path='docs/doc[@for="TabControl.ProcessKeyPreview"]/*' />
/// <devdoc>
/// We override this to get the Ctrl and Ctrl-Shift Tab functionality.
/// </devdoc>
/// <internalonly/>
[
SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)
]
protected override bool ProcessKeyPreview(ref Message m) {
if (ProcessKeyEventArgs(ref m)) return true;
return base.ProcessKeyPreview(ref m);
}
/// <include file='doc\TabControl.uex' path='docs/doc[@for="TabControl.UpdateSize"]/*' />
/// <devdoc>
/// </devdoc>
/// <internalonly/>
internal void UpdateSize() {
if (this.skipUpdateSize) {
return;
}
// the spin control (left right arrows) won't update without resizing.
// the most correct thing would be to recreate the handle, but this works
// and is cheaper.
//
BeginUpdate();
Size size = Size;
Size = new Size(size.Width + 1, size.Height);
Size = size;
EndUpdate();
}
/// <include file='doc\TabControl.uex' path='docs/doc[@for="TabControl.OnFontChanged"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
protected override void OnFontChanged(EventArgs e) {
base.OnFontChanged(e);
cachedDisplayRect = Rectangle.Empty;
UpdateSize();
}
internal override void RecreateHandleCore() {
//
TabPage[] tabPages = GetTabPages();
int index = ((tabPages.Length > 0) && (SelectedIndex == -1)) ? 0: SelectedIndex;
// We don't actually want to remove the windows forms Tabpages - we only
// want to remove the corresponding TCITEM structs.
// So, no RemoveAll()
if (IsHandleCreated) {
SendMessage(NativeMethods.TCM_DELETEALLITEMS, 0, 0);
}
this.tabPages = null;
tabPageCount = 0;
base.RecreateHandleCore();
for (int i = 0; i < tabPages.Length; i++) {
TabPages.Add(tabPages[i]);
}
try {
tabControlState[TABCONTROLSTATE_fromCreateHandles] = true;
SelectedIndex = index;
}
finally {
tabControlState[TABCONTROLSTATE_fromCreateHandles] = false;
}
// The comctl32 TabControl seems to have some painting glitches. Briefly
// resizing the control seems to fix these.
//
UpdateSize();
}
/// <include file='doc\TabControl.uex' path='docs/doc[@for="TabControl.RemoveAll"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
protected void RemoveAll() {
this.Controls.Clear();
SendMessage(NativeMethods.TCM_DELETEALLITEMS, 0, 0);
tabPages = null;
tabPageCount = 0;
}
/// <include file='doc\TabControl.uex' path='docs/doc[@for="TabControl.RemoveItem"]/*' />
/// <devdoc>
/// </devdoc>
/// <internalonly/>
internal void RemoveTabPage(int index) {
if (index < 0 || index >= tabPageCount)
throw new ArgumentOutOfRangeException("index", SR.GetString(SR.InvalidArgument, "index", index.ToString(CultureInfo.CurrentCulture)));
tabPageCount--;
if (index < tabPageCount) {
Array.Copy(tabPages, index + 1, tabPages, index, tabPageCount - index);
}
tabPages[tabPageCount] = null;
if (IsHandleCreated) {
SendMessage(NativeMethods.TCM_DELETEITEM, index, 0);
}
cachedDisplayRect = Rectangle.Empty;
}
private void ResetItemSize() {
ItemSize = DEFAULT_ITEMSIZE;
}
private void ResetPadding() {
Padding = DEFAULT_PADDING;
}
private void ResizePages() {
Rectangle rect = DisplayRectangle;
TabPage[] pages = GetTabPages();
for (int i = 0; i < pages.Length; i++) {
pages[i].Bounds = rect;
}
}
/// <include file='doc\TabControl.uex' path='docs/doc[@for="TabControl.SetToolTip"]/*' />
/// <devdoc>
/// Called by ToolTip to poke in that Tooltip into this ComCtl so that the Native ChildToolTip is not exposed.
/// </devdoc>
/// <internalonly/>
internal void SetToolTip(ToolTip toolTip, string controlToolTipText) {
UnsafeNativeMethods.SendMessage(new HandleRef(this, this.Handle), NativeMethods.TCM_SETTOOLTIPS, new HandleRef(toolTip, toolTip.Handle), 0);
controlTipText = controlToolTipText;
}
/// <include file='doc\TabControl.uex' path='docs/doc[@for="TabControl.SetItem"]/*' />
/// <devdoc>
/// </devdoc>
/// <internalonly/>
internal void SetTabPage(int index, TabPage tabPage, NativeMethods.TCITEM_T tcitem) {
if (index < 0 || index >= tabPageCount)
throw new ArgumentOutOfRangeException("index", SR.GetString(SR.InvalidArgument, "index", index.ToString(CultureInfo.CurrentCulture)));
if (IsHandleCreated)
UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.TCM_SETITEM, index, tcitem);
// Make the Updated tab page the currently selected tab page
if (DesignMode && IsHandleCreated) {
UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.TCM_SETCURSEL, (IntPtr)index, IntPtr.Zero);
}
tabPages[index] = tabPage;
}
/// <include file='doc\TabControl.uex' path='docs/doc[@for="TabControl.SelectTab"]/*' />
/// <devdoc>
/// Allows the user to specify the index in Tabcontrol.TabPageCollection of the tabpage to be shown.
/// </devdoc>
public void SelectTab(int index) {
TabPage t = GetTabPage(index);
if(t != null)
{
SelectedTab = t;
}
}
/// <include file='doc\TabControl.uex' path='docs/doc[@for="TabControl.SelectTab1"]/*' />
/// <devdoc>
/// Allows the user to specify the tabpage in Tabcontrol.TabPageCollection to be shown.
/// </devdoc>
public void SelectTab(TabPage tabPage) {
if (tabPage == null) {
throw new ArgumentNullException("tabPage");
}
int index = FindTabPage(tabPage);
SelectTab(index);
}
/// <include file='doc\TabControl.uex' path='docs/doc[@for="TabControl.SelectTab2"]/*' />
/// <devdoc>
/// Allows the user to specify the name of the tabpage in Tabcontrol.TabPageCollection to be shown.
/// </devdoc>
public void SelectTab(string tabPageName) {
if (tabPageName == null) {
throw new ArgumentNullException("tabPageName");
}
TabPage tabPage = TabPages[tabPageName];
SelectTab(tabPage);
}
/// <include file='doc\TabControl.uex' path='docs/doc[@for="TabControl.SelectNextTab"]/*' />
/// <devdoc>
/// This is called by TabControl in response to the KeyDown event to override the selection of tabpages
/// for different key combinations.
/// Control + Tab selects the next tabpage.
/// Control + Shift + Tab selects the previous tabpage.
/// Control + PageDown selects the next tabpage.
/// Control + PageUp selects the previous tabpage.
/// </devdoc>
/// <internalonly/>
private void SelectNextTab(KeyEventArgs ke, bool forward) {
// WmSelChanging actually changes focus to cause validations.
// So cache in the Focused value so that we can reuse it later
bool focused = Focused;
// Fire Deselecting .. Deselected on currently selected TabPage...
if (WmSelChanging()) {
tabControlState[TABCONTROLSTATE_UISelection] = false;
return;
}
if(ValidationCancelled) {
tabControlState[TABCONTROLSTATE_UISelection] = false;
return;
}
else {
int sel = SelectedIndex;
if (sel != -1) {
int count = TabCount;
if (forward)
sel = (sel + 1) % count;
else
sel = (sel + count - 1) % count;
// this is special casing..
// this function is called from OnKeyDown( ) which selects the NEXT TABPAGE
// But now we call the WmSelChanging( ) to Focus the tab page
// This breaks the logic in UpdateTabSelection (which is called
// thru SET of SelectedIndex) to Select the First control
// So adding this new Flag to select the first control.
try
{
tabControlState[TABCONTROLSTATE_UISelection] = true;
tabControlState[TABCONTROLSTATE_selectFirstControl] = true;
SelectedIndex = sel;
// This is required so that we select the first control if the TabControl is not current focused.
tabControlState[TABCONTROLSTATE_selectFirstControl] = !focused;
// Fire Selecting .. Selected on newly selected TabPage...
WmSelChange();
}
finally
{
// tabControlState[TABCONTROLSTATE_selectFirstControl] can be true if the TabControl is not focussed
// But at the end of this function reset the state !!
tabControlState[TABCONTROLSTATE_selectFirstControl] = false;
ke.Handled = true;
}
}
}
}
// Refer vsW: 543074: TabControl overrides this method to return true.
internal override bool ShouldPerformContainerValidation() {
return true;
}
private bool ShouldSerializeItemSize() {
return !itemSize.Equals(DEFAULT_ITEMSIZE);
}
private new bool ShouldSerializePadding() {
return !padding.Equals(DEFAULT_PADDING);
}
/// <include file='doc\TabControl.uex' path='docs/doc[@for="TabControl.ToString"]/*' />
/// <devdoc>
/// Returns a string representation for this control.
/// </devdoc>
/// <internalonly/>
public override string ToString() {
string s = base.ToString();
if (TabPages != null) {
s += ", TabPages.Count: " + TabPages.Count.ToString(CultureInfo.CurrentCulture);
if (TabPages.Count > 0)
s += ", TabPages[0]: " + TabPages[0].ToString();
}
return s;
}
[EditorBrowsable(EditorBrowsableState.Never)]
protected override void ScaleCore(float dx, float dy) {
currentlyScaling = true;
base.ScaleCore(dx, dy);
currentlyScaling = false;
}
/// <include file='doc\TabControl.uex' path='docs/doc[@for="TabControl.UpdateTabSelection"]/*' />
/// <devdoc>
/// </devdoc>
/// <internalonly/>
/// <devdoc>
/// Set the panel selections appropriately
/// </devdoc>
/// <internalonly/>
protected void UpdateTabSelection(bool updateFocus) {
if (IsHandleCreated) {
int index = SelectedIndex;
// make current panel invisible
TabPage[] tabPages = GetTabPages();
if (index != -1) {
// VSWhidbey #163724
// Changing the bounds of the tabPage during scaling
// will force a layout to occur. After this layout
// the tabpage will then be scaled again resulting
// in incorrect sizes. Suspend Layout in this case.
if (currentlyScaling) {
tabPages[index].SuspendLayout();
}
tabPages[index].Bounds = DisplayRectangle;
// After changing the Bounds of TabPages, we need to
// make TabPages Redraw.
// Use Invalidate directly here has no performance
// issue, since ReSize is calling low frequence.
tabPages[index].Invalidate();
if (currentlyScaling) {
tabPages[index].ResumeLayout(false);
}
tabPages[index].Visible = true;
if (updateFocus) {
if (!Focused || tabControlState[TABCONTROLSTATE_selectFirstControl]) {
tabControlState[TABCONTROLSTATE_UISelection] = false;
bool selectNext = false;
// SECREVIEW : Taking focus and activating a control in response
// : to a user gesture (WM_SETFOCUS) is OK.
//
IntSecurity.ModifyFocus.Assert();
try {
selectNext = tabPages[index].SelectNextControl(null, true, true, false, false);
}
finally {
CodeAccessPermission.RevertAssert();
}
if (selectNext) {
if (!ContainsFocus) {
IContainerControl c = GetContainerControlInternal();
if (c != null) {
while (c.ActiveControl is ContainerControl) {
c = (IContainerControl) c.ActiveControl;
}
if (c.ActiveControl != null)
{
c.ActiveControl.FocusInternal();
}
}
}
}
else {
IContainerControl c = GetContainerControlInternal();
if (c != null && !DesignMode) {
if (c is ContainerControl) {
((ContainerControl)c).SetActiveControlInternal(this);
}
else {
// SECREVIEW : Taking focus and activating a control in response
// : to a user gesture (WM_SETFOCUS) is OK.
//
IntSecurity.ModifyFocus.Assert();
try {
c.ActiveControl = this;
}
finally {
CodeAccessPermission.RevertAssert();
}
}
}
}
}
}
}
for (int i = 0; i < tabPages.Length; i++) {
if (i != SelectedIndex) {
tabPages[i].Visible = false;
}
}
}
}
/// <include file='doc\TabControl.uex' path='docs/doc[@for="TabControl.OnStyleChanged"]/*' />
/// <devdoc>
/// </devdoc>
/// <internalonly/>
protected override void OnStyleChanged(EventArgs e) {
base.OnStyleChanged(e);
cachedDisplayRect = Rectangle.Empty;
UpdateTabSelection(false);
}
/// <include file='doc\TabControl.uex' path='docs/doc[@for="TabControl.UpdateTab"]/*' />
/// <devdoc>
/// </devdoc>
/// <internalonly/>
internal void UpdateTab(TabPage tabPage) {
int index = FindTabPage(tabPage);
SetTabPage(index, tabPage, tabPage.GetTCITEM());
// It's possible that changes to this TabPage will change the DisplayRectangle of the
// TabControl (e.g. ASURT 99087), so invalidate and resize the size of this page.
//
cachedDisplayRect = Rectangle.Empty;
UpdateTabSelection(false);
}
/// <include file='doc\TabControl.uex' path='docs/doc[@for="TabControl.WmNeedText"]/*' />
/// <devdoc>
/// </devdoc>
/// <internalonly/>
private void WmNeedText(ref Message m) {
NativeMethods.TOOLTIPTEXT ttt = (NativeMethods.TOOLTIPTEXT) m.GetLParam(typeof(NativeMethods.TOOLTIPTEXT));
int commandID = (int)ttt.hdr.idFrom;
string tipText = GetToolTipText(GetTabPage(commandID));
if (!string.IsNullOrEmpty(tipText))
ttt.lpszText = tipText;
else
ttt.lpszText = controlTipText;
ttt.hinst = IntPtr.Zero;
// RightToLeft reading order
//
if (RightToLeft == RightToLeft.Yes) {
ttt.uFlags |= NativeMethods.TTF_RTLREADING;
}
Marshal.StructureToPtr(ttt, m.LParam, false);
}
/// <include file='doc\TabControl.uex' path='docs/doc[@for="TabControl.WmReflectDrawItem"]/*' />
/// <devdoc>
/// </devdoc>
/// <internalonly/>
private void WmReflectDrawItem(ref Message m) {
NativeMethods.DRAWITEMSTRUCT dis = (NativeMethods.DRAWITEMSTRUCT)m.GetLParam(typeof(NativeMethods.DRAWITEMSTRUCT));
IntPtr oldPal = SetUpPalette(dis.hDC, false /*force*/, false /*realize*/);
using (Graphics g = Graphics.FromHdcInternal(dis.hDC)) {
OnDrawItem(new DrawItemEventArgs(g, Font, Rectangle.FromLTRB(dis.rcItem.left, dis.rcItem.top, dis.rcItem.right, dis.rcItem.bottom), dis.itemID, (DrawItemState)dis.itemState));
}
if (oldPal != IntPtr.Zero) {
SafeNativeMethods.SelectPalette(new HandleRef(null, dis.hDC), new HandleRef(null, oldPal), 0);
}
m.Result = (IntPtr)1;
}
/// <include file='doc\TabControl.uex' path='docs/doc[@for="TabControl.WmSelChange"]/*' />
/// <devdoc>
/// </devdoc>
/// <internalonly/>
private bool WmSelChange() {
TabControlCancelEventArgs tcc = new TabControlCancelEventArgs(this.SelectedTab, this.SelectedIndex, false, TabControlAction.Selecting);
OnSelecting(tcc);
if (!tcc.Cancel) {
OnSelected (new TabControlEventArgs (this.SelectedTab, this.SelectedIndex, TabControlAction.Selected));
OnSelectedIndexChanged(EventArgs.Empty);
}
else {
// user Cancelled the Selection of the new Tab.
SendMessage(NativeMethods.TCM_SETCURSEL, lastSelection, 0);
UpdateTabSelection(true);
}
return tcc.Cancel;
}
/// <include file='doc\TabControl.uex' path='docs/doc[@for="TabControl.WmSelChanging"]/*' />
/// <devdoc>
/// </devdoc>
/// <internalonly/>
private bool WmSelChanging() {
IContainerControl c = GetContainerControlInternal();
if (c != null && !DesignMode) {
if (c is ContainerControl) {
((ContainerControl)c).SetActiveControlInternal(this);
}
else {
// SECREVIEW : Taking focus and activating a control in response
// : to a user gesture (WM_SETFOCUS) is OK.
//
IntSecurity.ModifyFocus.Assert();
try {
c.ActiveControl = this;
}
finally {
CodeAccessPermission.RevertAssert();
}
}
}
// Fire DeSelecting .... on the current Selected Index...
// Set the return value to a global
// if 'cancelled' return from here else..
// fire Deselected.
lastSelection = SelectedIndex;
TabControlCancelEventArgs tcc = new TabControlCancelEventArgs(this.SelectedTab, this.SelectedIndex, false, TabControlAction.Deselecting);
OnDeselecting(tcc);
if (!tcc.Cancel) {
OnDeselected(new TabControlEventArgs(this.SelectedTab, this.SelectedIndex, TabControlAction.Deselected));
}
return tcc.Cancel;
}
/// <include file='doc\TabControl.uex' path='docs/doc[@for="TabControl.WmTabBaseReLayout"]/*' />
/// <devdoc>
/// </devdoc>
/// <internalonly/>
private void WmTabBaseReLayout(ref Message m) {
BeginUpdate();
cachedDisplayRect = Rectangle.Empty;
UpdateTabSelection(false);
EndUpdate();
Invalidate(true);
// Remove other TabBaseReLayout messages from the message queue
NativeMethods.MSG msg = new NativeMethods.MSG();
IntPtr hwnd = Handle;
while (UnsafeNativeMethods.PeekMessage(ref msg, new HandleRef(this, hwnd),
tabBaseReLayoutMessage,
tabBaseReLayoutMessage,
NativeMethods.PM_REMOVE)) {
; // NULL loop
}
}
/// <include file='doc\TabControl.uex' path='docs/doc[@for="TabControl.WndProc"]/*' />
/// <devdoc>
/// The tab's window procedure. Inheritng classes can override this
/// to add extra functionality, but should not forget to call
/// base.wndProc(m); to ensure the tab continues to function properly.
/// </devdoc>
/// <internalonly/>
[SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)]
protected override void WndProc(ref Message m) {
switch (m.Msg) {
case NativeMethods.WM_REFLECT + NativeMethods.WM_DRAWITEM:
WmReflectDrawItem(ref m);
break;
case NativeMethods.WM_REFLECT + NativeMethods.WM_MEASUREITEM:
// We use TCM_SETITEMSIZE instead
break;
case NativeMethods.WM_NOTIFY:
case NativeMethods.WM_REFLECT + NativeMethods.WM_NOTIFY:
NativeMethods.NMHDR nmhdr = (NativeMethods.NMHDR) m.GetLParam(typeof(NativeMethods.NMHDR));
switch (nmhdr.code) {
// new switch added to prevent the TabControl from changing to next TabPage ...
//in case of validation cancelled...
//Turn tabControlState[TABCONTROLSTATE_UISelection] = false and Return So that no WmSelChange() gets fired.
//If validation not cancelled then tabControlState[TABCONTROLSTATE_UISelection] is turned ON to set the focus on to the ...
//next TabPage..
case NativeMethods.TCN_SELCHANGING:
if (WmSelChanging()) {
m.Result = (IntPtr)1;
tabControlState[TABCONTROLSTATE_UISelection] = false;
return;
}
if(ValidationCancelled) {
m.Result = (IntPtr)1;
tabControlState[TABCONTROLSTATE_UISelection] = false;
return;
}
else {
tabControlState[TABCONTROLSTATE_UISelection] = true;
}
break;
case NativeMethods.TCN_SELCHANGE:
if (WmSelChange ()) {
m.Result = (IntPtr)1;
tabControlState[TABCONTROLSTATE_UISelection] = false;
return;
}
else {
tabControlState[TABCONTROLSTATE_UISelection] = true;
}
break;
case NativeMethods.TTN_GETDISPINFOA:
case NativeMethods.TTN_GETDISPINFOW:
// MSDN:
// Setting the max width has the added benefit of enabling Multiline
// tool tips!
//
UnsafeNativeMethods.SendMessage(new HandleRef(nmhdr, nmhdr.hwndFrom), NativeMethods.TTM_SETMAXTIPWIDTH, 0, SystemInformation.MaxWindowTrackSize.Width);
WmNeedText(ref m);
m.Result = (IntPtr)1;
return;
}
break;
}
if (m.Msg == tabBaseReLayoutMessage) {
WmTabBaseReLayout(ref m);
return;
}
base.WndProc(ref m);
}
/// <include file='doc\TabControl.uex' path='docs/doc[@for="TabControl.TabPageCollection"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
public class TabPageCollection : IList {
private TabControl owner;
/// A caching mechanism for key accessor
/// We use an index here rather than control so that we don't have lifetime
/// issues by holding on to extra references.
private int lastAccessedIndex = -1;
/// <include file='doc\TabControl.uex' path='docs/doc[@for="TabControl.TabPageCollection.TabPageCollection"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
[SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly")]
public TabPageCollection( TabControl owner ) {
if (owner == null) {
throw new ArgumentNullException("owner");
}
this.owner = owner;
}
/// <include file='doc\TabControl.uex' path='docs/doc[@for="TabControl.TabPageCollection.this"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
public virtual TabPage this[int index] {
get {
return owner.GetTabPage(index);
}
set {
owner.SetTabPage(index, value, value.GetTCITEM());
}
}
/// <include file='doc\TabControl.uex' path='docs/doc[@for="TabPageCollection.IList.this"]/*' />
/// <internalonly/>
object IList.this[int index] {
get {
return this[index];
}
[SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly")]
[
SuppressMessage("Microsoft.Globalization", "CA1303:DoNotPassLiteralsAsLocalizedParameters") // value is the name of the param passed in.
// So we don't have to localize it.
]
set {
if (value is TabPage) {
this[index] = (TabPage)value;
}
else {
throw new ArgumentException("value");
}
}
}
/// <include file='doc\TabControl.uex' path='docs/doc[@for="TabControl.TabPageCollection.this"]/*' />
/// <devdoc>
/// <para>Retrieves the child control with the specified key.</para>
/// </devdoc>
public virtual TabPage this[string key] {
get {
// We do not support null and empty string as valid keys.
if (string.IsNullOrEmpty(key)){
return null;
}
// Search for the key in our collection
int index = IndexOfKey(key);
if (IsValidIndex(index)) {
return this[index];
}
else {
return null;
}
}
}
/// <include file='doc\TabControl.uex' path='docs/doc[@for="TabControl.TabPageCollection.Count"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
[Browsable(false)]
public int Count {
get {
return owner.tabPageCount;
}
}
/// <include file='doc\TabControl.uex' path='docs/doc[@for="TabPageCollection.ICollection.SyncRoot"]/*' />
/// <internalonly/>
object ICollection.SyncRoot {
get {
return this;
}
}
/// <include file='doc\TabControl.uex' path='docs/doc[@for="TabPageCollection.ICollection.IsSynchronized"]/*' />
/// <internalonly/>
bool ICollection.IsSynchronized {
get {
return false;
}
}
/// <include file='doc\TabControl.uex' path='docs/doc[@for="TabPageCollection.IList.IsFixedSize"]/*' />
/// <internalonly/>
bool IList.IsFixedSize {
get {
return false;
}
}
/// <include file='doc\TabControl.uex' path='docs/doc[@for="TabControl.TabPageCollection.IsReadOnly"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
public bool IsReadOnly {
get {
return false;
}
}
/// <include file='doc\TabControl.uex' path='docs/doc[@for="TabControl.TabPageCollection.Add"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
[SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly")]
public void Add(TabPage value) {
if (value == null) {
throw new ArgumentNullException("value");
}
owner.Controls.Add(value);
}
/// <include file='doc\TabControl.uex' path='docs/doc[@for="TabPageCollection.IList.Add"]/*' />
/// <internalonly/>
[
SuppressMessage("Microsoft.Globalization", "CA1303:DoNotPassLiteralsAsLocalizedParameters") // value is the name of the param passed in.
// So we don't have to localize it.
]
[SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly")]
int IList.Add(object value) {
if (value is TabPage) {
Add((TabPage)value);
return IndexOf((TabPage)value);
}
else {
throw new ArgumentException("value");
}
}
// <-- NEW ADD OVERLOADS FOR WHIDBEY
/// <include file='doc\TabControl.uex' path='docs/doc[@for="TabControl.TabPageCollection.Add1"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
public void Add(string text) {
TabPage page = new TabPage();
page.Text = text;
Add(page);
}
/// <include file='doc\TabControl.uex' path='docs/doc[@for="TabControl.TabPageCollection.Add2"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
public void Add(string key, string text) {
TabPage page = new TabPage();
page.Name = key;
page.Text = text;
Add(page);
}
/// <include file='doc\TabControl.uex' path='docs/doc[@for="TabControl.TabPageCollection.Add3"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
public void Add(string key, string text, int imageIndex) {
TabPage page = new TabPage();
page.Name = key;
page.Text = text;
page.ImageIndex = imageIndex;
Add(page);
}
/// <include file='doc\TabControl.uex' path='docs/doc[@for="TabControl.TabPageCollection.Add4"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
public void Add(string key, string text, string imageKey) {
TabPage page = new TabPage();
page.Name = key;
page.Text = text;
page.ImageKey = imageKey;
Add(page);
}
// END - NEW ADD OVERLOADS FOR WHIDBEY -->
/// <include file='doc\TabControl.uex' path='docs/doc[@for="TabControl.TabPageCollection.AddRange"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
[SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly")]
public void AddRange(TabPage[] pages) {
if (pages == null) {
throw new ArgumentNullException("pages");
}
foreach(TabPage page in pages) {
Add(page);
}
}
/// <include file='doc\TabControl.uex' path='docs/doc[@for="TabControl.TabPageCollection.Contains"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
[SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly")]
public bool Contains(TabPage page) {
//check for the page not to be null
if (page == null)
throw new ArgumentNullException("value");
//end check
return IndexOf(page) != -1;
}
/// <include file='doc\TabControl.uex' path='docs/doc[@for="TabPageCollection.IList.Contains"]/*' />
/// <internalonly/>
bool IList.Contains(object page) {
if (page is TabPage) {
return Contains((TabPage)page);
}
else {
return false;
}
}
/// <include file='doc\TabControl.uex' path='docs/doc[@for="TabControl.TabPageCollection.ContainsKey"]/*' />
/// <devdoc>
/// <para>Returns true if the collection contains an item with the specified key, false otherwise.</para>
/// </devdoc>
public virtual bool ContainsKey(string key) {
return IsValidIndex(IndexOfKey(key));
}
/// <include file='doc\TabControl.uex' path='docs/doc[@for="TabControl.TabPageCollection.IndexOf"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
[SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly")]
public int IndexOf(TabPage page) {
//check for the page not to be null
if (page == null)
throw new ArgumentNullException("value");
//end check
for(int index=0; index < Count; ++index) {
if (this[index] == page) {
return index;
}
}
return -1;
}
/// <include file='doc\TabControl.uex' path='docs/doc[@for="TabPageCollection.IList.IndexOf"]/*' />
/// <internalonly/>
int IList.IndexOf(object page) {
if (page is TabPage) {
return IndexOf((TabPage)page);
}
else {
return -1;
}
}
/// <include file='doc\TabControl.uex' path='docs/doc[@for="TabControl.TabPageCollection.this"]/*' />
/// <devdoc>
/// <para>The zero-based index of the first occurrence of value within the entire CollectionBase, if found; otherwise, -1.</para>
/// </devdoc>
public virtual int IndexOfKey(String key) {
// Step 0 - Arg validation
if (string.IsNullOrEmpty(key)){
return -1; // we dont support empty or null keys.
}
// step 1 - check the last cached item
if (IsValidIndex(lastAccessedIndex))
{
if (WindowsFormsUtils.SafeCompareStrings(this[lastAccessedIndex].Name, key, /* ignoreCase = */ true)) {
return lastAccessedIndex;
}
}
// step 2 - search for the item
for (int i = 0; i < this.Count; i ++) {
if (WindowsFormsUtils.SafeCompareStrings(this[i].Name, key, /* ignoreCase = */ true)) {
lastAccessedIndex = i;
return i;
}
}
// step 3 - we didn't find it. Invalidate the last accessed index and return -1.
lastAccessedIndex = -1;
return -1;
}
/// <include file='doc\TabControl.uex' path='docs/doc[@for="ToolBar.TabPageCollection.Insert"]/*' />
/// <devdoc>
/// <para>Inserts the supplied Tabpage at the given index.</para>
/// </devdoc>
public void Insert(int index, TabPage tabPage) {
owner.InsertItem(index, tabPage);
try {
// 247078 : See InsertingItem property
owner.InsertingItem = true;
owner.Controls.Add(tabPage);
}
finally {
owner.InsertingItem = false;
}
owner.Controls.SetChildIndex(tabPage, index);
}
/// <include file='doc\TabControl.uex' path='docs/doc[@for="TabPageCollection.IList.Insert"]/*' />
/// <internalonly/>
[SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly")]
[
SuppressMessage("Microsoft.Globalization", "CA1303:DoNotPassLiteralsAsLocalizedParameters") // tabPage is the name of the param passed in.
// So we don't have to localize it.
]
void IList.Insert(int index, object tabPage) {
if (tabPage is TabPage) {
Insert(index, (TabPage)tabPage);
}
else {
throw new ArgumentException("tabPage");
}
}
// <-- NEW INSERT OVERLOADS FOR WHIDBEY
/// <include file='doc\TabControl.uex' path='docs/doc[@for="TabControl.TabPageCollection.Insert1"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
public void Insert(int index, string text) {
TabPage page = new TabPage();
page.Text = text;
Insert(index, page);
}
/// <include file='doc\TabControl.uex' path='docs/doc[@for="TabControl.TabPageCollection.Insert2"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
public void Insert(int index, string key, string text) {
TabPage page = new TabPage();
page.Name = key;
page.Text = text;
Insert(index, page);
}
/// <include file='doc\TabControl.uex' path='docs/doc[@for="TabControl.TabPageCollection.Insert3"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
public void Insert(int index, string key, string text, int imageIndex) {
TabPage page = new TabPage();
page.Name = key;
page.Text = text;
Insert(index,page);
// ImageKey and ImageIndex require parenting...
page.ImageIndex = imageIndex;
}
/// <include file='doc\TabControl.uex' path='docs/doc[@for="TabControl.TabPageCollection.Insert4"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
public void Insert(int index, string key, string text, string imageKey) {
TabPage page = new TabPage();
page.Name = key;
page.Text = text;
Insert(index, page);
// ImageKey and ImageIndex require parenting...
page.ImageKey = imageKey;
}
// END - NEW INSERT OVERLOADS FOR WHIDBEY -->
/// <include file='doc\TabControl.uex' path='docs/doc[@for="TabControl.TabPageCollection.IsValidIndex"]/*' />
/// <devdoc>
/// <para>Determines if the index is valid for the collection.</para>
/// </devdoc>
/// <internalonly/>
private bool IsValidIndex(int index) {
return ((index >= 0) && (index < this.Count));
}
/// <include file='doc\TabControl.uex' path='docs/doc[@for="TabControl.TabPageCollection.Clear"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
public virtual void Clear() {
owner.RemoveAll();
}
/// <include file='doc\TabControl.uex' path='docs/doc[@for="TabPageCollection.ICollection.CopyTo"]/*' />
/// <internalonly/>
void ICollection.CopyTo(Array dest, int index) {
if (Count > 0) {
System.Array.Copy(owner.GetTabPages(), 0, dest, index, Count);
}
}
/// <include file='doc\TabControl.uex' path='docs/doc[@for="TabControl.TabPageCollection.GetEnumerator"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
public IEnumerator GetEnumerator() {
TabPage[] tabPages = owner.GetTabPages();
if (tabPages != null) {
return tabPages.GetEnumerator();
}
else {
return new TabPage[0].GetEnumerator();
}
}
/// <include file='doc\TabControl.uex' path='docs/doc[@for="TabControl.TabPageCollection.Remove"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
[SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly")]
public void Remove(TabPage value) {
//check for the value not to be null
if (value == null)
throw new ArgumentNullException("value");
//end check
owner.Controls.Remove(value);
}
/// <include file='doc\TabControl.uex' path='docs/doc[@for="TabPageCollection.IList.Remove"]/*' />
/// <internalonly/>
void IList.Remove(object value) {
if (value is TabPage) {
Remove((TabPage)value);
}
}
/// <include file='doc\TabControl.uex' path='docs/doc[@for="TabControl.TabPageCollection.RemoveAt"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
public void RemoveAt(int index) {
owner.Controls.RemoveAt(index);
}
/// <include file='doc\TabControl.uex' path='docs/doc[@for="TabControl.TabPageCollection.RemoveByKey"]/*' />
/// <devdoc>
/// <para>Removes the child control with the specified key.</para>
/// </devdoc>
public virtual void RemoveByKey(string key) {
int index = IndexOfKey(key);
if (IsValidIndex(index)) {
RemoveAt(index);
}
}
}
/// <include file='doc\TabControl.uex' path='docs/doc[@for="TabControl.ControlCollection"]/*' />
/// <devdoc>
/// Collection of controls...
/// </devdoc>
[ComVisible(false)]
public new class ControlCollection : Control.ControlCollection {
private TabControl owner;
/*C#r: protected*/
/// <internalonly/>
/// <include file='doc\TabControl.uex' path='docs/doc[@for="TabControl.ControlCollection.ControlCollection"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
public ControlCollection(TabControl owner)
: base(owner) {
this.owner = owner;
}
/// <include file='doc\TabControl.uex' path='docs/doc[@for="TabControl.ControlCollection.Add"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
public override void Add(Control value) {
if (!(value is TabPage)) {
throw new ArgumentException(SR.GetString(SR.TabControlInvalidTabPageType, value.GetType().Name));
}
TabPage tabPage = (TabPage)value;
// 247078 : See InsertingItem property
if (!owner.InsertingItem)
{
if (owner.IsHandleCreated) {
owner.AddTabPage(tabPage, tabPage.GetTCITEM());
}
else {
owner.Insert(owner.TabCount, tabPage);
}
}
base.Add(tabPage);
tabPage.Visible = false;
// Without this check, we force handle creation on the tabcontrol
// which is not good at all of there are any OCXs on it.
//
if (owner.IsHandleCreated) {
tabPage.Bounds = owner.DisplayRectangle;
}
// site the tabPage if necessary.
ISite site = owner.Site;
if (site != null) {
ISite siteTab = tabPage.Site;
if (siteTab == null) {
IContainer container = site.Container;
if (container != null)
{
container.Add(tabPage);
}
}
}
owner.ApplyItemSize();
owner.UpdateTabSelection(false);
}
/// <include file='doc\TabControl.uex' path='docs/doc[@for="TabControl.ControlCollection.Remove"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
public override void Remove(Control value) {
base.Remove(value);
if (!(value is TabPage)) {
return;
}
int index = owner.FindTabPage((TabPage)value);
int curSelectedIndex = owner.SelectedIndex;
if (index != -1) {
owner.RemoveTabPage(index);
if (index == curSelectedIndex)
{
owner.SelectedIndex = 0; //Always select the first tabPage is the Selected TabPage is removed.
}
}
owner.UpdateTabSelection(false);
}
}
}
}
|