|
//#define DEBUG_PAINT
//------------------------------------------------------------------------------
// <copyright file="ToolStripPanel.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
//------------------------------------------------------------------------------
/*
*/
namespace System.Windows.Forms {
using System.Drawing;
using System.Windows.Forms.Layout;
using System.Collections.Specialized;
using System.Collections;
using System.ComponentModel;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.InteropServices;
using System.ComponentModel.Design.Serialization;
using System.Security.Permissions;
using System.Security;
using System.Globalization;
using System.Windows.Forms.Internal;
/// <include file='doc\ToolStripPanel.uex' path='docs/doc[@for="ToolStripPanel"]/*' />
[ComVisible(true)]
[ClassInterface(ClassInterfaceType.AutoDispatch)]
[Designer("System.Windows.Forms.Design.ToolStripPanelDesigner, " + AssemblyRef.SystemDesign)]
[ToolboxBitmapAttribute(typeof(ToolStripPanel), "ToolStripPanel_standalone.bmp")]
public class ToolStripPanel : ContainerControl, IArrangedElement {
private Orientation orientation = Orientation.Horizontal;
private static readonly Padding rowMargin = new Padding(3,0,0,0);
private Padding scaledRowMargin = rowMargin;
private ToolStripRendererSwitcher rendererSwitcher = null;
private Type currentRendererType = typeof(System.Type);
private BitVector32 state = new BitVector32();
private ToolStripContainer owner;
#if DEBUG
internal static TraceSwitch ToolStripPanelDebug = new TraceSwitch("ToolStripPanelDebug", "Debug code for rafting mouse movement");
internal static TraceSwitch ToolStripPanelFeedbackDebug = new TraceSwitch("ToolStripPanelFeedbackDebug", "Debug code for rafting feedback");
internal static TraceSwitch ToolStripPanelMissingRowDebug = new TraceSwitch("ToolStripPanelMissingRowDebug", "Debug code for rafting feedback");
#else
internal static TraceSwitch ToolStripPanelDebug;
internal static TraceSwitch ToolStripPanelFeedbackDebug;
internal static TraceSwitch ToolStripPanelMissingRowDebug;
#endif
[ThreadStatic]
private static Rectangle lastFeedbackRect = Rectangle.Empty;
// properties
private static readonly int PropToolStripPanelRowCollection = PropertyStore.CreateKey();
// states
private static readonly int stateLocked = BitVector32.CreateMask();
private static readonly int stateBeginInit = BitVector32.CreateMask(stateLocked);
private static readonly int stateChangingZOrder = BitVector32.CreateMask(stateBeginInit);
private static readonly int stateInJoin = BitVector32.CreateMask(stateChangingZOrder);
private static readonly int stateEndInit = BitVector32.CreateMask(stateInJoin);
private static readonly int stateLayoutSuspended = BitVector32.CreateMask(stateEndInit);
private static readonly int stateRightToLeftChanged = BitVector32.CreateMask(stateLayoutSuspended);
// events
internal static readonly Padding DragMargin = new Padding(10);
private static readonly object EventRendererChanged = new object();
/// <include file='doc\ToolStripPanel.uex' path='docs/doc[@for="ToolStripPanel.ToolStripPanel1"]/*' />
public ToolStripPanel() {
if (DpiHelper.EnableToolStripHighDpiImprovements) {
scaledRowMargin = DpiHelper.LogicalToDeviceUnits(rowMargin);
}
SuspendLayout();
AutoScaleMode = AutoScaleMode.None;
InitFlowLayout();
this.AutoSize = true;
this.MinimumSize = Size.Empty; // consider 1,1
this.state[stateLocked | stateBeginInit | stateChangingZOrder] = false;
this.TabStop = false;
ToolStripManager.ToolStripPanels.Add(this);
// not setting ControlStyles.AllPaintingInWmPaint as we dont do any painting in OnPaint... all
// is done in OnPaintBackground... so its better to show the rafting container in WM_ERASEBACKGROUND.
SetStyle(ControlStyles.ResizeRedraw | ControlStyles.OptimizedDoubleBuffer | /*ControlStyles.AllPaintingInWmPaint |*/ControlStyles.SupportsTransparentBackColor, true);
SetStyle(ControlStyles.Selectable, false);
ResumeLayout(true);
}
internal ToolStripPanel(ToolStripContainer owner)
: this() {
this.owner = owner;
}
[
Browsable(false),
EditorBrowsable(EditorBrowsableState.Never), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden),
]
public override bool AllowDrop {
get { return base.AllowDrop; }
set { base.AllowDrop = value; }
}
[
Browsable(false),
EditorBrowsable(EditorBrowsableState.Never), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden),
]
public override bool AutoScroll {
get { return base.AutoScroll; }
set { base.AutoScroll = value; }
}
[
Browsable(false),
EditorBrowsable(EditorBrowsableState.Never), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden),
]
public new Size AutoScrollMargin {
get { return base.AutoScrollMargin; }
set { base.AutoScrollMargin = value; }
}
[
Browsable(false),
EditorBrowsable(EditorBrowsableState.Never), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden),
]
public new Size AutoScrollMinSize {
get { return base.AutoScrollMinSize; }
set { base.AutoScrollMinSize = value; }
}
/// <include file='doc\ToolStripPanel.uex' path='docs/doc[@for="ToolStripPanel.AutoSize"]/*' />
[
DefaultValue(true),
DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)
]
public override bool AutoSize {
get {
return base.AutoSize;
}
set {
base.AutoSize = value;
}
}
/// <include file='doc\ToolStripPanel.uex' path='docs\doc[@for="ToolStripPanel.AutoSizeChanged"]*' />
/// Override base AutoSizeChanged to we can change visibility/browsability attributes
[
Browsable(true),
EditorBrowsable(EditorBrowsableState.Always)
]
public new event EventHandler AutoSizeChanged {
add {
base.AutoSizeChanged += value;
}
remove {
base.AutoSizeChanged -= value;
}
}
/// <include file='doc\ToolStripPanel.uex' path='docs/doc[@for="ToolStripPanel.DefaultPadding"]/*' />
protected override Padding DefaultPadding {
get { return Padding.Empty; }
}
protected override Padding DefaultMargin {
get { return Padding.Empty; }
}
public Padding RowMargin {
get { return scaledRowMargin; }
set {
scaledRowMargin = value;
LayoutTransaction.DoLayout(this, this, "RowMargin");
}
}
/// <include file='doc\ToolStripPanel.uex' path='docs/doc[@for="ToolStripPanel.Dock"]/*' />
public override DockStyle Dock {
get {
return base.Dock;
}
set {
base.Dock = value;
if (value == DockStyle.Left || value == DockStyle.Right) {
Orientation = Orientation.Vertical;
}
else {
Orientation = Orientation.Horizontal;
}
}
}
internal Rectangle DragBounds {
get {
return LayoutUtils.InflateRect(this.ClientRectangle, DragMargin);
}
}
internal bool IsInDesignMode {
get {
return DesignMode;
}
}
/// <include file='doc\ToolStripPanel.uex' path='docs/doc[@for="ToolStripPanel.LayoutEngine"]/*' />
public override LayoutEngine LayoutEngine {
get {
return FlowLayout.Instance;
}
}
/// <include file='doc\ToolStripPanel.uex' path='docs/doc[@for="ToolStripPanel.Locked"]/*' />
[
DefaultValue(false),
Browsable(false),
EditorBrowsable(EditorBrowsableState.Advanced)
]
public bool Locked {
get {
return state[stateLocked];
}
set {
state[stateLocked] = value;
}
}
/// <include file='doc\ToolStripPanel.uex' path='docs/doc[@for="ToolStripPanel.Orientation"]/*' />
public Orientation Orientation {
get {
return orientation;
}
set {
if (orientation != value) {
orientation = value;
scaledRowMargin = LayoutUtils.FlipPadding(scaledRowMargin);
InitFlowLayout();
foreach (ToolStripPanelRow row in this.RowsInternal) {
row.OnOrientationChanged();
}
}
}
}
private ToolStripRendererSwitcher RendererSwitcher {
get {
if (rendererSwitcher == null) {
rendererSwitcher = new ToolStripRendererSwitcher(this);
HandleRendererChanged(this, EventArgs.Empty);
rendererSwitcher.RendererChanged += new EventHandler(HandleRendererChanged);
}
return rendererSwitcher;
}
}
[Browsable(false)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
// PM team has reviewed and decided on naming changes already
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
public ToolStripRenderer Renderer {
get {
return RendererSwitcher.Renderer;
}
set {
RendererSwitcher.Renderer = value;
}
}
/// <include file='doc\ToolStrip.uex' path='docs/doc[@for="ToolStrip.DrawMode"]/*' />
[
SRDescription(SR.ToolStripRenderModeDescr),
SRCategory(SR.CatAppearance),
]
public ToolStripRenderMode RenderMode {
get {
return RendererSwitcher.RenderMode;
}
set {
RendererSwitcher.RenderMode = value;
}
}
[SRCategory(SR.CatAppearance), SRDescription(SR.ToolStripRendererChanged)]
// PM team has reviewed and decided on naming changes already
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
public event EventHandler RendererChanged {
add {
Events.AddHandler(EventRendererChanged, value);
}
remove {
Events.RemoveHandler(EventRendererChanged, value);
}
}
/// <include file='doc\ToolStripPanel.uex' path='docs/doc[@for="ToolStripPanel.Rows"]/*' />
/// <devdoc>
/// Collection of child controls.
/// </devdoc>
[
Browsable(false),
DesignerSerializationVisibility(DesignerSerializationVisibility.Content),
SRDescription(SR.ToolStripPanelRowsDescr)
]
internal ToolStripPanelRowCollection RowsInternal {
get {
ToolStripPanelRowCollection rowCollection = (ToolStripPanelRowCollection)Properties.GetObject(PropToolStripPanelRowCollection);
if (rowCollection == null) {
rowCollection = CreateToolStripPanelRowCollection();
Properties.SetObject(PropToolStripPanelRowCollection, rowCollection);
}
return rowCollection;
}
}
[
Browsable(false),
DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden),
SRDescription(SR.ToolStripPanelRowsDescr),
SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")
]
public ToolStripPanelRow[] Rows {
get {
ToolStripPanelRow[] rows = new ToolStripPanelRow[RowsInternal.Count];
RowsInternal.CopyTo(rows,0);
return rows;
}
}
/// <include file='doc\ToolStripPanel.uex' path='docs/doc[@for="ToolStripPanel.TabIndex"]/*' />
[
Browsable(false),
EditorBrowsable(EditorBrowsableState.Never),
DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)
]
public new int TabIndex {
get {
return base.TabIndex;
}
set {
base.TabIndex = value;
}
}
/// <include file='doc\ToolStripPanel.uex' path='docs/doc[@for="ToolStripPanel.TabIndexChanged"]/*' />
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
public new event EventHandler TabIndexChanged {
add {
base.TabIndexChanged += value;
}
remove {
base.TabIndexChanged -= value;
}
}
/// <include file='doc\ToolStripPanel.uex' path='docs/doc[@for="ToolStripPanel.TabIndex"]/*' />
[
DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden),
Browsable(false),
EditorBrowsable(EditorBrowsableState.Never)
]
public new bool TabStop {
get {
return base.TabStop;
}
set {
if (AccessibilityImprovements.Level2) {
SetStyle(ControlStyles.Selectable, value);
}
base.TabStop = value;
}
}
/// <include file='doc\ToolStripPanel.uex' path='docs/doc[@for="ToolStripPanel.TabStopChanged"]/*' />
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
public new event EventHandler TabStopChanged {
add {
base.TabStopChanged += value;
}
remove {
base.TabStopChanged -= value;
}
}
/// <include file='doc\ToolStripPanel.uex' path='docs/doc[@for="ToolStripPanel.Text"]/*' />
[
Browsable(false),
EditorBrowsable(EditorBrowsableState.Never),
DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)
]
public override string Text {
get {
return base.Text;
}
set {
base.Text = value;
}
}
/// <include file='doc\ToolStripPanel.uex' path='docs/doc[@for="ToolStripPanel.TextChanged"]/*' />
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
public new event EventHandler TextChanged {
add {
base.TextChanged += value;
}
remove {
base.TextChanged -= value;
}
}
#region ISupportInitialize
public void BeginInit() {
state[stateBeginInit] = true;
}
public void EndInit() {
state[stateBeginInit] = false;
state[stateEndInit] = true;
try {
if (!state[stateInJoin]) {
JoinControls();
}
}
finally {
state[stateEndInit] = false;
}
}
#endregion ISupportInitialize
/// <include file='doc\ToolStripPanel.uex' path='docs/doc[@for="ToolStripPanel.CreateToolStripPanelRowCollection"]/*' />
private ToolStripPanelRowCollection CreateToolStripPanelRowCollection() {
return new ToolStripPanelRowCollection(this);
}
protected override Control.ControlCollection CreateControlsInstance() {
return new ToolStripPanelControlCollection(this);
}
/// <include file='doc\ToolStripPanel.uex' path='docs/doc[@for="ToolStripPanel.Dispose"]/*' />
/// <devdoc>
/// <para>Disposes of the resources (other than memory) used by
/// the <see cref='System.Windows.Forms.ContainerControl'/>
/// .</para>
/// </devdoc>
protected override void Dispose(bool disposing) {
if (disposing) {
ToolStripManager.ToolStripPanels.Remove(this);
}
base.Dispose(disposing);
}
private void InitFlowLayout() {
if (Orientation == Orientation.Horizontal) {
FlowLayout.SetFlowDirection(this, FlowDirection.TopDown);
}
else {
FlowLayout.SetFlowDirection(this, FlowDirection.LeftToRight);
}
FlowLayout.SetWrapContents(this, false);
}
private Point GetStartLocation(ToolStrip toolStripToDrag) {
if (toolStripToDrag.IsCurrentlyDragging
&& Orientation == Orientation.Horizontal
&& toolStripToDrag.RightToLeft == RightToLeft.Yes ) {
// the grip is on the right side, not left.
return new Point(toolStripToDrag.Right,toolStripToDrag.Top);
}
return toolStripToDrag.Location;
}
private void HandleRendererChanged(object sender, EventArgs e) {
OnRendererChanged(e);
}
#if DEBUG_PAINT
/// <include file='doc\ToolStripPanel.uex' path='docs/doc[@for="ToolStripPanel.OnPaint"]/*' />
protected override void OnPaint(PaintEventArgs e) {
base.OnPaint(e);
Graphics g = e.Graphics;
foreach (ToolStripPanelRow row in this.RowsInternal) {
g.DrawRectangle(SystemPens.Highlight, row.Bounds);
row.PaintColumns(e);
}
}
#endif
[EditorBrowsable(EditorBrowsableState.Advanced)]
protected override void OnPaintBackground(PaintEventArgs e) {
ToolStripPanelRenderEventArgs rea = new ToolStripPanelRenderEventArgs(e.Graphics, this);
Renderer.DrawToolStripPanelBackground(rea);
if (!rea.Handled) {
base.OnPaintBackground(e);
}
}
protected override void OnControlAdded(ControlEventArgs e) {
base.OnControlAdded(e);
if (!state[stateBeginInit] && !state[stateInJoin]) {
if (!state[stateLayoutSuspended]) {
this.Join(e.Control as ToolStrip, e.Control.Location);
}
else {
BeginInit();
}
}
}
protected override void OnControlRemoved(ControlEventArgs e) {
ISupportToolStripPanel controlToBeDragged = e.Control as ISupportToolStripPanel;
if (controlToBeDragged != null) {
if (controlToBeDragged.ToolStripPanelRow != null/* && controlToBeDragged.ToolStripPanelRow != owner*/) {
controlToBeDragged.ToolStripPanelRow.ControlsInternal.Remove(e.Control);
}
}
base.OnControlRemoved(e);
}
/// <include file='doc\ToolStrip.uex' path='docs/doc[@for="ToolStrip.OnLayout"]/*' />
protected override void OnLayout(LayoutEventArgs e) {
if (e.AffectedComponent != ParentInternal && e.AffectedComponent as Control != null) {
ISupportToolStripPanel draggedControl = e.AffectedComponent as ISupportToolStripPanel;
if (draggedControl != null && this.RowsInternal.Contains(draggedControl.ToolStripPanelRow)) {
// there's a problem in the base onlayout... if toolstrip needs more space it talks to us
// not the row that needs layout.
LayoutTransaction.DoLayout(draggedControl.ToolStripPanelRow, e.AffectedComponent as IArrangedElement, e.AffectedProperty);
}
}
base.OnLayout(e);
}
internal override void OnLayoutSuspended() {
base.OnLayoutSuspended();
state[stateLayoutSuspended] = true;
}
internal override void OnLayoutResuming(bool resumeLayout) {
base.OnLayoutResuming(resumeLayout);
state[stateLayoutSuspended] = false;
if (state[stateBeginInit]) {
EndInit();
}
}
protected override void OnRightToLeftChanged(EventArgs e) {
base.OnRightToLeftChanged(e);
if (!state[stateBeginInit]) {
if (Controls.Count > 0) {
// rejoin the controls on the other side of the toolstrippanel.
SuspendLayout();
Control[] controls = new Control[this.Controls.Count];
Point[] controlLocations = new Point[this.Controls.Count];
int j = 0;
foreach (ToolStripPanelRow row in this.RowsInternal) {
foreach (Control control in row.ControlsInternal) {
controls[j] = control;
controlLocations[j] = new Point(row.Bounds.Width - control.Right, control.Top);
j++;
}
}
this.Controls.Clear();
for ( int i = 0; i < controls.Length; i++) {
Join(controls[i] as ToolStrip, controlLocations[i]);
}
ResumeLayout(true);
}
}
else {
state[stateRightToLeftChanged] = true;
}
}
/// <include file='doc\ToolStrip.uex' path='docs/doc[@for="ToolStrip.OnDefaultRendererChanged"]/*' />
// PM team has reviewed and decided on naming changes already
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
protected virtual void OnRendererChanged(EventArgs e) {
Renderer.InitializePanel(this);
this.Invalidate();
EventHandler handler = (EventHandler)Events[EventRendererChanged];
if (handler != null) handler(this, e);
}
/// <include file='doc\ToolStripPanel.uex' path='docs/doc[@for="ToolStripPanel.OnParentChanged"]/*' />
/// <devdoc>
/// We want to Set ToolStripPanel at DesignTime when the ToolStripPanel is added to the Form,
/// </devdoc>
protected override void OnParentChanged(EventArgs e) {
PerformUpdate();
base.OnParentChanged(e);
}
protected override void OnDockChanged(EventArgs e) {
PerformUpdate();
base.OnDockChanged(e);
}
internal void PerformUpdate() {
PerformUpdate(false);
}
internal void PerformUpdate(bool forceLayout) {
if (!state[stateBeginInit] && !state[stateInJoin]) {
JoinControls(forceLayout);
}
}
private void ResetRenderMode() {
RendererSwitcher.ResetRenderMode();
}
private bool ShouldSerializeRenderMode() {
return RendererSwitcher.ShouldSerializeRenderMode();
}
private bool ShouldSerializeDock() {
return (owner == null && (Dock != DockStyle.None));
}
private void JoinControls() {
JoinControls(false);
}
private void JoinControls(bool forceLayout) {
// undone: config - shift to other container
ToolStripPanelControlCollection controls = this.Controls as ToolStripPanelControlCollection;
if (controls.Count > 0) {
controls.Sort();
// since Join is going to mess with our order - make a copy. (ugh).
Control[] controlArray = new Control[controls.Count];
controls.CopyTo(controlArray, 0);
for (int i = 0; i < controlArray.Length; i++) {
int numRows = RowsInternal.Count;
ISupportToolStripPanel draggedControl = controlArray[i] as ISupportToolStripPanel;
if (draggedControl != null && draggedControl.ToolStripPanelRow != null && !draggedControl.IsCurrentlyDragging) {
ToolStripPanelRow row = draggedControl.ToolStripPanelRow;
if (row.Bounds.Contains(controlArray[i].Location)) {
// this toolstrip does not require join.
continue;
}
}
if (controlArray[i].AutoSize) {
controlArray[i].Size = controlArray[i].PreferredSize;
}
Point controlLocation = controlArray[i].Location;
// right to left has changed while layout was deferred...
if (state[stateRightToLeftChanged]) {
controlLocation = new Point(this.Width - controlArray[i].Right, controlLocation.Y);
}
this.Join(controlArray[i] as ToolStrip, controlArray[i].Location);
if (numRows < RowsInternal.Count || forceLayout) {
// OK this is wierd but here we're in the midst of a suspend layout.
// the only way we can deterministically place these guys is to force a layout
// each time we've added a new row. Otherwise we wont find the correct
// row to add the control to (PointToRow will fail as Row.Bounds isnt set yet)
OnLayout(new LayoutEventArgs(this, PropertyNames.Rows));
}
}
}
state[stateRightToLeftChanged] = false;
}
#region Feedback
[ThreadStatic]
private static FeedbackRectangle feedbackRect;
private void GiveToolStripPanelFeedback(ToolStrip toolStripToDrag, Point screenLocation) {
if (Orientation == Orientation.Horizontal && RightToLeft == RightToLeft.Yes) {
// paint the feedback in the correct location when RTL.Yes
screenLocation.Offset(-toolStripToDrag.Width,0);
}
if (CurrentFeedbackRect == null) {
Debug.WriteLineIf(ToolStripPanelFeedbackDebug.TraceVerbose, "FEEDBACK: creating NEW feedback at " + screenLocation.ToString());
CurrentFeedbackRect = new FeedbackRectangle(toolStripToDrag.ClientRectangle);
}
if (!CurrentFeedbackRect.Visible) {
Debug.WriteLineIf(ToolStripPanelFeedbackDebug.TraceVerbose, "FEEDBACK: Showing NEW feedback at " + screenLocation.ToString());
toolStripToDrag.SuspendCaputureMode();
try {
CurrentFeedbackRect.Show(screenLocation);
toolStripToDrag.CaptureInternal = true;
}
finally {
toolStripToDrag.ResumeCaputureMode();
}
}
else {
Debug.WriteLineIf(ToolStripPanelFeedbackDebug.TraceVerbose, "FEEDBACK: Moving feedback to " + screenLocation.ToString());
CurrentFeedbackRect.Move(screenLocation);
}
}
internal static void ClearDragFeedback() {
#if DEBUG
if (ToolStripPanelFeedbackDebug.TraceVerbose){
Debug.WriteLine("FEEDBACK: clearing old feedback at "/*+ new StackTrace().ToString()*/);
}
#endif
FeedbackRectangle oldFeedback = feedbackRect;
feedbackRect = null;
if (oldFeedback != null) {
oldFeedback.Dispose();
}
}
private static FeedbackRectangle CurrentFeedbackRect {
get {
return feedbackRect;
}
set {
feedbackRect = value;
}
}
// The FeedbackRectangle happens to encapsulate a toolstripdropdown
// with a special region. The feedback rectangle exposes the minimum
// API so the underlying implementation can be replaced if necessary.
private class FeedbackRectangle : IDisposable {
private FeedbackDropDown dropDown;
public FeedbackRectangle(Rectangle bounds) {
dropDown = new FeedbackDropDown(bounds);
}
public bool Visible {
get {
if (dropDown != null && !dropDown.IsDisposed) {
return dropDown.Visible;
}
return false;
}
set {
if (dropDown != null && !dropDown.IsDisposed) {
dropDown.Visible = value;
}
}
}
public void Show(Point newLocation) {
dropDown.Show(newLocation);
}
public void Move(Point newLocation) {
dropDown.MoveTo(newLocation);
}
protected void Dispose(bool disposing) {
if (disposing) {
if (dropDown != null) {
Visible = false;
dropDown.Dispose();
dropDown = null;
}
}
}
public void Dispose() {
Dispose(true);
}
~FeedbackRectangle() {
Dispose(false);
}
private class FeedbackDropDown : ToolStripDropDown {
private const int MAX_PAINTS_TO_SERVICE = 20;
private int _numPaintsServiced = 0; // member variable to protect against re-entrancy
public FeedbackDropDown(Rectangle bounds) : base(){
SetStyle(ControlStyles.AllPaintingInWmPaint, false);
SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
SetStyle(ControlStyles.CacheText, true);
this.AutoClose = false;
this.AutoSize = false;
this.DropShadowEnabled = false;
this.Bounds = bounds;
// caching as this is unlikely to change during the lifetime
// of the dropdown.
Rectangle regionRect = bounds; //create a region the size of the client area
regionRect.Inflate(-1,-1); //squish down by one pixel
Region rgn = new Region(bounds); // create region
rgn.Exclude(regionRect); // exclude the center part
// set it into the toolstripdropdown’s region
IntSecurity.ChangeWindowRegionForTopLevel.Assert();
this.Region = rgn;
// RevertAssert automatically called here when exiting function.
}
// ForceSynchronousPaint - peeks through the message queue, looking for WM_PAINTs
// calls UpdateWindow on the hwnd to force the paint to happen now.
//
// When we're changing the location of the feedback dropdown, we need to
// force WM_PAINTS to happen, as things that dont respond to WM_ERASEBKGND
// have bits of the dropdown region drawn all over them.
private void ForceSynchronousPaint() {
if (!IsDisposed) {
if (_numPaintsServiced == 0) { // protect against re-entrancy.
try {
NativeMethods.MSG msg = new NativeMethods.MSG();
while (UnsafeNativeMethods.PeekMessage(ref msg, new HandleRef(this, IntPtr.Zero),
NativeMethods.WM_PAINT, NativeMethods.WM_PAINT,
NativeMethods.PM_REMOVE)) {
SafeNativeMethods.UpdateWindow(new HandleRef(null, msg.hwnd));
// Infinite loop protection
if (_numPaintsServiced++ > MAX_PAINTS_TO_SERVICE) {
Debug.Fail("somehow we've gotten ourself in a situation where we're pumping an unreasonable number of paint messages, investigate.");
break;
}
}
}
finally {
_numPaintsServiced = 0;
}
}
}
}
protected override void OnPaint(PaintEventArgs e) {
}
protected override void OnPaintBackground(PaintEventArgs e) {
// respond to everything in WM_ERASEBKGND
Renderer.DrawToolStripBackground(new ToolStripRenderEventArgs(e.Graphics, this));
Renderer.DrawToolStripBorder(new ToolStripRenderEventArgs(e.Graphics, this));
}
protected override void OnOpening(CancelEventArgs e) {
base.OnOpening(e);
e.Cancel = false;
}
public void MoveTo(Point newLocation) {
this.Location = newLocation;
// if we dont force a paint here, we'll only send WM_ERASEBKGNDs right away
// and leave rectangles all over controls that dont respond to that window message.
ForceSynchronousPaint();
}
[SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)]
protected override void WndProc(ref Message m){
if (m.Msg == NativeMethods.WM_NCHITTEST){
m.Result = (IntPtr)NativeMethods.HTTRANSPARENT;
}
base.WndProc(ref m);
}
}
}
#endregion Feedback
#region JoinAndMove
public void Join(ToolStrip toolStripToDrag) {
Join(toolStripToDrag, Point.Empty);
}
public void Join(ToolStrip toolStripToDrag, int row) {
if (row < 0) {
throw new ArgumentOutOfRangeException("row", SR.GetString(SR.IndexOutOfRange, row.ToString(CultureInfo.CurrentCulture)));
}
Point location = Point.Empty;
Rectangle dragRect = Rectangle.Empty;
if (row >= RowsInternal.Count) {
dragRect = DragBounds;
}
else {
dragRect = this.RowsInternal[row].DragBounds;
}
if (Orientation == Orientation.Horizontal) {
location = new Point(0, dragRect.Bottom -1);
}
else {
location = new Point(dragRect.Right -1, 0);
}
Join(toolStripToDrag, location);
}
public void Join(ToolStrip toolStripToDrag, int x, int y) {
Join(toolStripToDrag, new Point(x, y));
}
[
SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters") // Using ToolStrip instead of Control intentionally
]
public void Join(ToolStrip toolStripToDrag, Point location) {
if (toolStripToDrag == null) {
throw new ArgumentNullException("toolStripToDrag");
}
if (!state[stateBeginInit] && !state[stateInJoin]) {
try {
state[stateInJoin] = true;
toolStripToDrag.ParentInternal = this;
MoveInsideContainer(toolStripToDrag, location);
}
finally {
state[stateInJoin] = false;
}
}
else {
this.Controls.Add(toolStripToDrag);
toolStripToDrag.Location = location;
}
}
internal void MoveControl(ToolStrip toolStripToDrag, Point screenLocation) {
ISupportToolStripPanel draggedControl = toolStripToDrag as ISupportToolStripPanel;
if (draggedControl == null) {
Debug.Fail("Move called on immovable object.");
return;
}
Point clientLocation = PointToClient(screenLocation);
if (!this.DragBounds.Contains(clientLocation)) {
Debug.WriteLineIf(ToolStripPanelDebug.TraceVerbose, String.Format(CultureInfo.CurrentCulture, "RC.MoveControl - Point {0} is not in current rafting container drag bounds {1}, calling MoveOutsideContainer", clientLocation, DragBounds));
MoveOutsideContainer(toolStripToDrag, screenLocation);
return;
}
else {
Join(toolStripToDrag as ToolStrip, clientLocation);
}
}
private void MoveInsideContainer(ToolStrip toolStripToDrag, Point clientLocation) {
ISupportToolStripPanel draggedControl = toolStripToDrag as ISupportToolStripPanel;
// if the point is not in this rafting container forward on to the appropriate container.
if (draggedControl.IsCurrentlyDragging && !this.DragBounds.Contains(clientLocation)) {
return;
}
// we know we're moving inside the container.
bool changedRow = false;
ClearDragFeedback();
// VSWhidbey 439542 - in design mode we get bogus values for client location.
if (toolStripToDrag.Site != null && toolStripToDrag.Site.DesignMode && IsHandleCreated) {
if (clientLocation.X < 0 || clientLocation.Y < 0) {
Point currentCursorLoc = PointToClient(WindowsFormsUtils.LastCursorPoint);
if (ClientRectangle.Contains(currentCursorLoc)) {
clientLocation = currentCursorLoc;
}
}
}
//
// Point INSIDE this rafting container
//
ToolStripPanelRow currentToolStripPanelRow = draggedControl.ToolStripPanelRow;
#if DEBUG
bool debugModeOnly_ChangedContainers = currentToolStripPanelRow != null ?
(currentToolStripPanelRow.ToolStripPanel != this) : true;
#endif
// Dev10 bug 477755 - in design mode toolstrip location jump back after being set.
bool pointInCurrentRow = false;
if (currentToolStripPanelRow != null && currentToolStripPanelRow.Visible && currentToolStripPanelRow.ToolStripPanel == this) {
if (toolStripToDrag.IsCurrentlyDragging) {
// Dragging with mouse, use DragBounds to check
pointInCurrentRow = currentToolStripPanelRow.DragBounds.Contains(clientLocation);
}
else {
// Location is set directly, use normal Bounds to check
pointInCurrentRow = currentToolStripPanelRow.Bounds.Contains(clientLocation);
}
}
if (pointInCurrentRow) {
// Point INSIDE same rafting row
Debug.WriteLineIf(ToolStripPanelDebug.TraceVerbose, "RC.MoveControl - Point " + clientLocation + "is in the same row as the control" + draggedControl.ToolStripPanelRow.DragBounds);
draggedControl.ToolStripPanelRow.MoveControl(toolStripToDrag, GetStartLocation(toolStripToDrag), clientLocation);
}
else {
// Point OUTSIDE current rafting row.
Debug.WriteLineIf(ToolStripPanelDebug.TraceVerbose, "RC.MoveControl - Point " + clientLocation + " is outside the current rafting row.");
ToolStripPanelRow row = PointToRow(clientLocation);
if (row == null) {
Debug.WriteLineIf(ToolStripPanelDebug.TraceVerbose, String.Format(CultureInfo.CurrentCulture, "\tThere is no row corresponding to this point, creating a new one."));
// there's no row at this point so lets create one
int index = this.RowsInternal.Count;
if (Orientation == Orientation.Horizontal) {
// if it's above the first row, insert at the front.
index = (clientLocation.Y <= this.Padding.Left) ? 0 : index;
}
else { // Orientation.Vertical
// if it's before the first row, insert at the front.
index = (clientLocation.X <= this.Padding.Left) ? 0 : index;
}
ToolStripPanelRow previousRow = null;
if (this.RowsInternal.Count > 0) {
if (index == 0) {
previousRow = this.RowsInternal[0];
}
else if (index > 0) {
previousRow = this.RowsInternal[index - 1];
}
}
if (previousRow != null /* there was a previous row */
&& previousRow.ControlsInternal.Count == 1 /*toolStripToDrag*/
&& previousRow.ControlsInternal.Contains(toolStripToDrag)) {
// if the previous row already contains this control
// it's futile to create a new row, we're just going to wind
// up disposing this one and causing great amounts of flicker.
row = previousRow;
Debug.WriteLineIf(ToolStripPanelRow.ToolStripPanelRowCreationDebug.TraceVerbose, "Reusing previous row");
// Move the ToolStrip to the new Location in the existing row.
if (toolStripToDrag.IsInDesignMode) {
Point endLocation = (Orientation == Orientation.Horizontal) ? new Point (clientLocation.X, row.Bounds.Y) : new Point (row.Bounds.X, clientLocation.Y);
draggedControl.ToolStripPanelRow.MoveControl(toolStripToDrag, GetStartLocation(toolStripToDrag), endLocation);
}
}
else {
// Create a new row and insert it.
//
Debug.WriteLineIf(ToolStripPanelRow.ToolStripPanelRowCreationDebug.TraceVerbose, "Inserting a new row at " + index.ToString(CultureInfo.InvariantCulture));
row = new ToolStripPanelRow(this);
this.RowsInternal.Insert(index, row);
}
}
else if (!row.CanMove(toolStripToDrag)) {
Debug.WriteLineIf(ToolStripPanelRow.ToolStripPanelRowCreationDebug.TraceVerbose, String.Format(CultureInfo.CurrentCulture, "\tThere was a row, but we cant add the control to it, creating/inserting new row."));
// we have a row at that point, but its too full or doesnt want
// anyone to join it.
int index = RowsInternal.IndexOf(row);
if (currentToolStripPanelRow != null && currentToolStripPanelRow.ControlsInternal.Count == 1) {
if (index > 0 && index-1 == RowsInternal.IndexOf(currentToolStripPanelRow)) {
Debug.WriteLineIf(ToolStripPanelRow.ToolStripPanelRowCreationDebug.TraceVerbose, String.Format(CultureInfo.CurrentCulture, "\tAttempts to leave the current row failed as there's no space in the next row. Since there's only one control, just keep the row."));
return;
}
}
row = new ToolStripPanelRow(this);
this.RowsInternal.Insert(index, row);
clientLocation.Y = row.Bounds.Y;
}
changedRow = (currentToolStripPanelRow != row);
if (!changedRow) {
if (currentToolStripPanelRow != null && currentToolStripPanelRow.ControlsInternal.Count >1) {
// force a leave/re-enter to occur.
currentToolStripPanelRow.LeaveRow(toolStripToDrag);
currentToolStripPanelRow = null;
changedRow = true;
}
}
if (changedRow) {
Debug.WriteLineIf(ToolStripPanelDebug.TraceVerbose, String.Format(CultureInfo.CurrentCulture, "\tCalling JoinRow."));
if (currentToolStripPanelRow != null) {
currentToolStripPanelRow.LeaveRow(toolStripToDrag);
}
row.JoinRow(toolStripToDrag, clientLocation);
}
if (changedRow && draggedControl.IsCurrentlyDragging) {
// force the layout of the new row.
for (int i = 0; i < RowsInternal.Count; i++) {
LayoutTransaction.DoLayout(RowsInternal[i], this, PropertyNames.Rows);
}
if (RowsInternal.IndexOf(row) > 0) {
// VSWhidbey 327225 - when joining a new row, move the cursor to to the location of
// the grip, otherwise budging the mouse can pull it down into the next row.
IntSecurity.AdjustCursorPosition.Assert();
try {
Point cursorLoc = toolStripToDrag.PointToScreen(toolStripToDrag.GripRectangle.Location);
if (Orientation == Orientation.Vertical) {
cursorLoc.X += toolStripToDrag.GripRectangle.Width /2;
cursorLoc.Y = Cursor.Position.Y;
}
else {
cursorLoc.Y += toolStripToDrag.GripRectangle.Height /2;
cursorLoc.X = Cursor.Position.X;
}
Cursor.Position = cursorLoc;
}
finally {
CodeAccessPermission.RevertAssert();
}
}
}
}
#if DEBUG
Debug_VerifyOneToOneCellRowControlMatchup();
// Debug_VerifyCountRows();
if (draggedControl.IsCurrentlyDragging && changedRow && !debugModeOnly_ChangedContainers) {
// if we have changed containers, we're in a SuspendLayout.
Debug_VerifyNoOverlaps();
}
#endif
}
private void MoveOutsideContainer(ToolStrip toolStripToDrag, Point screenLocation) {
// look for another rafting container.
ToolStripPanel panel = ToolStripManager.ToolStripPanelFromPoint(toolStripToDrag, screenLocation);
if (panel != null) {
using (new LayoutTransaction(panel, panel, null)) {
panel.MoveControl(toolStripToDrag, screenLocation);
}
toolStripToDrag.PerformLayout();
#if DEBUG
ISupportToolStripPanel draggedControl = toolStripToDrag as ISupportToolStripPanel;
if (draggedControl.IsCurrentlyDragging) {
Debug_VerifyNoOverlaps();
}
#endif
}
else {
GiveToolStripPanelFeedback(toolStripToDrag, screenLocation);
}
}
/// <include file='doc\ToolStripPanel.uex' path='docs/doc[@for="ToolStripPanel.PointToRow"]/*' />
/// <devdoc>
/// Given a point within the ToolStripPanel client area -
/// it returns the row. If no such row exists, returns null
/// </devdoc>
public ToolStripPanelRow PointToRow(Point clientLocation) {
// PERF: since we're using the PropertyStore for this.RowsInternal, its actually
// faster to use foreach.
foreach (ToolStripPanelRow row in this.RowsInternal) {
Rectangle bounds = LayoutUtils.InflateRect(row.Bounds, row.Margin);
// at this point we may not be sized correctly. Guess.
if (ParentInternal != null) {
if (Orientation == Orientation.Horizontal && (bounds.Width == 0)) {
bounds.Width = ParentInternal.DisplayRectangle.Width;
}
else if (Orientation == Orientation.Vertical && (bounds.Height == 0)) {
bounds.Height = ParentInternal.DisplayRectangle.Height;
}
}
if (bounds.Contains(clientLocation)) {
return row;
}
}
return null;
}
#endregion JoinAndMove
[Conditional("DEBUG")]
private void Debug_VerifyOneToOneCellRowControlMatchup() {
for (int i = 0; i < this.RowsInternal.Count; i++) {
ToolStripPanelRow row = this.RowsInternal[i];
foreach (ToolStripPanelCell cell in row.Cells) {
if (cell.Control != null) {
ToolStripPanelRow currentlyAssignedRow = ((ISupportToolStripPanel)cell.Control).ToolStripPanelRow;
if (currentlyAssignedRow != row) {
int goodRowIndex = (currentlyAssignedRow != null) ? RowsInternal.IndexOf(currentlyAssignedRow) : -1;
if (goodRowIndex == -1) {
Debug.Fail(String.Format(CultureInfo.CurrentCulture, "ToolStripPanelRow has not been assigned! Should be set to {0}.", i));
}
else {
Debug.Fail(String.Format(CultureInfo.CurrentCulture, "Detected orphan cell! {0} is in row {1}. It shouldnt have a cell in {2}! \r\n\r\nTurn on DEBUG_PAINT in ToolStripPanel and ToolStripPanelRow to investigate.", cell.Control.Name, goodRowIndex, i));
}
}
}
else {
Debug.Fail("why do we have a cell with a null control in this row?");
}
}
}
}
[Conditional("DEBUG")]
private void Debug_PrintRows() {
for (int i = 0; i < RowsInternal.Count; i++) {
Debug.Write("Row " + i.ToString(CultureInfo.CurrentCulture) + ": ");
for (int j = 0; j < RowsInternal[i].ControlsInternal.Count; j++) {
Debug.Write(String.Format(CultureInfo.CurrentCulture, "[{0} {1}] ", RowsInternal[i].ControlsInternal[j].Name, ((ToolStripPanelCell)RowsInternal[i].Cells[j]).Margin));
}
Debug.Write("\r\n");
}
}
[Conditional("DEBUG")]
private void Debug_VerifyCountRows() {
Debug.Assert(RowsInternal.Count <= Controls.Count, "How did the number of rows get larger than the number of controls?");
}
[Conditional("DEBUG")]
private void Debug_VerifyNoOverlaps() {
foreach (Control c1 in this.Controls) {
foreach (Control c2 in this.Controls) {
if (c1 == c2) {
continue;
}
Rectangle intersection = c1.Bounds;
intersection.Intersect(c2.Bounds);
if (!LayoutUtils.IsZeroWidthOrHeight(intersection)) {
ISupportToolStripPanel draggedToolStrip1 = c1 as ISupportToolStripPanel;
ISupportToolStripPanel draggedToolStrip2 = c2 as ISupportToolStripPanel;
string fail = String.Format(CultureInfo.CurrentCulture,
"OVERLAP detection:\r\n{0}: {1} row {2} row bounds {3}",
c1.Name == null ? "" : c1.Name,
c1.Bounds,
!RowsInternal.Contains(draggedToolStrip1.ToolStripPanelRow) ? "unknown" : RowsInternal.IndexOf(draggedToolStrip1.ToolStripPanelRow).ToString(CultureInfo.CurrentCulture),
draggedToolStrip1.ToolStripPanelRow.Bounds);
fail += String.Format(CultureInfo.CurrentCulture,
"\r\n{0}: {1} row {2} row bounds {3}",
c2.Name == null ? "" : c2.Name,
c2.Bounds,
!RowsInternal.Contains(draggedToolStrip2.ToolStripPanelRow) ? "unknown" : RowsInternal.IndexOf(draggedToolStrip2.ToolStripPanelRow).ToString(CultureInfo.CurrentCulture),
draggedToolStrip2.ToolStripPanelRow.Bounds);
Debug.Fail(fail);
}
}
}
}
/// <include file='doc\ToolStripPanel.uex' path='docs/doc[@for="ToolStripPanel.IArrangedElement.Children"]/*' />
/// <internalonly/>
ArrangedElementCollection IArrangedElement.Children {
get {
return RowsInternal;
}
}
/// <include file='doc\ToolStripPanel.uex' path='docs/doc[@for="ToolStripPanelRowCollection"]/*' />
[
ListBindable(false),
ComVisible(false)
]
[SuppressMessage("Microsoft.Design", "CA1034:NestedTypesShouldNotBeVisible")]
public class ToolStripPanelRowCollection : ArrangedElementCollection, IList {
private ToolStripPanel owner;
/// <include file='doc\ToolStripPanel.uex' path='docs/doc[@for="ToolStripPanelRowCollection.ToolStripPanelRowCollection"]/*' />
public ToolStripPanelRowCollection(ToolStripPanel owner) {
this.owner = owner;
}
/// <include file='doc\ToolStripPanel.uex' path='docs/doc[@for="ToolStripPanelRowCollection.ToolStripPanelRowCollection1"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
public ToolStripPanelRowCollection(ToolStripPanel owner, ToolStripPanelRow[] value) {
this.owner = owner;
AddRange(value);
}
/// <include file='doc\ToolStripPanel.uex' path='docs/doc[@for="ToolStripPanelRowCollection.this"]/*' />
/// <devdoc>
/// <para></para>
/// </devdoc>
public new virtual ToolStripPanelRow this[int index] {
get {
return (ToolStripPanelRow)(InnerList[index]);
}
}
/// <include file='doc\ToolStripPanel.uex' path='docs/doc[@for="ToolStripPanelRowCollection.Add"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
public int Add(ToolStripPanelRow value) {
if (value == null) {
throw new ArgumentNullException("value");
}
int retVal = InnerList.Add(value);
OnAdd(value, retVal);
return retVal;
}
/// <include file='doc\ToolStripPanel.uex' path='docs/doc[@for="ToolStripPanelRowCollection.AddRange"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
public void AddRange(ToolStripPanelRow[] value) {
if (value == null) {
throw new ArgumentNullException("value");
}
ToolStripPanel currentOwner = this.owner;
if (currentOwner != null) {
currentOwner.SuspendLayout();
}
try {
for (int i = 0; i < value.Length; i++) {
this.Add(value[i]);
}
}
finally {
if (currentOwner != null) {
currentOwner.ResumeLayout();
}
}
}
/// <include file='doc\ToolStripPanel.uex' path='docs/doc[@for="ToolStripPanelRowCollection.AddRange1"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
public void AddRange(ToolStripPanelRowCollection value) {
if (value == null) {
throw new ArgumentNullException("value");
}
ToolStripPanel currentOwner = this.owner;
if (currentOwner != null) {
currentOwner.SuspendLayout();
}
try {
int currentCount = value.Count;
for (int i = 0; i < currentCount; i++) {
this.Add(value[i]);
}
}
finally {
if (currentOwner != null) {
currentOwner.ResumeLayout();
}
}
}
/// <include file='doc\ToolStripPanel.uex' path='docs/doc[@for="ToolStripPanelRowCollection.Contains"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
public bool Contains(ToolStripPanelRow value) {
return InnerList.Contains(value);
}
/// <include file='doc\ToolStripPanel.uex' path='docs/doc[@for="ToolStripPanelRowCollection.Clear"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
public virtual void Clear() {
if (owner != null) {
owner.SuspendLayout();
}
try {
while (Count != 0) {
RemoveAt(Count - 1);
}
}
finally {
if (owner != null) {
owner.ResumeLayout();
}
}
}
void IList.Clear() { Clear(); }
bool IList.IsFixedSize { get { return InnerList.IsFixedSize; } }
bool IList.Contains(object value) { return InnerList.Contains(value); }
bool IList.IsReadOnly { get { return InnerList.IsReadOnly; } }
void IList.RemoveAt(int index) { RemoveAt(index); }
void IList.Remove(object value) { Remove(value as ToolStripPanelRow); }
int IList.Add(object value) { return Add(value as ToolStripPanelRow); }
int IList.IndexOf(object value) { return IndexOf(value as ToolStripPanelRow); }
void IList.Insert(int index, object value) { Insert(index, value as ToolStripPanelRow); }
/// <include file='doc\ToolStripPanel.uex' path='docs/doc[@for="ToolStripPanelRowCollection.IList.this"]/*' />
/// <internalonly/>
object IList.this[int index] {
get { return InnerList[index]; }
set { throw new NotSupportedException(SR.GetString(SR.ToolStripCollectionMustInsertAndRemove)); /* InnerList[index] = value; */ }
}
/// <include file='doc\ToolStripPanel.uex' path='docs/doc[@for="ToolStripPanelRowCollection.IndexOf"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
public int IndexOf(ToolStripPanelRow value) {
return InnerList.IndexOf(value);
}
/// <include file='doc\ToolStripPanel.uex' path='docs/doc[@for="ToolStripPanelRowCollection.Insert"]/*' />
public void Insert(int index, ToolStripPanelRow value) {
if (value == null) {
throw new ArgumentNullException("value");
}
InnerList.Insert(index, value);
OnAdd(value, index);
}
private void OnAdd(ToolStripPanelRow value, int index) {
if (owner != null) {
LayoutTransaction.DoLayout(owner, value, PropertyNames.Parent);
}
}
/// <devdoc>
/// Do proper cleanup of ownership, etc.
/// </devdoc>
private void OnAfterRemove(ToolStripPanelRow row) {
#if DEBUG
if (ToolStripPanelMissingRowDebug.TraceVerbose) {
if (row != null) {
Debug.Write("Removing row: ");
row.Debug_PrintRowID();
Debug.WriteLine(new StackTrace().ToString());
}
}
#endif
}
/// <include file='doc\ToolStripPanel.uex' path='docs/doc[@for="ToolStripPanelRowCollection.Remove"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
public void Remove(ToolStripPanelRow value) {
InnerList.Remove(value);
OnAfterRemove(value);
}
/// <include file='doc\ToolStripPanel.uex' path='docs/doc[@for="ToolStripPanelRowCollection.RemoveAt"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
public void RemoveAt(int index) {
ToolStripPanelRow item = null;
if (index < Count && index >= 0) {
item = (ToolStripPanelRow)(InnerList[index]);
}
InnerList.RemoveAt(index);
OnAfterRemove(item);
}
/// <include file='doc\ToolStripPanel.uex' path='docs/doc[@for="ToolStripPanelRowCollection.CopyTo"]/*' />
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
public void CopyTo(ToolStripPanelRow[] array, int index) {
InnerList.CopyTo(array, index);
}
}
internal class ToolStripPanelControlCollection : WindowsFormsUtils.TypedControlCollection {
private ToolStripPanel owner;
public ToolStripPanelControlCollection(ToolStripPanel owner)
: base(owner, typeof(ToolStrip)) {
this.owner = owner;
}
internal override void AddInternal(Control value) {
if (value != null) {
using (new LayoutTransaction(value, value, PropertyNames.Parent)) {
base.AddInternal(value);
}
}
else {
base.AddInternal(value);
}
}
internal void Sort() {
if (owner.Orientation == Orientation.Horizontal) {
InnerList.Sort(new YXComparer());
}
else {
InnerList.Sort(new XYComparer());
}
}
// sort by X, then Y
public class XYComparer : IComparer {
public XYComparer() { }
public int Compare(object first, object second) {
Control one = first as Control;
Control two = second as Control;
if (one.Bounds.X < two.Bounds.X) {
return -1;
}
if (one.Bounds.X == two.Bounds.X) {
if (one.Bounds.Y < two.Bounds.Y) {
return -1;
}
return 1;
}
return 1;
}
}
// sort by Y, then X
public class YXComparer : IComparer {
public YXComparer() { }
public int Compare(object first, object second) {
Control one = first as Control;
Control two = second as Control;
if (one.Bounds.Y < two.Bounds.Y) {
return -1;
}
if (one.Bounds.Y == two.Bounds.Y) {
if (one.Bounds.X < two.Bounds.X) {
return -1;
}
return 1;
}
return 1;
}
}
}
}
}
|