|
//------------------------------------------------------------------------------
// <copyright file="ToolBar.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
//------------------------------------------------------------------------------
/*
*/
namespace System.Windows.Forms {
using System.Runtime.Serialization.Formatters;
using System.Runtime.Remoting;
using System.Runtime.InteropServices;
using System.ComponentModel;
using System.Diagnostics;
using System;
using System.Security.Permissions;
using System.Drawing;
using System.Collections;
using System.ComponentModel.Design;
using Microsoft.Win32;
using System.Globalization;
/// <include file='doc\ToolBar.uex' path='docs/doc[@for="ToolBar"]/*' />
/// <devdoc>
/// <para>Represents a Windows toolbar.</para>
/// </devdoc>
[
ComVisible(true),
ClassInterface(ClassInterfaceType.AutoDispatch),
DefaultEvent("ButtonClick"),
Designer("System.Windows.Forms.Design.ToolBarDesigner, " + AssemblyRef.SystemDesign),
DefaultProperty("Buttons")
]
public class ToolBar : Control {
private ToolBarButtonCollection buttonsCollection;
/// <include file='doc\ToolBar.uex' path='docs/doc[@for="ToolBar.buttonSize"]/*' />
/// <devdoc>
/// The size of a button in the ToolBar
/// </devdoc>
internal Size buttonSize = System.Drawing.Size.Empty;
/// <include file='doc\ToolBar.uex' path='docs/doc[@for="ToolBar.requestedSize"]/*' />
/// <devdoc>
/// This is used by our autoSizing support.
/// </devdoc>
private int requestedSize;
/// <include file='doc\ToolBar.uex' path='docs/doc[@for="ToolBar.DDARROW_WIDTH"]/*' />
/// <devdoc>
/// This represents the width of the drop down arrow we have if the
/// DropDownArrows property is true. this value is used by the ToolBarButton
/// objects to compute their size
/// </devdoc>
internal const int DDARROW_WIDTH = 15;
/// <include file='doc\ToolBar.uex' path='docs/doc[@for="ToolBar.appearance"]/*' />
/// <devdoc>
/// Indicates what our appearance will be. This will either be normal
/// or flat.
/// </devdoc>
private ToolBarAppearance appearance = ToolBarAppearance.Normal;
/// <include file='doc\ToolBar.uex' path='docs/doc[@for="ToolBar.borderStyle"]/*' />
/// <devdoc>
/// Indicates whether or not we have a border
/// </devdoc>
private BorderStyle borderStyle = System.Windows.Forms.BorderStyle.None;
/// <include file='doc\ToolBar.uex' path='docs/doc[@for="ToolBar.buttons"]/*' />
/// <devdoc>
/// The array of buttons we're working with.
/// </devdoc>
private ToolBarButton [] buttons;
/// <include file='doc\ToolBar.uex' path='docs/doc[@for="ToolBar.buttonCount"]/*' />
/// <devdoc>
/// The number of buttons we're working with
/// </devdoc>
private int buttonCount = 0;
/// <include file='doc\ToolBar.uex' path='docs/doc[@for="ToolBar.textAlign"]/*' />
/// <devdoc>
/// Indicates if text captions should go underneath images in buttons or
/// to the right of them
/// </devdoc>
private ToolBarTextAlign textAlign = ToolBarTextAlign.Underneath;
/// <include file='doc\ToolBar.uex' path='docs/doc[@for="ToolBar.imageList"]/*' />
/// <devdoc>
/// The ImageList object that contains the main images for our control.
/// </devdoc>
private ImageList imageList = null;
/// <include file='doc\ToolBar.uex' path='docs/doc[@for="ToolBar.maxWidth"]/*' />
/// <devdoc>
/// The maximum width of buttons currently being displayed. This is needed
/// by our autoSizing code. If this value is -1, it needs to be recomputed.
/// </devdoc>
private int maxWidth = -1;
/// <include file='doc\ToolBar.uex' path='docs/doc[@for="ToolBar.hotItem"]/*' />
/// <devdoc>
/// To be supplied.
/// </devdoc>
private int hotItem = -1;
// VSWhidbey 219660: Track the current scale factor so we can scale our buttons
private float currentScaleDX = 1.0F;
private float currentScaleDY = 1.0F;
private const int TOOLBARSTATE_wrappable = 0x00000001;
private const int TOOLBARSTATE_dropDownArrows = 0x00000002;
private const int TOOLBARSTATE_divider = 0x00000004;
private const int TOOLBARSTATE_showToolTips = 0x00000008;
private const int TOOLBARSTATE_autoSize = 0x00000010;
// PERF: take all the bools and put them into a state variable
private System.Collections.Specialized.BitVector32 toolBarState; // see TOOLBARSTATE_ consts above
// event handlers
//
private ToolBarButtonClickEventHandler onButtonClick = null;
private ToolBarButtonClickEventHandler onButtonDropDown = null;
/// <include file='doc\ToolBar.uex' path='docs/doc[@for="ToolBar.ToolBar"]/*' />
/// <devdoc>
/// <para>Initializes a new instance of the <see cref='System.Windows.Forms.ToolBar'/> class.</para>
/// </devdoc>
public ToolBar()
: base() {
// Set this BEFORE calling any other methods so that these defaults will be propagated
toolBarState = new System.Collections.Specialized.BitVector32(TOOLBARSTATE_autoSize |
TOOLBARSTATE_showToolTips |
TOOLBARSTATE_divider |
TOOLBARSTATE_dropDownArrows |
TOOLBARSTATE_wrappable);
SetStyle(ControlStyles.UserPaint, false);
SetStyle(ControlStyles.FixedHeight, AutoSize);
SetStyle(ControlStyles.FixedWidth, false);
TabStop = false;
Dock = DockStyle.Top;
buttonsCollection = new ToolBarButtonCollection(this);
}
/// <include file='doc\ToolBar.uex' path='docs/doc[@for="ToolBar.Appearance"]/*' />
/// <devdoc>
/// <para>Gets or sets the appearance of the toolbar
/// control and its buttons.</para>
/// </devdoc>
[
SRCategory(SR.CatBehavior),
DefaultValue(ToolBarAppearance.Normal),
Localizable(true),
SRDescription(SR.ToolBarAppearanceDescr)
]
public ToolBarAppearance Appearance {
get {
return appearance;
}
set {
//valid values are 0x0 to 0x1
if (!ClientUtils.IsEnumValid(value, (int)value, (int)ToolBarAppearance.Normal, (int)ToolBarAppearance.Flat)){
throw new InvalidEnumArgumentException("value", (int)value, typeof(ToolBarAppearance));
}
if (value != appearance) {
appearance = value;
RecreateHandle();
}
}
}
/// <include file='doc\ToolBar.uex' path='docs/doc[@for="ToolBar.AutoSize"]/*' />
/// <devdoc>
/// <para> Indicates whether the toolbar
/// adjusts its size automatically based on the size of the buttons and the
/// dock style.</para>
/// </devdoc>
[
SRCategory(SR.CatBehavior),
DefaultValue(true),
Localizable(true),
SRDescription(SR.ToolBarAutoSizeDescr),
Browsable(true),
EditorBrowsable(EditorBrowsableState.Always),
DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)
]
public override bool AutoSize {
get {
return toolBarState[TOOLBARSTATE_autoSize];
}
set {
// Note that we intentionally do not call base. Toolbars size themselves by
// overriding SetBoundsCore (old RTM code). We let CommonProperties.GetAutoSize
// continue to return false to keep our LayoutEngines from messing with TextBoxes.
// This is done for backwards compatibility since the new AutoSize behavior differs.
if (AutoSize != value) {
toolBarState[TOOLBARSTATE_autoSize] = value;
if (Dock == DockStyle.Left || Dock == DockStyle.Right) {
SetStyle(ControlStyles.FixedWidth, AutoSize);
SetStyle(ControlStyles.FixedHeight, false);
}
else {
SetStyle(ControlStyles.FixedHeight, AutoSize);
SetStyle(ControlStyles.FixedWidth, false);
}
AdjustSize(Dock);
OnAutoSizeChanged(EventArgs.Empty);
}
}
}
/// <include file='doc\ToolBar.uex' path='docs/doc[@for="ToolBar.AutoSizeChanged"]/*' />
[SRCategory(SR.CatPropertyChanged), SRDescription(SR.ControlOnAutoSizeChangedDescr)]
[Browsable(true), EditorBrowsable(EditorBrowsableState.Always)]
new public event EventHandler AutoSizeChanged
{
add
{
base.AutoSizeChanged += value;
}
remove
{
base.AutoSizeChanged -= value;
}
}
/// <include file='doc\ToolBar.uex' path='docs/doc[@for="ToolBar.BackColor"]/*' />
/// <internalonly/>
/// <devdoc>
/// </devdoc>
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
public override Color BackColor {
get {
return base.BackColor;
}
set {
base.BackColor = value;
}
}
/// <include file='doc\ToolBar.uex' path='docs/doc[@for="ToolBar.BackColorChanged"]/*' />
/// <internalonly/>
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
new public event EventHandler BackColorChanged {
add {
base.BackColorChanged += value;
}
remove {
base.BackColorChanged -= value;
}
}
/// <include file='doc\ToolBar.uex' path='docs/doc[@for="ToolBar.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\ToolBar.uex' path='docs/doc[@for="ToolBar.BackgroundImageChanged"]/*' />
/// <internalonly/>
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
new public event EventHandler BackgroundImageChanged {
add {
base.BackgroundImageChanged += value;
}
remove {
base.BackgroundImageChanged -= value;
}
}
/// <include file='doc\ToolBar.uex' path='docs/doc[@for="ToolBar.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\ToolBar.uex' path='docs/doc[@for="ToolBar.BackgroundImageLayoutChanged"]/*' />
/// <internalonly/>
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
new public event EventHandler BackgroundImageLayoutChanged {
add {
base.BackgroundImageLayoutChanged += value;
}
remove {
base.BackgroundImageLayoutChanged -= value;
}
}
/// <include file='doc\ToolBar.uex' path='docs/doc[@for="ToolBar.BorderStyle"]/*' />
/// <devdoc>
/// <para> Gets or sets
/// the border style of the toolbar control.</para>
/// </devdoc>
[
SRCategory(SR.CatAppearance),
DefaultValue(BorderStyle.None),
DispId(NativeMethods.ActiveX.DISPID_BORDERSTYLE),
SRDescription(SR.ToolBarBorderStyleDescr)
]
public BorderStyle BorderStyle {
get {
return borderStyle;
}
set {
//valid values are 0x0 to 0x2
if (!ClientUtils.IsEnumValid(value, (int)value, (int)BorderStyle.None, (int)BorderStyle.Fixed3D)){
throw new InvalidEnumArgumentException("value", (int)value, typeof(BorderStyle));
}
if (borderStyle != value) {
borderStyle = value;
//UpdateStyles();
RecreateHandle(); // Looks like we need to recreate the handle to avoid painting glitches
}
}
}
/// <include file='doc\ToolBar.uex' path='docs/doc[@for="ToolBar.Buttons"]/*' />
/// <devdoc>
/// <para> A collection of <see cref='System.Windows.Forms.ToolBarButton'/> controls assigned to the
/// toolbar control. The property is read-only.</para>
/// </devdoc>
[
SRCategory(SR.CatBehavior),
DesignerSerializationVisibility(DesignerSerializationVisibility.Content),
Localizable(true),
SRDescription(SR.ToolBarButtonsDescr),
MergableProperty(false)
]
public ToolBarButtonCollection Buttons {
get {
return buttonsCollection;
}
}
/// <include file='doc\ToolBar.uex' path='docs/doc[@for="ToolBar.ButtonSize"]/*' />
/// <devdoc>
/// <para>Gets or sets
/// the size of the buttons on the toolbar control.</para>
/// </devdoc>
[
SRCategory(SR.CatAppearance),
RefreshProperties(RefreshProperties.All),
Localizable(true),
SRDescription(SR.ToolBarButtonSizeDescr)
]
public Size ButtonSize {
get {
if (buttonSize.IsEmpty) {
// Obtain the current buttonsize of the first button from the winctl control
//
if (IsHandleCreated && buttons != null && buttonCount > 0) {
int result = unchecked( (int) (long)SendMessage(NativeMethods.TB_GETBUTTONSIZE, 0, 0));
if (result > 0) {
return new Size(NativeMethods.Util.LOWORD(result), NativeMethods.Util.HIWORD(result));
}
}
if (TextAlign == ToolBarTextAlign.Underneath) {
return new Size(39, 36); // Default button size
}
else {
return new Size(23, 22); // Default button size
}
}
else {
return buttonSize;
}
}
set {
if (value.Width < 0 || value.Height < 0)
throw new ArgumentOutOfRangeException("ButtonSize", SR.GetString(SR.InvalidArgument, "ButtonSize", value.ToString()));
if (buttonSize != value) {
buttonSize = value;
maxWidth = -1; // Force recompute of maxWidth
RecreateHandle();
AdjustSize(Dock);
}
}
}
/// <include file='doc\ToolBar.uex' path='docs/doc[@for="ToolBar.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 get base.CreateParams 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_TOOLBAR;
// windows forms has it's own docking code.
//
cp.Style |= NativeMethods.CCS_NOPARENTALIGN
| NativeMethods.CCS_NORESIZE;
// | NativeMethods.WS_CHILD was commented out since setTopLevel should be able to work.
if (!Divider) cp.Style |= NativeMethods.CCS_NODIVIDER ;
if (Wrappable) cp.Style |= NativeMethods.TBSTYLE_WRAPPABLE;
if (ShowToolTips && !DesignMode) cp.Style |= NativeMethods.TBSTYLE_TOOLTIPS;
cp.ExStyle &= (~NativeMethods.WS_EX_CLIENTEDGE);
cp.Style &= (~NativeMethods.WS_BORDER);
switch (borderStyle) {
case BorderStyle.Fixed3D:
cp.ExStyle |= NativeMethods.WS_EX_CLIENTEDGE;
break;
case BorderStyle.FixedSingle:
cp.Style |= NativeMethods.WS_BORDER;
break;
}
switch (appearance) {
case ToolBarAppearance.Normal:
break;
case ToolBarAppearance.Flat:
cp.Style |= NativeMethods.TBSTYLE_FLAT;
break;
}
switch (textAlign) {
case ToolBarTextAlign.Underneath:
break;
case ToolBarTextAlign.Right:
cp.Style |= NativeMethods.TBSTYLE_LIST;
break;
}
return cp;
}
}
/// <include file='doc\ToolBar.uex' path='docs/doc[@for="ToolBar.DefaultImeMode"]/*' />
protected override ImeMode DefaultImeMode {
get {
return ImeMode.Disable;
}
}
/// <include file='doc\ToolBar.uex' path='docs/doc[@for="ToolBar.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(100, 22);
}
}
/// <include file='doc\ToolBar.uex' path='docs/doc[@for="ToolBar.Divider"]/*' />
/// <devdoc>
/// <para> Gets or sets a value indicating
/// whether the toolbar displays a divider.</para>
/// </devdoc>
[
SRCategory(SR.CatAppearance),
DefaultValue(true),
SRDescription(SR.ToolBarDividerDescr)
]
public bool Divider {
get {
return toolBarState[TOOLBARSTATE_divider];
}
set {
if (Divider != value) {
toolBarState[TOOLBARSTATE_divider] = value;
RecreateHandle();
}
}
}
/// <include file='doc\ToolBar.uex' path='docs/doc[@for="ToolBar.Dock"]/*' />
/// <internalonly/>
/// <devdoc>
/// <para>
/// Sets the way in which this ToolBar is docked to its parent. We need to
/// override this to ensure autoSizing works correctly
/// </para>
/// </devdoc>
[
Localizable(true),
DefaultValue(DockStyle.Top)
]
public override DockStyle Dock {
get { return base.Dock;}
set {
//valid values are 0x0 to 0x5
if (!ClientUtils.IsEnumValid(value, (int)value, (int)DockStyle.None, (int)DockStyle.Fill)){
throw new InvalidEnumArgumentException("value", (int)value, typeof(DockStyle));
}
if (Dock != value) {
if (value == DockStyle.Left || value == DockStyle.Right) {
SetStyle(ControlStyles.FixedWidth, AutoSize);
SetStyle(ControlStyles.FixedHeight, false);
}
else {
SetStyle(ControlStyles.FixedHeight, AutoSize);
SetStyle(ControlStyles.FixedWidth, false);
}
AdjustSize(value);
base.Dock = value;
}
}
}
/// <include file='doc\ToolBar.uex' path='docs/doc[@for="ToolBar.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\ToolBar.uex' path='docs/doc[@for="ToolBar.DropDownArrows"]/*' />
/// <devdoc>
/// <para>Gets or sets a value indicating whether drop-down buttons on a
/// toolbar display down arrows.</para>
/// </devdoc>
[
DefaultValue(false),
SRCategory(SR.CatAppearance),
Localizable(true),
SRDescription(SR.ToolBarDropDownArrowsDescr)
]
public bool DropDownArrows {
get {
return toolBarState[TOOLBARSTATE_dropDownArrows];
}
set {
if (DropDownArrows != value) {
toolBarState[TOOLBARSTATE_dropDownArrows] = value;
RecreateHandle();
}
}
}
/// <include file='doc\ToolBar.uex' path='docs/doc[@for="ToolBar.ForeColor"]/*' />
/// <internalonly/>
/// <devdoc>
/// </devdoc>
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
public override Color ForeColor {
get {
return base.ForeColor;
}
set {
base.ForeColor = value;
}
}
/// <include file='doc\ToolBar.uex' path='docs/doc[@for="ToolBar.ForeColorChanged"]/*' />
/// <internalonly/>
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
new public event EventHandler ForeColorChanged {
add {
base.ForeColorChanged += value;
}
remove {
base.ForeColorChanged -= value;
}
}
/// <include file='doc\ToolBar.uex' path='docs/doc[@for="ToolBar.ImageList"]/*' />
/// <devdoc>
/// <para> Gets or sets the collection of images available to the toolbar button
/// controls.</para>
/// </devdoc>
[
SRCategory(SR.CatBehavior),
DefaultValue(null),
SRDescription(SR.ToolBarImageListDescr)
]
public ImageList ImageList {
get {
return this.imageList;
}
set {
if (value != imageList) {
EventHandler recreateHandler = new EventHandler(ImageListRecreateHandle);
EventHandler disposedHandler = new EventHandler(DetachImageList);
if (imageList != null) {
imageList.Disposed -= disposedHandler;
imageList.RecreateHandle -= recreateHandler;
}
imageList = value;
if (value != null) {
value.Disposed += disposedHandler;
value.RecreateHandle += recreateHandler;
}
if (IsHandleCreated)
RecreateHandle();
}
}
}
/// <include file='doc\ToolBar.uex' path='docs/doc[@for="ToolBar.ImageSize"]/*' />
/// <devdoc>
/// <para>Gets the size of the images in the image list assigned to the
/// toolbar.</para>
/// </devdoc>
[
SRCategory(SR.CatBehavior),
Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced),
DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden),
SRDescription(SR.ToolBarImageSizeDescr)
]
public Size ImageSize {
get {
if (this.imageList != null) {
return this.imageList.ImageSize;
}
else {
return new Size(0, 0);
}
}
}
/// <include file='doc\ToolBar.uex' path='docs/doc[@for="ToolBar.ImeMode"]/*' />
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
new public ImeMode ImeMode {
get {
return base.ImeMode;
}
set {
base.ImeMode = value;
}
}
/// <include file='doc\ToolBar.uex' path='docs/doc[@for="ToolBar.ImeModeChanged"]/*' />
/// <internalonly/>
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
public new event EventHandler ImeModeChanged {
add {
base.ImeModeChanged += value;
}
remove {
base.ImeModeChanged -= value;
}
}
/// <include file='doc\ToolBar.uex' path='docs/doc[@for="ToolBar.PreferredHeight"]/*' />
/// <devdoc>
/// The preferred height for this ToolBar control. This is
/// used by the AutoSizing code.
/// </devdoc>
internal int PreferredHeight {
get {
int height = 0;
if (buttons == null || buttonCount == 0 || !IsHandleCreated) {
height = ButtonSize.Height;
}
else {
// get the first visible button and get it's height
//
NativeMethods.RECT rect = new NativeMethods.RECT();
int firstVisible;
for (firstVisible = 0; firstVisible < buttons.Length; firstVisible++) {
if (buttons[firstVisible] != null && buttons[firstVisible].Visible) {
break;
}
}
if (firstVisible == buttons.Length) {
firstVisible = 0;
}
SendMessage(NativeMethods.TB_GETRECT, firstVisible, ref rect);
// height is the button's height plus some extra goo
//
height = rect.bottom - rect.top;
}
// if the ToolBar is wrappable, and there is more than one row, make
// sure the height is correctly adjusted
//
if (Wrappable && IsHandleCreated) {
height = height * unchecked( (int) (long)SendMessage(NativeMethods.TB_GETROWS, 0, 0));
}
height = (height > 0) ? height : 1;
switch(borderStyle) {
case BorderStyle.FixedSingle:
height += SystemInformation.BorderSize.Height;
break;
case BorderStyle.Fixed3D:
height += SystemInformation.Border3DSize.Height;
break;
}
if (Divider) height += 2;
height += 4;
return height;
}
}
/// <include file='doc\ToolBar.uex' path='docs/doc[@for="ToolBar.PreferredWidth"]/*' />
/// <devdoc>
/// The preferred width for this ToolBar control. This is
/// used by AutoSizing code.
/// NOTE!!!!!!!!! This function assumes it's only going to get called
/// if the control is docked left or right [ie, it really
/// just returns a max width]
/// </devdoc>
internal int PreferredWidth {
get {
int width;
// fortunately, we compute this value sometimes, so we can just
// use it if we have it.
//
if (maxWidth == -1) {
// don't have it, have to recompute
//
if (!IsHandleCreated || buttons == null)
maxWidth = ButtonSize.Width;
else {
NativeMethods.RECT rect = new NativeMethods.RECT();
for (int x = 0; x < buttonCount; x++) {
SendMessage(NativeMethods.TB_GETRECT, 0, ref rect);
if ((rect.right - rect.left) > maxWidth)
maxWidth = rect.right - rect.left;
}
}
}
width = maxWidth;
if (borderStyle != BorderStyle.None) {
width += SystemInformation.BorderSize.Height * 4 + 3;
}
return width;
}
}
/// <include file='doc\ToolBar.uex' path='docs/doc[@for="ToolBar.RightToLeft"]/*' />
/// <internalonly/>
/// <devdoc>
/// </devdoc>
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
public override RightToLeft RightToLeft {
get {
return base.RightToLeft;
}
set {
base.RightToLeft = value;
}
}
/// <include file='doc\ToolBar.uex' path='docs/doc[@for="ToolBar.RightToLeftChanged"]/*' />
/// <internalonly/>
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
public new event EventHandler RightToLeftChanged {
add {
base.RightToLeftChanged += value;
}
remove {
base.RightToLeftChanged -= value;
}
}
/// <include file='doc\ToolBar.uex' path='docs/doc[@for="ToolBar.ScaleCore"]/*' />
/// <devdoc>
/// VSWhidbey 219660: We need to track the current scale factor so that we can tell the
/// unmanaged control how to scale its buttons.
/// </devdoc>
/// <internalonly/>
[EditorBrowsable(EditorBrowsableState.Never)]
protected override void ScaleCore(float dx, float dy) {
currentScaleDX = dx;
currentScaleDY = dy;
base.ScaleCore(dx, dy);
UpdateButtons();
}
/// <include file='doc\ToolBar.uex' path='docs/doc[@for="ToolBar.ScaleControl"]/*' />
/// <devdoc>
/// VSWhidbey 219660: We need to track the current scale factor so that we can tell the
/// unmanaged control how to scale its buttons.
/// </devdoc>
protected override void ScaleControl(SizeF factor, BoundsSpecified specified) {
currentScaleDX = factor.Width;
currentScaleDY = factor.Height;
base.ScaleControl(factor, specified);
}
/// <include file='doc\ToolBar.uex' path='docs/doc[@for="ToolBar.ShowToolTips"]/*' />
/// <devdoc>
/// <para>
/// Gets or sets a value indicating whether the toolbar displays a
/// tool tip for each button.</para>
/// </devdoc>
[
SRCategory(SR.CatBehavior),
DefaultValue(false),
Localizable(true),
SRDescription(SR.ToolBarShowToolTipsDescr)
]
public bool ShowToolTips {
get {
return toolBarState[TOOLBARSTATE_showToolTips];
}
set {
if (ShowToolTips != value) {
toolBarState[TOOLBARSTATE_showToolTips] = value;
RecreateHandle();
}
}
}
/// <include file='doc\ToolBar.uex' path='docs/doc[@for="ToolBar.TabStop"]/*' />
/// <internalonly/>
/// <devdoc>
/// </devdoc>
[DefaultValue(false)]
new public bool TabStop {
get {
return base.TabStop;
}
set {
base.TabStop = value;
}
}
/// <include file='doc\ToolBar.uex' path='docs/doc[@for="ToolBar.Text"]/*' />
/// <internalonly/>
/// <devdoc>
/// </devdoc>
[
Browsable(false), EditorBrowsable(EditorBrowsableState.Never),
Bindable(false),
DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)
]
public override string Text {
get {
return base.Text;
}
set {
base.Text = value;
}
}
/// <include file='doc\ToolBar.uex' path='docs/doc[@for="ToolBar.TextChanged"]/*' />
/// <internalonly/>
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
new public event EventHandler TextChanged {
add {
base.TextChanged += value;
}
remove {
base.TextChanged -= value;
}
}
/// <include file='doc\ToolBar.uex' path='docs/doc[@for="ToolBar.TextAlign"]/*' />
/// <devdoc>
/// <para> Gets or sets the alignment of text in relation to each
/// image displayed on
/// the toolbar button controls.</para>
/// </devdoc>
[
SRCategory(SR.CatAppearance),
DefaultValue(ToolBarTextAlign.Underneath),
Localizable(true),
SRDescription(SR.ToolBarTextAlignDescr)
]
public ToolBarTextAlign TextAlign {
get {
return textAlign;
}
set {
//valid values are 0x0 to 0x1
if (!ClientUtils.IsEnumValid(value, (int)value, (int)ToolBarTextAlign.Underneath, (int)ToolBarTextAlign.Right)) {
throw new InvalidEnumArgumentException("value", (int)value, typeof(ToolBarTextAlign));
}
if (textAlign == value) return;
textAlign = value;
RecreateHandle();
}
}
/// <include file='doc\ToolBar.uex' path='docs/doc[@for="ToolBar.Wrappable"]/*' />
/// <devdoc>
/// <para> Gets
/// or sets a value
/// indicating whether the toolbar buttons wrap to the next line if the
/// toolbar becomes too small to display all the buttons
/// on the same line.</para>
/// </devdoc>
[
SRCategory(SR.CatBehavior),
DefaultValue(true),
Localizable(true),
SRDescription(SR.ToolBarWrappableDescr)
]
public bool Wrappable {
get {
return toolBarState[TOOLBARSTATE_wrappable];
}
set {
if (Wrappable != value) {
toolBarState[TOOLBARSTATE_wrappable] = value;
RecreateHandle();
}
}
}
/// <include file='doc\ToolBar.uex' path='docs/doc[@for="ToolBar.ButtonClick"]/*' />
/// <devdoc>
/// <para>Occurs when a <see cref='System.Windows.Forms.ToolBarButton'/> on the <see cref='System.Windows.Forms.ToolBar'/> is clicked.</para>
/// </devdoc>
[SRCategory(SR.CatBehavior), SRDescription(SR.ToolBarButtonClickDescr)]
public event ToolBarButtonClickEventHandler ButtonClick {
add {
onButtonClick += value;
}
remove {
onButtonClick -= value;
}
}
/// <include file='doc\ToolBar.uex' path='docs/doc[@for="ToolBar.ButtonDropDown"]/*' />
/// <devdoc>
/// <para>Occurs when a drop-down style <see cref='System.Windows.Forms.ToolBarButton'/> or its down arrow is clicked.</para>
/// </devdoc>
[SRCategory(SR.CatBehavior), SRDescription(SR.ToolBarButtonDropDownDescr)]
public event ToolBarButtonClickEventHandler ButtonDropDown {
add {
onButtonDropDown += value;
}
remove {
onButtonDropDown -= value;
}
}
/// <include file='doc\ToolBar.uex' path='docs/doc[@for="ToolBar.OnPaint"]/*' />
/// <devdoc>
/// ToolBar 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\ToolBar.uex' path='docs/doc[@for="ToolBar.AdjustSize"]/*' />
/// <devdoc>
/// Adjusts the height or width of the ToolBar to make sure we have enough
/// room to show the buttons.
/// </devdoc>
/// <internalonly/>
// VS 30082 -- we pass in a value for dock rather than calling Dock ourselves
// because we can't change Dock until the size has been properly adjusted.
private void AdjustSize(DockStyle dock) {
int saveSize = requestedSize;
try {
if (dock == DockStyle.Left || dock == DockStyle.Right) {
Width = AutoSize ? PreferredWidth : saveSize;
}
else {
Height = AutoSize ? PreferredHeight : saveSize;
}
}
finally {
requestedSize = saveSize;
}
}
/// <include file='doc\ToolBar.uex' path='docs/doc[@for="ToolBar.BeginUpdate"]/*' />
/// <devdoc>
/// This routine lets us change a bunch of things about the toolbar without
/// having each operation wait for the paint to complete. This must be
/// matched up with a call to endUpdate().
/// </devdoc>
internal void BeginUpdate() {
BeginUpdateInternal();
}
/// <include file='doc\ToolBar.uex' path='docs/doc[@for="ToolBar.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_BAR_CLASSES;
SafeNativeMethods.InitCommonControlsEx(icc);
}
finally {
UnsafeNativeMethods.ThemingScope.Deactivate(userCookie);
}
}
base.CreateHandle();
}
/// <include file='doc\ToolBar.uex' path='docs/doc[@for="ToolBar.DetachImageList"]/*' />
/// <devdoc>
/// Resets the imageList to null. We wire this method up to the imageList's
/// Dispose event, so that we don't hang onto an imageList that's gone away.
/// </devdoc>
private void DetachImageList(object sender, EventArgs e) {
ImageList = null;
}
/// <include file='doc\ToolBar.uex' path='docs/doc[@for="ToolBar.Dispose"]/*' />
/// <devdoc>
/// </devdoc>
/// <internalonly/>
protected override void Dispose(bool disposing) {
if (disposing) {
//
lock(this) {
// We need to mark the Disposing state here so buttonsCollection won't attempt to update
// the buttons. VSW#340606.
bool currentDisposing = GetState(STATE_DISPOSING);
try {
SetState(STATE_DISPOSING, true);
if (imageList != null) {
imageList.Disposed -= new EventHandler(DetachImageList);
imageList = null;
}
if (buttonsCollection != null) {
ToolBarButton[] buttonCopy = new ToolBarButton[buttonsCollection.Count];
((ICollection)buttonsCollection).CopyTo(buttonCopy, 0);
buttonsCollection.Clear();
foreach (ToolBarButton b in buttonCopy) {
b.Dispose();
}
}
}
finally {
SetState(STATE_DISPOSING, currentDisposing);
}
}
}
base.Dispose(disposing);
}
/// <include file='doc\ToolBar.uex' path='docs/doc[@for="ToolBar.EndUpdate"]/*' />
/// <devdoc>
/// This routine lets us change a bunch of things about the toolbar without
/// having each operation wait for the paint to complete. This must be
/// matched up with a call to beginUpdate().
/// </devdoc>
internal void EndUpdate() {
EndUpdateInternal();
}
/// <include file='doc\ToolBar.uex' path='docs/doc[@for="ToolBar.ForceButtonWidths"]/*' />
/// <devdoc>
/// Forces the button sizes based on various different things. The default
/// ToolBar button sizing rules are pretty primitive and this tends to be
/// a little better, and lets us actually show things like DropDown Arrows
/// for ToolBars
/// </devdoc>
/// <internalonly/>
private void ForceButtonWidths() {
if (buttons != null && buttonSize.IsEmpty && IsHandleCreated) {
// force ourselves to re-compute this each time
//
maxWidth = -1;
for (int x = 0; x < buttonCount; x++) {
NativeMethods.TBBUTTONINFO tbbi = new NativeMethods.TBBUTTONINFO();
tbbi.cbSize = Marshal.SizeOf(typeof(NativeMethods.TBBUTTONINFO));
tbbi.cx = buttons[x].Width;
if (tbbi.cx > maxWidth) {
maxWidth = tbbi.cx;
}
tbbi.dwMask = NativeMethods.TBIF_SIZE;
UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.TB_SETBUTTONINFO, x, ref tbbi);
}
}
}
private void ImageListRecreateHandle(object sender, EventArgs e) {
if (IsHandleCreated)
RecreateHandle();
}
/// <include file='doc\ToolBar.uex' path='docs/doc[@for="ToolBar.Insert"]/*' />
/// <internalonly/>
/// <devdoc>
/// </devdoc>
private void Insert(int index, ToolBarButton button) {
button.parent = this;
if (buttons == null) {
buttons = new ToolBarButton[4];
}
else if (buttons.Length == buttonCount) {
ToolBarButton[] newButtons = new ToolBarButton[buttonCount + 4];
System.Array.Copy(buttons, 0, newButtons, 0, buttonCount);
buttons = newButtons;
}
if (index < buttonCount)
System.Array.Copy(buttons, index, buttons, index + 1, buttonCount - index);
buttons[index] = button;
buttonCount++;
}
/// <include file='doc\ToolBar.uex' path='docs/doc[@for="ToolBar.InsertButton"]/*' />
/// <devdoc>
/// <para>Inserts a button at a given location on the toolbar control.</para>
/// </devdoc>
private void InsertButton(int index, ToolBarButton value) {
if (value == null)
throw new ArgumentNullException("value");
if (index < 0 || ((buttons != null) && (index > buttonCount)))
throw new ArgumentOutOfRangeException("index", SR.GetString(SR.InvalidArgument, "index", index.ToString(CultureInfo.CurrentCulture)));
// insert the button into our local array, and then into the
// real windows ToolBar control
//
Insert(index, value);
if (IsHandleCreated) {
NativeMethods.TBBUTTON tbbutton = value.GetTBBUTTON(index);
UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.TB_INSERTBUTTON, index, ref tbbutton);
}
UpdateButtons();
}
/// <include file='doc\ToolBar.uex' path='docs/doc[@for="ToolBar.InternalAddButton"]/*' />
/// <devdoc>
/// Adds a button to the ToolBar
/// </devdoc>
/// <internalonly/>
private int InternalAddButton(ToolBarButton button) {
if (button == null)
throw new ArgumentNullException("button");
int index = buttonCount;
Insert(index, button);
return index;
}
/// <include file='doc\ToolBar.uex' path='docs/doc[@for="ToolBar.InternalSetButton"]/*' />
/// <devdoc>
/// Changes the data for a button in the ToolBar, and then does the appropriate
/// work to update the ToolBar control.
/// </devdoc>
/// <internalonly/>
internal void InternalSetButton(int index, ToolBarButton value, bool recreate, bool updateText) {
// tragically, there doesn't appear to be a way to remove the
// string for the button if it has one, so we just have to leave
// it in there.
//
buttons[index].parent = null;
buttons[index].stringIndex = (IntPtr)(-1);
buttons[index] = value;
buttons[index].parent = this;
if (IsHandleCreated) {
NativeMethods.TBBUTTONINFO tbbi = value.GetTBBUTTONINFO(updateText, index);
UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.TB_SETBUTTONINFO, index, ref tbbi);
if (tbbi.pszText != IntPtr.Zero) {
Marshal.FreeHGlobal(tbbi.pszText);
}
if (recreate) {
UpdateButtons();
}
else {
// after doing anything with the comctl ToolBar control, this
// appears to be a good idea.
//
SendMessage(NativeMethods.TB_AUTOSIZE, 0, 0);
ForceButtonWidths();
this.Invalidate();
}
}
}
/// <include file='doc\ToolBar.uex' path='docs/doc[@for="ToolBar.OnButtonClick"]/*' />
/// <devdoc>
/// <para>Raises the <see cref='System.Windows.Forms.ToolBar.ButtonClick'/>
/// event.</para>
/// </devdoc>
protected virtual void OnButtonClick(ToolBarButtonClickEventArgs e) {
if (onButtonClick != null) onButtonClick(this, e);
}
/// <include file='doc\ToolBar.uex' path='docs/doc[@for="ToolBar.OnButtonDropDown"]/*' />
/// <devdoc>
/// <para>Raises the <see cref='System.Windows.Forms.ToolBar.ButtonDropDown'/>
/// event.</para>
/// </devdoc>
protected virtual void OnButtonDropDown(ToolBarButtonClickEventArgs e) {
if (onButtonDropDown != null) onButtonDropDown(this, e);
}
/// <include file='doc\ToolBar.uex' path='docs/doc[@for="ToolBar.OnHandleCreated"]/*' />
/// <internalonly/>
/// <devdoc>
/// <para>
/// Overridden from the control class so we can add all the buttons
/// and do whatever work needs to be done.
/// Don't forget to call base.OnHandleCreated.
/// </para>
/// </devdoc>
protected override void OnHandleCreated(EventArgs e) {
base.OnHandleCreated(e);
// we have to set the button struct size, because they don't.
//
SendMessage(NativeMethods.TB_BUTTONSTRUCTSIZE, Marshal.SizeOf(typeof(NativeMethods.TBBUTTON)), 0);
// set up some extra goo
//
if (DropDownArrows)
SendMessage(NativeMethods.TB_SETEXTENDEDSTYLE, 0, NativeMethods.TBSTYLE_EX_DRAWDDARROWS);
// if we have an imagelist, add it in now.
//
if (imageList != null)
SendMessage(NativeMethods.TB_SETIMAGELIST, 0, imageList.Handle);
RealizeButtons();
// Force a repaint, as occasionally the ToolBar border does not paint properly
// (comctl ToolBar is flaky)
//
BeginUpdate();
try {
Size size = Size;
Size = new Size(size.Width + 1, size.Height);
Size = size;
}
finally {
EndUpdate();
}
}
/// <include file='doc\ToolBar.uex' path='docs/doc[@for="ToolBar.OnResize"]/*' />
/// <internalonly/>
/// <devdoc>
/// <para>
/// The control is being resized. Make sure the width/height are correct.
/// </para>
/// </devdoc>
protected override void OnResize(EventArgs e) {
base.OnResize(e);
if (Wrappable) AdjustSize(Dock);
}
/// <include file='doc\ToolBar.uex' path='docs/doc[@for="ToolBar.OnFontChanged"]/*' />
/// <internalonly/>
/// <devdoc>
/// <para>
/// Overridden to ensure that the buttons and the control resize properly
/// whenever the font changes.
/// </para>
/// </devdoc>
protected override void OnFontChanged(EventArgs e) {
base.OnFontChanged(e);
if (IsHandleCreated) {
if (!buttonSize.IsEmpty) {
SendToolbarButtonSizeMessage();
}
else {
AdjustSize(Dock);
ForceButtonWidths();
}
}
}
/// <include file='doc\ToolBar.uex' path='docs/doc[@for="ToolBar.RealizeButtons"]/*' />
/// <devdoc>
/// Sets all the button data into the ToolBar control
/// </devdoc>
/// <internalonly/>
private void RealizeButtons() {
if (buttons != null) {
IntPtr ptbbuttons = IntPtr.Zero;
try {
BeginUpdate();
// go and add in all the strings for all of our buttons
//
for (int x = 0; x < buttonCount; x++) {
if (buttons[x].Text.Length > 0) {
string addString = buttons[x].Text + '\0'.ToString();
buttons[x].stringIndex = SendMessage(NativeMethods.TB_ADDSTRING, 0, addString);
}
else {
buttons[x].stringIndex = (IntPtr)(-1);
}
}
// insert the buttons and set their parent pointers
//
int cb = Marshal.SizeOf(typeof(NativeMethods.TBBUTTON));
int count = buttonCount;
ptbbuttons = Marshal.AllocHGlobal(checked(cb * count));
for (int x = 0; x < count; x++) {
NativeMethods.TBBUTTON tbbutton = buttons[x].GetTBBUTTON(x);
Marshal.StructureToPtr(tbbutton, (IntPtr)(checked((long)ptbbuttons + (cb * x))), true);
buttons[x].parent = this;
}
SendMessage(NativeMethods.TB_ADDBUTTONS, count, ptbbuttons);
// after doing anything with the comctl ToolBar control, this
// appears to be a good idea.
//
SendMessage(NativeMethods.TB_AUTOSIZE, 0, 0);
// The win32 ToolBar control is somewhat unpredictable here. We
// have to set the button size after we've created all the
// buttons. Otherwise, we need to manually set the width of all
// the buttons so they look reasonable
//
if (!buttonSize.IsEmpty) {
SendToolbarButtonSizeMessage();
}
else {
ForceButtonWidths();
}
AdjustSize(Dock);
}
finally {
Marshal.FreeHGlobal(ptbbuttons);
EndUpdate();
}
}
}
/// <include file='doc\ToolBar.uex' path='docs/doc[@for="ToolBar.RemoveAt"]/*' />
/// <internalonly/>
/// <devdoc>
/// </devdoc>
private void RemoveAt(int index) {
buttons[index].parent = null;
buttons[index].stringIndex = (IntPtr)(-1);
buttonCount--;
if (index < buttonCount)
System.Array.Copy(buttons, index + 1, buttons, index, buttonCount - index);
buttons[buttonCount] = null;
}
/// <include file='doc\ToolBar.uex' path='docs/doc[@for="ToolBar.ResetButtonSize"]/*' />
/// <devdoc>
/// <para> Resets the toolbar buttons to the minimum
/// size. </para>
/// </devdoc>
private void ResetButtonSize() {
buttonSize = Size.Empty;
RecreateHandle();
}
/// Sends a TB_SETBUTTONSIZE message to the unmanaged control, with size arguments properly scaled.
private void SendToolbarButtonSizeMessage() {
SendMessage(NativeMethods.TB_SETBUTTONSIZE, 0, NativeMethods.Util.MAKELPARAM((int)(buttonSize.Width*currentScaleDX), (int)(buttonSize.Height*currentScaleDY)));
}
/// <include file='doc\ToolBar.uex' path='docs/doc[@for="ToolBar.SetBoundsCore"]/*' />
/// <devdoc>
/// Overrides Control.setBoundsCore to enforce AutoSize.
/// </devdoc>
/// <internalonly/>
protected override void SetBoundsCore(int x, int y, int width, int height, BoundsSpecified specified) {
int originalHeight = height;
int originalWidth = width;
base.SetBoundsCore(x, y, width, height, specified);
Rectangle bounds = Bounds;
if (Dock == DockStyle.Left || Dock == DockStyle.Right) {
if ((specified & BoundsSpecified.Width) != BoundsSpecified.None) requestedSize = width;
if (AutoSize) width = PreferredWidth;
if (width != originalWidth && Dock == DockStyle.Right) {
int deltaWidth = originalWidth - width;
x += deltaWidth;
}
}
else {
if ((specified & BoundsSpecified.Height) != BoundsSpecified.None) requestedSize = height;
if (AutoSize) height = PreferredHeight;
if (height != originalHeight && Dock == DockStyle.Bottom) {
int deltaHeight = originalHeight - height;
y += deltaHeight;
}
}
base.SetBoundsCore(x, y, width, height, specified);
}
/// <include file='doc\ToolBar.uex' path='docs/doc[@for="ToolBar.ShouldSerializeButtonSize"]/*' />
/// <devdoc>
/// <para>Determines if the <see cref='System.Windows.Forms.ToolBar.ButtonSize'/> property needs to be persisted.</para>
/// </devdoc>
private bool ShouldSerializeButtonSize() {
return !buttonSize.IsEmpty;
}
/// <include file='doc\ToolBar.uex' path='docs/doc[@for="ToolBar.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) {
UnsafeNativeMethods.SendMessage(new HandleRef(this, this.Handle), NativeMethods.TB_SETTOOLTIPS, new HandleRef(toolTip, toolTip.Handle), 0);
}
/// <include file='doc\ToolBar.uex' path='docs/doc[@for="ToolBar.ToString"]/*' />
/// <devdoc>
/// Returns a string representation for this control.
/// </devdoc>
/// <internalonly/>
public override string ToString() {
string s = base.ToString();
s += ", Buttons.Count: " + buttonCount.ToString(CultureInfo.CurrentCulture);
if (buttonCount > 0)
s += ", Buttons[0]: " + buttons[0].ToString();
return s;
}
/// <include file='doc\ToolBar.uex' path='docs/doc[@for="ToolBar.UpdateButtons"]/*' />
/// <devdoc>
/// Updates all the information in the ToolBar. Tragically, the win32
/// control is pretty flakey, and the only real choice here is to recreate
/// the handle and re-realize all the buttons.
/// </devdoc>
/// <internalonly/>
internal void UpdateButtons() {
if (IsHandleCreated) {
RecreateHandle();
}
}
/// <include file='doc\ToolBar.uex' path='docs/doc[@for="ToolBar.WmNotifyDropDown"]/*' />
/// <devdoc>
/// The button clicked was a dropdown button. If it has a menu specified,
/// show it now. Otherwise, fire an onButtonDropDown event.
/// </devdoc>
/// <internalonly/>
private void WmNotifyDropDown(ref Message m) {
NativeMethods.NMTOOLBAR nmTB = (NativeMethods.NMTOOLBAR)m.GetLParam(typeof(NativeMethods.NMTOOLBAR));
ToolBarButton tbb = (ToolBarButton)buttons[nmTB.iItem];
if (tbb == null)
throw new InvalidOperationException(SR.GetString(SR.ToolBarButtonNotFound));
OnButtonDropDown(new ToolBarButtonClickEventArgs(tbb));
Menu menu = tbb.DropDownMenu;
if (menu != null) {
NativeMethods.RECT rc = new NativeMethods.RECT();
NativeMethods.TPMPARAMS tpm = new NativeMethods.TPMPARAMS();
SendMessage(NativeMethods.TB_GETRECT, nmTB.iItem, ref rc);
if ((menu.GetType()).IsAssignableFrom(typeof(ContextMenu))) {
((ContextMenu)menu).Show(this, new Point(rc.left, rc.bottom));
}
else {
Menu main = menu.GetMainMenu();
if (main != null) {
main.ProcessInitMenuPopup(menu.Handle);
}
UnsafeNativeMethods.MapWindowPoints(new HandleRef(nmTB.hdr, nmTB.hdr.hwndFrom), NativeMethods.NullHandleRef, ref rc, 2);
tpm.rcExclude_left = rc.left;
tpm.rcExclude_top = rc.top;
tpm.rcExclude_right = rc.right;
tpm.rcExclude_bottom = rc.bottom;
SafeNativeMethods.TrackPopupMenuEx(
new HandleRef(menu, menu.Handle),
NativeMethods.TPM_LEFTALIGN |
NativeMethods.TPM_LEFTBUTTON |
NativeMethods.TPM_VERTICAL,
rc.left, rc.bottom,
new HandleRef(this, Handle), tpm);
}
}
}
private void WmNotifyNeedText(ref Message m) {
NativeMethods.TOOLTIPTEXT ttt = (NativeMethods.TOOLTIPTEXT) m.GetLParam(typeof(NativeMethods.TOOLTIPTEXT));
int commandID = (int)ttt.hdr.idFrom;
ToolBarButton tbb = (ToolBarButton) buttons[commandID];
if (tbb != null && tbb.ToolTipText != null)
ttt.lpszText = tbb.ToolTipText;
else
ttt.lpszText = null;
ttt.hinst = IntPtr.Zero;
// RightToLeft reading order
//
if (RightToLeft == RightToLeft.Yes) {
ttt.uFlags |= NativeMethods.TTF_RTLREADING;
}
Marshal.StructureToPtr(ttt, m.LParam, false);
}
private void WmNotifyNeedTextA(ref Message m) {
NativeMethods.TOOLTIPTEXTA ttt = (NativeMethods.TOOLTIPTEXTA) m.GetLParam(typeof(NativeMethods.TOOLTIPTEXTA));
int commandID = (int)ttt.hdr.idFrom;
ToolBarButton tbb = (ToolBarButton) buttons[commandID];
if (tbb != null && tbb.ToolTipText != null)
ttt.lpszText = tbb.ToolTipText;
else
ttt.lpszText = null;
ttt.hinst = IntPtr.Zero;
// RightToLeft reading order
//
if (RightToLeft == RightToLeft.Yes) {
ttt.uFlags |= NativeMethods.TTF_RTLREADING;
}
Marshal.StructureToPtr(ttt, m.LParam, false);
}
// VSWhidbey 177016: Track the currently hot item since the user might be using the tab and
// arrow keys to navigate the toolbar and if that's the case, we'll need to know where to re-
// position the tooltip window when the underlying toolbar control attempts to display it.
private void WmNotifyHotItemChange(ref Message m) {
// Should we set the hot item?
NativeMethods.NMTBHOTITEM nmTbHotItem = (NativeMethods.NMTBHOTITEM)m.GetLParam(typeof(NativeMethods.NMTBHOTITEM));
if (NativeMethods.HICF_ENTERING == (nmTbHotItem.dwFlags & NativeMethods.HICF_ENTERING))
this.hotItem = nmTbHotItem.idNew;
else if (NativeMethods.HICF_LEAVING == (nmTbHotItem.dwFlags & NativeMethods.HICF_LEAVING))
this.hotItem = -1;
else if (NativeMethods.HICF_MOUSE == (nmTbHotItem.dwFlags & NativeMethods.HICF_MOUSE))
this.hotItem = nmTbHotItem.idNew;
else if (NativeMethods.HICF_ARROWKEYS == (nmTbHotItem.dwFlags & NativeMethods.HICF_ARROWKEYS))
this.hotItem = nmTbHotItem.idNew;
else if (NativeMethods.HICF_ACCELERATOR == (nmTbHotItem.dwFlags & NativeMethods.HICF_ACCELERATOR))
this.hotItem = nmTbHotItem.idNew;
else if (NativeMethods.HICF_DUPACCEL == (nmTbHotItem.dwFlags & NativeMethods.HICF_DUPACCEL))
this.hotItem = nmTbHotItem.idNew;
else if (NativeMethods.HICF_RESELECT == (nmTbHotItem.dwFlags & NativeMethods.HICF_RESELECT))
this.hotItem = nmTbHotItem.idNew;
else if (NativeMethods.HICF_LMOUSE == (nmTbHotItem.dwFlags & NativeMethods.HICF_LMOUSE))
this.hotItem = nmTbHotItem.idNew;
else if (NativeMethods.HICF_TOGGLEDROPDOWN == (nmTbHotItem.dwFlags & NativeMethods.HICF_TOGGLEDROPDOWN))
this.hotItem = nmTbHotItem.idNew;
}
/// <include file='doc\ToolBar.uex' path='docs/doc[@for="ToolBar.WmReflectCommand"]/*' />
/// <devdoc>
/// </devdoc>
/// <internalonly/>
private void WmReflectCommand(ref Message m) {
int id = NativeMethods.Util.LOWORD(m.WParam);
ToolBarButton tbb = buttons[id];
if (tbb != null) {
ToolBarButtonClickEventArgs e = new ToolBarButtonClickEventArgs(tbb);
OnButtonClick(e);
}
base.WndProc(ref m);
ResetMouseEventArgs();
}
/// <include file='doc\ToolBar.uex' path='docs/doc[@for="ToolBar.WndProc"]/*' />
/// <devdoc>
/// </devdoc>
/// <internalonly/>
[SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)]
protected override void WndProc(ref Message m) {
switch (m.Msg) {
case NativeMethods.WM_COMMAND + NativeMethods.WM_REFLECT:
WmReflectCommand(ref m);
break;
case NativeMethods.WM_NOTIFY:
case NativeMethods.WM_NOTIFY + NativeMethods.WM_REFLECT:
NativeMethods.NMHDR note = (NativeMethods.NMHDR) m.GetLParam(typeof(NativeMethods.NMHDR));
switch (note.code) {
case NativeMethods.TTN_NEEDTEXTA:
// MSDN:
// Setting the max width has the added benefit of enabling multiline
// tool tips!
//
// Microsoft: temporarily commenting out this code to fix Pri0 bug 152233
// UnsafeNativeMethods.SendMessage(new HandleRef(note, note.hwndFrom), NativeMethods.TTM_SETMAXTIPWIDTH, 0, SystemInformation.MaxWindowTrackSize.Width);
WmNotifyNeedTextA(ref m);
m.Result = (IntPtr)1;
return;
case NativeMethods.TTN_NEEDTEXTW:
// On Win 98/IE 5,we still get W messages. If we ignore them, it will send the A version.
if (Marshal.SystemDefaultCharSize == 2) {
// MSDN:
// Setting the max width has the added benefit of enabling multiline
// tool tips!
//
// Microsoft: temporarily commenting out this code to fix Pri0 bug 152233
// UnsafeNativeMethods.SendMessage(new HandleRef(note, note.hwndFrom), NativeMethods.TTM_SETMAXTIPWIDTH, 0, SystemInformation.MaxWindowTrackSize.Width);
WmNotifyNeedText(ref m);
m.Result = (IntPtr)1;
return;
}
break;
case NativeMethods.TTN_SHOW:
// VSWhidbey 177016: Prevent the tooltip from displaying in the upper left corner of the
// desktop when the control is nowhere near that location.
NativeMethods.WINDOWPLACEMENT wndPlacement = new NativeMethods.WINDOWPLACEMENT();
int nRet = UnsafeNativeMethods.GetWindowPlacement(new HandleRef(null, note.hwndFrom), ref wndPlacement);
// Is this tooltip going to be positioned in the upper left corner of the display,
// but nowhere near the toolbar button?
if (wndPlacement.rcNormalPosition_left == 0 &&
wndPlacement.rcNormalPosition_top == 0 &&
this.hotItem != -1) {
// Assume that we're going to vertically center the tooltip on the right edge of the current
// hot item.
// Where is the right edge of the current hot item?
int buttonRight = 0;
for(int idx = 0; idx <= this.hotItem; idx++) {
// How wide is the item at this index? (It could be a separator, and therefore a different width.)
buttonRight += buttonsCollection[idx].GetButtonWidth();
}
// Where can we place this tooltip so that it will be completely visible on the current display?
int tooltipWidth = wndPlacement.rcNormalPosition_right - wndPlacement.rcNormalPosition_left;
int tooltipHeight = wndPlacement.rcNormalPosition_bottom - wndPlacement.rcNormalPosition_top;
// We'll need screen coordinates of this position for setting the tooltip's position
int x = this.Location.X + buttonRight + 1;
int y = this.Location.Y + (this.ButtonSize.Height / 2);
NativeMethods.POINT leftTop = new NativeMethods.POINT(x, y);
UnsafeNativeMethods.ClientToScreen(new HandleRef(this, this.Handle), leftTop);
// Will the tooltip bleed off the top?
if (leftTop.y < SystemInformation.WorkingArea.Y) {
// Reposition the tooltip to be displayed below the button
leftTop.y += (this.ButtonSize.Height / 2) + 1;
}
// Will the tooltip bleed off the bottom?
if (leftTop.y + tooltipHeight > SystemInformation.WorkingArea.Height) {
// Reposition the tooltip to be displayed above the button
leftTop.y -= ((this.ButtonSize.Height / 2) + tooltipHeight + 1);
}
// Will the tooltip bleed off the right edge?
if (leftTop.x + tooltipWidth > SystemInformation.WorkingArea.Right) {
// Move the tooltip far enough left that it will display in the working area
leftTop.x -= (this.ButtonSize.Width + tooltipWidth + 2);
}
SafeNativeMethods.SetWindowPos(new HandleRef(null, note.hwndFrom), NativeMethods.NullHandleRef, leftTop.x, leftTop.y, 0, 0, NativeMethods.SWP_NOSIZE | NativeMethods.SWP_NOZORDER | NativeMethods.SWP_NOACTIVATE);
m.Result = (IntPtr)1;
return;
}
break;
case NativeMethods.TBN_HOTITEMCHANGE:
WmNotifyHotItemChange(ref m);
break;
case NativeMethods.TBN_QUERYINSERT:
m.Result = (IntPtr)1;
break;
case NativeMethods.TBN_DROPDOWN:
WmNotifyDropDown(ref m);
break;
}
break;
}
base.WndProc(ref m);
}
/// <include file='doc\ToolBar.uex' path='docs/doc[@for="ToolBar.ToolBarButtonCollection"]/*' />
/// <devdoc>
/// <para>Encapsulates a collection of <see cref='System.Windows.Forms.ToolBarButton'/> controls for use by the
/// <see cref='System.Windows.Forms.ToolBar'/> class. </para>
/// </devdoc>
public class ToolBarButtonCollection : IList {
private ToolBar owner;
private bool suspendUpdate;
/// 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\ToolBar.uex' path='docs/doc[@for="ToolBar.ToolBarButtonCollection.ToolBarButtonCollection"]/*' />
/// <devdoc>
/// <para>Initializes a new instance of the <see cref='System.Windows.Forms.ToolBar.ToolBarButtonCollection'/> class and assigns it to the specified toolbar.</para>
/// </devdoc>
public ToolBarButtonCollection(ToolBar owner) {
this.owner = owner;
}
/// <include file='doc\ToolBar.uex' path='docs/doc[@for="ToolBar.ToolBarButtonCollection.this"]/*' />
/// <devdoc>
/// <para>Gets or sets the toolbar button at the specified indexed location in the
/// toolbar button collection.</para>
/// </devdoc>
public virtual ToolBarButton this[int index] {
get {
if (index < 0 || ((owner.buttons != null) && (index >= owner.buttonCount)))
throw new ArgumentOutOfRangeException("index", SR.GetString(SR.InvalidArgument, "index", index.ToString(CultureInfo.CurrentCulture)));
return owner.buttons[index];
}
set {
// Sanity check parameters
//
if (index < 0 || ((owner.buttons != null) && index >= owner.buttonCount)) {
throw new ArgumentOutOfRangeException("index", SR.GetString(SR.InvalidArgument, "index", index.ToString(CultureInfo.CurrentCulture)));
}
if (value == null) {
throw new ArgumentNullException("value");
}
owner.InternalSetButton(index, value, true, true);
}
}
/// <include file='doc\ToolBar.uex' path='docs/doc[@for="ToolBarButtonCollection.IList.this"]/*' />
/// <internalonly/>
object IList.this[int index] {
get {
return this[index];
}
set {
if (value is ToolBarButton) {
this[index] = (ToolBarButton)value;
}
else {
throw new ArgumentException(SR.GetString(SR.ToolBarBadToolBarButton),"value");
}
}
}
/// <include file='doc\ToolBar.uex' path='docs/doc[@for="ToolBar.ToolBarButtonCollection.this"]/*' />
/// <devdoc>
/// <para>Retrieves the child control with the specified key.</para>
/// </devdoc>
public virtual ToolBarButton 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\ToolBar.uex' path='docs/doc[@for="ToolBar.ToolBarButtonCollection.Count"]/*' />
/// <devdoc>
/// <para> Gets the number of buttons in the toolbar button collection.</para>
/// </devdoc>
[Browsable(false)]
public int Count {
get {
return owner.buttonCount;
}
}
/// <include file='doc\ToolBar.uex' path='docs/doc[@for="ToolBarButtonCollection.ICollection.SyncRoot"]/*' />
/// <internalonly/>
object ICollection.SyncRoot {
get {
return this;
}
}
/// <include file='doc\ToolBar.uex' path='docs/doc[@for="ToolBarButtonCollection.ICollection.IsSynchronized"]/*' />
/// <internalonly/>
bool ICollection.IsSynchronized {
get {
return false;
}
}
/// <include file='doc\ToolBar.uex' path='docs/doc[@for="ToolBarButtonCollection.IList.IsFixedSize"]/*' />
/// <internalonly/>
bool IList.IsFixedSize {
get {
return false;
}
}
/// <include file='doc\ToolBar.uex' path='docs/doc[@for="ToolBar.ToolBarButtonCollection.IsReadOnly"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
public bool IsReadOnly {
get {
return false;
}
}
/// <include file='doc\ToolBar.uex' path='docs/doc[@for="ToolBar.ToolBarButtonCollection.Add"]/*' />
/// <devdoc>
/// <para>Adds a new toolbar button to
/// the end of the toolbar button collection.</para>
/// </devdoc>
public int Add(ToolBarButton button) {
int index = owner.InternalAddButton(button);
if (!suspendUpdate) {
owner.UpdateButtons();
}
return index;
}
/// <include file='doc\ToolBar.uex' path='docs/doc[@for="ToolBar.ToolBarButtonCollection.Add1"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
public int Add(string text) {
ToolBarButton button = new ToolBarButton(text);
return Add(button);
}
/// <include file='doc\ToolBar.uex' path='docs/doc[@for="ToolBarButtonCollection.IList.Add"]/*' />
/// <internalonly/>
int IList.Add(object button) {
if (button is ToolBarButton) {
return Add((ToolBarButton)button);
}
else {
throw new ArgumentException(SR.GetString(SR.ToolBarBadToolBarButton),"button");
}
}
/// <include file='doc\ToolBar.uex' path='docs/doc[@for="ToolBar.ToolBarButtonCollection.AddRange"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
public void AddRange(ToolBarButton[] buttons) {
if (buttons == null) {
throw new ArgumentNullException("buttons");
}
try {
suspendUpdate = true;
foreach(ToolBarButton button in buttons) {
Add(button);
}
}
finally {
suspendUpdate = false;
owner.UpdateButtons();
}
}
/// <include file='doc\ToolBar.uex' path='docs/doc[@for="ToolBar.ToolBarButtonCollection.Clear"]/*' />
/// <devdoc>
/// <para>Removes
/// all buttons from the toolbar button collection.</para>
/// </devdoc>
public void Clear() {
if (owner.buttons == null) {
return;
}
for (int x = owner.buttonCount; x > 0; x--) {
if (owner.IsHandleCreated) {
owner.SendMessage(NativeMethods.TB_DELETEBUTTON, x - 1, 0);
}
owner.RemoveAt(x - 1);
}
owner.buttons = null;
owner.buttonCount = 0;
if (!owner.Disposing) {
owner.UpdateButtons();
}
}
/// <include file='doc\ToolBar.uex' path='docs/doc[@for="ToolBar.ToolBarButtonCollection.Contains"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
public bool Contains(ToolBarButton button) {
return IndexOf(button) != -1;
}
/// <include file='doc\ToolBar.uex' path='docs/doc[@for="ToolBarButtonCollection.IList.Contains"]/*' />
/// <internalonly/>
bool IList.Contains(object button) {
if (button is ToolBarButton) {
return Contains((ToolBarButton)button);
}
else {
return false;
}
}
/// <include file='doc\ToolBar.uex' path='docs/doc[@for="ToolBar.ToolBarButtonCollection.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\ToolBar.uex' path='docs/doc[@for="ToolBarButtonCollection.ICollection.CopyTo"]/*' />
/// <internalonly/>
void ICollection.CopyTo(Array dest, int index) {
if (owner.buttonCount > 0) {
System.Array.Copy(owner.buttons, 0, dest, index, owner.buttonCount);
}
}
/// <include file='doc\ToolBar.uex' path='docs/doc[@for="ToolBar.ToolBarButtonCollection.IndexOf"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
public int IndexOf(ToolBarButton button) {
for(int index=0; index < Count; ++index) {
if (this[index] == button) {
return index;
}
}
return -1;
}
/// <include file='doc\ToolBar.uex' path='docs/doc[@for="ToolBarButtonCollection.IList.IndexOf"]/*' />
/// <internalonly/>
int IList.IndexOf(object button) {
if (button is ToolBarButton) {
return IndexOf((ToolBarButton)button);
}
else {
return -1;
}
}
/// <include file='doc\ToolBar.uex' path='docs/doc[@for="ToolBar.ToolBarButtonCollection.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\ToolBar.uex' path='docs/doc[@for="ToolBar.ToolBarButtonCollection.Insert"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
public void Insert(int index, ToolBarButton button) {
owner.InsertButton(index, button);
}
/// <include file='doc\ToolBar.uex' path='docs/doc[@for="ToolBarButtonCollection.IList.Insert"]/*' />
/// <internalonly/>
void IList.Insert(int index, object button) {
if (button is ToolBarButton) {
Insert(index, (ToolBarButton)button);
}
else {
throw new ArgumentException(SR.GetString(SR.ToolBarBadToolBarButton),"button");
}
}
/// <include file='doc\ToolBar.uex' path='docs/doc[@for="ToolBar.ToolBarButtonCollection.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\ToolBar.uex' path='docs/doc[@for="ToolBar.ToolBarButtonCollection.RemoveAt"]/*' />
/// <devdoc>
/// <para>Removes
/// a given button from the toolbar button collection.</para>
/// </devdoc>
public void RemoveAt(int index) {
int count = (owner.buttons == null) ? 0 : owner.buttonCount;
if (index < 0 || index >= count)
throw new ArgumentOutOfRangeException("index", SR.GetString(SR.InvalidArgument, "index", index.ToString(CultureInfo.CurrentCulture)));
if (owner.IsHandleCreated) {
owner.SendMessage(NativeMethods.TB_DELETEBUTTON, index, 0);
}
owner.RemoveAt(index);
owner.UpdateButtons();
}
/// <include file='doc\ToolBar.uex' path='docs/doc[@for="ToolBar.ToolBarButtonCollection.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\ToolBar.uex' path='docs/doc[@for="ToolBar.ToolBarButtonCollection.Remove"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
public void Remove(ToolBarButton button) {
int index = IndexOf(button);
if (index != -1) {
RemoveAt(index);
}
}
/// <include file='doc\ToolBar.uex' path='docs/doc[@for="ToolBarButtonCollection.IList.Remove"]/*' />
/// <internalonly/>
void IList.Remove(object button) {
if (button is ToolBarButton) {
Remove((ToolBarButton)button);
}
}
/// <include file='doc\ToolBar.uex' path='docs/doc[@for="ToolBar.ToolBarButtonCollection.GetEnumerator"]/*' />
/// <devdoc>
/// <para>Returns an enumerator that can be used to iterate
/// through the toolbar button collection.</para>
/// </devdoc>
public IEnumerator GetEnumerator() {
return new WindowsFormsUtils.ArraySubsetEnumerator(owner.buttons, owner.buttonCount);
}
}
}
}
|