|
//------------------------------------------------------------------------------
// <copyright file="StatusStrip.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
//------------------------------------------------------------------------------
namespace System.Windows.Forms {
using System;
using System.ComponentModel;
using System.Drawing;
using System.Windows.Forms;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Collections.Specialized;
using System.Runtime.InteropServices;
using System.Windows.Forms.Layout;
using System.Security.Permissions;
using System.Security;
/// <include file='doc\StatusStrip.uex' path='docs/doc[@for="StatusStrip"]/*' />
[ComVisible(true),
ClassInterface(ClassInterfaceType.AutoDispatch),
SRDescription(SR.DescriptionStatusStrip)
]
public class StatusStrip : ToolStrip {
private const AnchorStyles AllAnchor = AnchorStyles.Left | AnchorStyles.Right | AnchorStyles.Bottom | AnchorStyles.Top;
private const AnchorStyles HorizontalAnchor = AnchorStyles.Left | AnchorStyles.Right;
private const AnchorStyles VerticalAnchor = AnchorStyles.Top | AnchorStyles.Bottom;
private BitVector32 state = new BitVector32();
private static readonly int stateSizingGrip = BitVector32.CreateMask();
private static readonly int stateCalledSpringTableLayout = BitVector32.CreateMask(stateSizingGrip);
private const int gripWidth = 12;
private RightToLeftLayoutGrip rtlLayoutGrip;
private Orientation lastOrientation = Orientation.Horizontal;
/// <include file='doc\StatusStrip.uex' path='docs/doc[@for="StatusStrip.StatusStrip"]/*' />
public StatusStrip() {
SuspendLayout();
this.CanOverflow = false;
this.LayoutStyle = ToolStripLayoutStyle.Table;
this.RenderMode = ToolStripRenderMode.System;
this.GripStyle = ToolStripGripStyle.Hidden;
SetStyle(ControlStyles.ResizeRedraw, true);
this.Stretch = true;
state[stateSizingGrip] = true;
ResumeLayout(true);
}
[
DefaultValue(false),
SRDescription(SR.ToolStripCanOverflowDescr),
SRCategory(SR.CatLayout),
Browsable(false)
]
public new bool CanOverflow {
get {
return base.CanOverflow;
}
set {
base.CanOverflow = value;
}
}
protected override bool DefaultShowItemToolTips {
get {
return false;
}
}
protected override Size DefaultSize {
get {
return new Size(200, 22);
}
}
protected override Padding DefaultPadding {
get {
if (Orientation == Orientation.Horizontal) {
if (RightToLeft == RightToLeft.No) {
return new Padding(1, 0, 14, 0);
}
else {
return new Padding(14, 0, 1, 0);
}
}
else {
// vertical
// the difference in symmetry here is that the grip does not actually rotate, it remains the same height it
// was before, so the DisplayRectangle needs to shrink up by its height.
return new Padding(1, 3, 1, DefaultSize.Height);
}
}
}
protected override DockStyle DefaultDock {
get {
return DockStyle.Bottom;
}
}
/// <include file='doc\StatusStrip.uex' path='docs/doc[@for="StatusStrip.Dock"]/*' />
[DefaultValue(DockStyle.Bottom)]
public override DockStyle Dock {
get {
return base.Dock;
}
set {
base.Dock = value;
}
}
[DefaultValue(ToolStripGripStyle.Hidden)]
public new ToolStripGripStyle GripStyle {
get {
return base.GripStyle;
}
set {
base.GripStyle = value;
}
}
[DefaultValue(ToolStripLayoutStyle.Table)]
public new ToolStripLayoutStyle LayoutStyle {
get { return base.LayoutStyle; }
set { base.LayoutStyle = value; }
}
// we do some custom stuff with padding to accomodate size grip.
// changing this is not supported at DT
[Browsable(false)]
public new Padding Padding {
get {
return base.Padding;
}
set {
base.Padding = value;
}
}
[Browsable(false)]
public new event EventHandler PaddingChanged {
add {
base.PaddingChanged += value;
}
remove {
base.PaddingChanged -= value;
}
}
private Control RTLGrip {
get{
if (rtlLayoutGrip == null) {
rtlLayoutGrip = new RightToLeftLayoutGrip();
}
return rtlLayoutGrip;
}
}
[DefaultValue(false)]
[SRDescription(SR.ToolStripShowItemToolTipsDescr)]
[SRCategory(SR.CatBehavior)]
public new bool ShowItemToolTips {
get {
return base.ShowItemToolTips;
}
set {
base.ShowItemToolTips = value;
}
}
// VSWhidbey 430251
// return whether we should paint the sizing grip.
private bool ShowSizingGrip {
get{
if (SizingGrip && IsHandleCreated) {
if (DesignMode) {
return true; // we dont care about the state of VS.
}
else {
HandleRef rootHwnd = WindowsFormsUtils.GetRootHWnd(this);
if (rootHwnd.Handle != IntPtr.Zero) {
return !UnsafeNativeMethods.IsZoomed(rootHwnd);
}
}
}
return false;
}
}
[
SRCategory(SR.CatAppearance),
DefaultValue(true),
SRDescription(SR.StatusStripSizingGripDescr)
]
public bool SizingGrip {
get {
return state[stateSizingGrip];
}
set {
if (value != state[stateSizingGrip]) {
state[stateSizingGrip] = value;
EnsureRightToLeftGrip();
Invalidate(true);
}
}
}
[Browsable(false)]
public Rectangle SizeGripBounds {
get {
if (SizingGrip) {
Size statusStripSize = this.Size;
// we cant necessarily make this the height of the status strip, as
// the orientation could change.
int gripHeight = Math.Min(DefaultSize.Height, statusStripSize.Height);
if (RightToLeft == RightToLeft.Yes) {
return new Rectangle(0, statusStripSize.Height - gripHeight, gripWidth, gripHeight);
}
else {
return new Rectangle(statusStripSize.Width - gripWidth, statusStripSize.Height - gripHeight, gripWidth, gripHeight);
}
}
return Rectangle.Empty;
}
}
[DefaultValue(true)]
[SRCategory(SR.CatLayout)]
[SRDescription(SR.ToolStripStretchDescr)]
public new bool Stretch {
get {
return base.Stretch;
}
set {
base.Stretch = value;
}
}
private TableLayoutSettings TableLayoutSettings {
get { return this.LayoutSettings as TableLayoutSettings; }
}
/// <include file='doc\StatusStrip.uex' path='docs/doc[@for="StatusStrip.CreateAccessibilityInstance"]/*' />
protected override AccessibleObject CreateAccessibilityInstance() {
return new StatusStripAccessibleObject(this);
}
protected internal override ToolStripItem CreateDefaultItem(string text, Image image, EventHandler onClick) {
return new ToolStripStatusLabel(text,image,onClick);
}
protected override void Dispose( bool disposing ) {
if (disposing) {
if (rtlLayoutGrip != null) {
rtlLayoutGrip.Dispose();
rtlLayoutGrip = null;
}
}
base.Dispose(disposing);
}
// in RTL, we parent a transparent control over the grip to support mirroring.
private void EnsureRightToLeftGrip() {
if (SizingGrip && RightToLeft == RightToLeft.Yes) {
RTLGrip.Bounds = SizeGripBounds;
if (!this.Controls.Contains(RTLGrip)) {
WindowsFormsUtils.ReadOnlyControlCollection controlCollection = this.Controls as WindowsFormsUtils.ReadOnlyControlCollection;
if (controlCollection != null) {
controlCollection.AddInternal(RTLGrip);
}
}
}
else if (rtlLayoutGrip != null) {
if (this.Controls.Contains(rtlLayoutGrip)) {
WindowsFormsUtils.ReadOnlyControlCollection controlCollection = this.Controls as WindowsFormsUtils.ReadOnlyControlCollection;
if (controlCollection != null) {
controlCollection.RemoveInternal(rtlLayoutGrip);
}
rtlLayoutGrip.Dispose();
rtlLayoutGrip = null;
}
}
}
internal override Size GetPreferredSizeCore(Size proposedSize) {
if (LayoutStyle == ToolStripLayoutStyle.Table) {
if (proposedSize.Width == 1) {
proposedSize.Width = Int32.MaxValue;
}
if (proposedSize.Height == 1) {
proposedSize.Height = Int32.MaxValue;
}
if (Orientation == Orientation.Horizontal) {
return GetPreferredSizeHorizontal(this, proposedSize) + Padding.Size;
}
else {
return GetPreferredSizeVertical(this, proposedSize) + Padding.Size;
}
}
return base.GetPreferredSizeCore(proposedSize);
}
protected override void OnPaintBackground(PaintEventArgs e) {
base.OnPaintBackground(e);
if (ShowSizingGrip) {
Renderer.DrawStatusStripSizingGrip(new ToolStripRenderEventArgs(e.Graphics, this));
}
}
protected override void OnLayout(LayoutEventArgs levent) {
state[stateCalledSpringTableLayout] = false;
bool inDisplayedItemCollecton = false;
ToolStripItem item = levent.AffectedComponent as ToolStripItem;
int itemCount = DisplayedItems.Count;
if (item != null) {
inDisplayedItemCollecton = DisplayedItems.Contains(item);
}
if (this.LayoutStyle == ToolStripLayoutStyle.Table) {
OnSpringTableLayoutCore();
}
base.OnLayout(levent);
if (itemCount != DisplayedItems.Count || (item != null && (inDisplayedItemCollecton != DisplayedItems.Contains(item)))) {
// calling OnLayout has changed the displayed items collection
// the SpringTableLayoutCore requires the count of displayed items to
// be accurate.
// - so we need to perform layout again.
if (this.LayoutStyle == ToolStripLayoutStyle.Table) {
OnSpringTableLayoutCore();
base.OnLayout(levent);
}
}
EnsureRightToLeftGrip();
}
internal override bool SupportsUiaProviders {
get {
return AccessibilityImprovements.Level3 && !DesignMode; ;
}
}
protected override void SetDisplayedItems() {
if (state[stateCalledSpringTableLayout]) {
bool rightToLeft = ((Orientation == Orientation.Horizontal) && (RightToLeft == RightToLeft.Yes));
// shove all items that dont fit one pixel outside the displayed region
Rectangle displayRect = DisplayRectangle;
Point noMansLand = displayRect.Location;
noMansLand.X += ClientSize.Width + 1;
noMansLand.Y += ClientSize.Height + 1;
bool overflow = false;
Rectangle lastItemBounds = Rectangle.Empty;
ToolStripItem lastItem = null;
for (int i = 0; i < Items.Count; i++) {
ToolStripItem item = Items[i];
// using spring layout we can get into a situation where there's extra items which arent
// visible.
if (overflow || ((IArrangedElement)item).ParticipatesInLayout) {
if (overflow || (SizingGrip && item.Bounds.IntersectsWith(SizeGripBounds))) {
// if the item collides with the size grip, set the location to nomansland.
SetItemLocation(item, noMansLand);
item.SetPlacement(ToolStripItemPlacement.None);
}
}
else if (lastItem != null && (lastItemBounds.IntersectsWith(item.Bounds))) {
// if it overlaps the previous element, set the location to nomansland.
SetItemLocation(item, noMansLand);
item.SetPlacement(ToolStripItemPlacement.None);
}
else if (item.Bounds.Width == 1) {
ToolStripStatusLabel panel = item as ToolStripStatusLabel;
if (panel != null && panel.Spring) {
// once we get down to one pixel, there can always be a one pixel
// distribution problem with the TLP - there's usually a spare hanging around.
// so set this off to nomansland as well.
SetItemLocation(item, noMansLand);
item.SetPlacement(ToolStripItemPlacement.None);
}
}
if (item.Bounds.Location != noMansLand){
// set the next item to inspect for collisions
lastItem = item;
lastItemBounds = lastItem.Bounds;
}
else {
// we cant fit an item, everything else after it should not be displayed
if (((IArrangedElement)item).ParticipatesInLayout) {
overflow = true;
}
}
}
}
base.SetDisplayedItems();
}
internal override void ResetRenderMode() {
RenderMode = ToolStripRenderMode.System;
}
internal override bool ShouldSerializeRenderMode() {
// We should NEVER serialize custom.
return (RenderMode != ToolStripRenderMode.System && RenderMode != ToolStripRenderMode.Custom);
}
/// <devdoc>
/// Override this function if you want to do custom table layouts for the
/// StatusStrip. The default layoutstyle is tablelayout, and we need to play
/// with the row/column styles
/// </devdoc>
protected virtual void OnSpringTableLayoutCore() {
if (this.LayoutStyle == ToolStripLayoutStyle.Table) {
state[stateCalledSpringTableLayout]= true;
this.SuspendLayout();
if (lastOrientation != Orientation) {
TableLayoutSettings settings = this.TableLayoutSettings;
settings.RowCount = 0;
settings.ColumnCount = 0;
settings.ColumnStyles.Clear();
settings.RowStyles.Clear();
}
lastOrientation = Orientation;
if (Orientation == Orientation.Horizontal) {
//
// Horizontal layout
//
TableLayoutSettings.GrowStyle = TableLayoutPanelGrowStyle.AddColumns;
int originalColumnCount = this.TableLayoutSettings.ColumnStyles.Count;
// iterate through the elements which are going to be displayed.
for (int i = 0; i < this.DisplayedItems.Count; i++) {
if (i >= originalColumnCount) {
// add if it's necessary.
this.TableLayoutSettings.ColumnStyles.Add(new ColumnStyle());
}
// determine if we "spring" or "autosize" the column
ToolStripStatusLabel panel = DisplayedItems[i] as ToolStripStatusLabel;
bool spring = (panel != null && panel.Spring);
DisplayedItems[i].Anchor = (spring) ? AllAnchor : VerticalAnchor;
// spring is achieved by using 100% as the column style
ColumnStyle colStyle = this.TableLayoutSettings.ColumnStyles[i];
colStyle.Width = 100; // this width is ignored in AutoSize.
colStyle.SizeType = (spring) ? SizeType.Percent : SizeType.AutoSize;
}
if (TableLayoutSettings.RowStyles.Count > 1 || TableLayoutSettings.RowStyles.Count == 0) {
TableLayoutSettings.RowStyles.Clear();
TableLayoutSettings.RowStyles.Add(new RowStyle());
}
TableLayoutSettings.RowCount = 1;
TableLayoutSettings.RowStyles[0].SizeType = SizeType.Absolute;
TableLayoutSettings.RowStyles[0].Height = Math.Max(0,this.DisplayRectangle.Height);
TableLayoutSettings.ColumnCount = DisplayedItems.Count+1; // add an extra cell so it fills the remaining space
// dont remove the extra column styles, just set them back to autosize.
for (int i = DisplayedItems.Count; i < TableLayoutSettings.ColumnStyles.Count; i++) {
this.TableLayoutSettings.ColumnStyles[i].SizeType = SizeType.AutoSize;
}
}
else {
//
// Vertical layout
//
TableLayoutSettings.GrowStyle = TableLayoutPanelGrowStyle.AddRows;
int originalRowCount = this.TableLayoutSettings.RowStyles.Count;
// iterate through the elements which are going to be displayed.
for (int i = 0; i < this.DisplayedItems.Count; i++) {
if (i >= originalRowCount) {
// add if it's necessary.
this.TableLayoutSettings.RowStyles.Add(new RowStyle());
}
// determine if we "spring" or "autosize" the row
ToolStripStatusLabel panel = DisplayedItems[i] as ToolStripStatusLabel;
bool spring = (panel != null && panel.Spring);
DisplayedItems[i].Anchor = (spring) ? AllAnchor : HorizontalAnchor;
// spring is achieved by using 100% as the row style
RowStyle rowStyle = this.TableLayoutSettings.RowStyles[i];
rowStyle.Height = 100; // this width is ignored in AutoSize.
rowStyle.SizeType = (spring) ? SizeType.Percent : SizeType.AutoSize;
}
TableLayoutSettings.ColumnCount = 1;
if (TableLayoutSettings.ColumnStyles.Count > 1 || TableLayoutSettings.ColumnStyles.Count == 0) {
TableLayoutSettings.ColumnStyles.Clear();
TableLayoutSettings.ColumnStyles.Add(new ColumnStyle());
}
TableLayoutSettings.ColumnCount = 1;
TableLayoutSettings.ColumnStyles[0].SizeType = SizeType.Absolute;
TableLayoutSettings.ColumnStyles[0].Width = Math.Max(0,this.DisplayRectangle.Width);
TableLayoutSettings.RowCount = DisplayedItems.Count+1; // add an extra cell so it fills the remaining space
// dont remove the extra column styles, just set them back to autosize.
for (int i = DisplayedItems.Count; i < TableLayoutSettings.RowStyles.Count; i++) {
this.TableLayoutSettings.RowStyles[i].SizeType = SizeType.AutoSize;
}
}
this.ResumeLayout(false);
}
}
[SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)]
protected override void WndProc(ref Message m) {
if ((m.Msg == NativeMethods.WM_NCHITTEST) && SizingGrip) {
// if we're within the grip bounds tell windows
// that we're the bottom right of the window.
Rectangle sizeGripBounds = SizeGripBounds;
int x = NativeMethods.Util.LOWORD(m.LParam);
int y = NativeMethods.Util.HIWORD(m.LParam);
if (sizeGripBounds.Contains(PointToClient(new Point(x, y)))) {
HandleRef rootHwnd = WindowsFormsUtils.GetRootHWnd(this);
// if the main window isnt maximized - we should paint a resize grip.
// double check that we're at the bottom right hand corner of the window.
if (rootHwnd.Handle != IntPtr.Zero && !UnsafeNativeMethods.IsZoomed(rootHwnd)) {
// get the client area of the topmost window. If we're next to the edge then
// the sizing grip is valid.
NativeMethods.RECT rootHwndClientArea = new NativeMethods.RECT();
UnsafeNativeMethods.GetClientRect(rootHwnd, ref rootHwndClientArea);
// map the size grip FROM statusStrip coords TO the toplevel window coords.
NativeMethods.POINT gripLocation;
if (RightToLeft == RightToLeft.Yes) {
gripLocation = new NativeMethods.POINT(SizeGripBounds.Left, SizeGripBounds.Bottom);
}
else {
gripLocation = new NativeMethods.POINT(SizeGripBounds.Right, SizeGripBounds.Bottom);
}
UnsafeNativeMethods.MapWindowPoints(new HandleRef(this, this.Handle), rootHwnd, gripLocation, 1);
int deltaBottomEdge = Math.Abs(rootHwndClientArea.bottom - gripLocation.y);
int deltaRightEdge = Math.Abs(rootHwndClientArea.right - gripLocation.x);
if (RightToLeft != RightToLeft.Yes) {
if ((deltaRightEdge + deltaBottomEdge) < 2) {
m.Result = (IntPtr)NativeMethods.HTBOTTOMRIGHT;
return;
}
}
}
}
}
base.WndProc(ref m);
}
// special transparent mirrored window which says it's the bottom left of the form.
private class RightToLeftLayoutGrip : Control {
public RightToLeftLayoutGrip() {
SetStyle(ControlStyles.SupportsTransparentBackColor, true);
this.BackColor = Color.Transparent;
}
protected override CreateParams CreateParams {
[SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)]
get {
CreateParams cp = base.CreateParams;
cp.ExStyle |= NativeMethods.WS_EX_LAYOUTRTL;
return cp;
}
}
[SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)]
protected override void WndProc(ref Message m) {
if (m.Msg == NativeMethods.WM_NCHITTEST) {
int x = NativeMethods.Util.LOWORD(m.LParam);
int y = NativeMethods.Util.HIWORD(m.LParam);
if (ClientRectangle.Contains(PointToClient(new Point(x, y)))) {
m.Result = (IntPtr)NativeMethods.HTBOTTOMLEFT;
return;
}
}
base.WndProc(ref m);
}
}
[System.Runtime.InteropServices.ComVisible(true)]
internal class StatusStripAccessibleObject : ToolStripAccessibleObject {
public StatusStripAccessibleObject(StatusStrip owner) : base(owner) {
}
public override AccessibleRole Role {
get {
AccessibleRole role = Owner.AccessibleRole;
if (role != AccessibleRole.Default) {
return role;
}
return AccessibleRole.StatusBar;
}
}
internal override object GetPropertyValue(int propertyID) {
if (AccessibilityImprovements.Level3 && propertyID == NativeMethods.UIA_ControlTypePropertyId) {
return NativeMethods.UIA_StatusBarControlTypeId;
}
return base.GetPropertyValue(propertyID);
}
internal override UnsafeNativeMethods.IRawElementProviderFragment FragmentNavigate(UnsafeNativeMethods.NavigateDirection direction) {
StatusStrip statusStrip = Owner as StatusStrip;
if (statusStrip == null || statusStrip.Items.Count == 0) {
return null;
}
switch (direction) {
case UnsafeNativeMethods.NavigateDirection.FirstChild:
AccessibleObject firstChild = null;
for (int i = 0; i < GetChildCount(); i++) {
firstChild = GetChild(i);
if (firstChild != null && !(firstChild is ControlAccessibleObject)) {
return firstChild;
}
}
return null;
case UnsafeNativeMethods.NavigateDirection.LastChild:
AccessibleObject lastChild = null;
for (int i = GetChildCount() - 1; i >= 0; i--) {
lastChild = GetChild(i);
if (lastChild != null && !(lastChild is ControlAccessibleObject)) {
return lastChild;
}
}
return null;
}
return base.FragmentNavigate(direction);
}
internal override UnsafeNativeMethods.IRawElementProviderFragment ElementProviderFromPoint(double x, double y) {
return HitTest((int)x, (int)y);
}
internal override UnsafeNativeMethods.IRawElementProviderFragment GetFocus() {
return GetFocused();
}
}
}
}
|