Практическое руководство. Реализация пользовательского класса, производного от ToolStripRenderer
Обновлен: Ноябрь 2007
Можно настроить внешний вид элемента управления ToolStrip путем реализации класса, производного от ToolStripRenderer. Таким образом, можно добиться гибкости в создании внешнего вида, отличного от внешнего вида, который обеспечивается классами ToolStripProfessionalRenderer и ToolStripSystemRenderer.
Пример
В следующем примере показано, как реализовать пользовательский класс ToolStripRenderer. В этом примере элемент управления GridStrip реализует головоломку со скользящими мозаиками, которая позволяет пользователю перемещать плитки в структуре таблицы для формирования изображения. Пользовательский элемент управления ToolStrip выстраивает свои элементы управления ToolStripButton в структуре сетки. В структуре присутствует одна пустая ячейка, в которую при помощи перетаскивания можно сдвинуть соседнюю мозаику. Мозаики, которые можно сдвигать, выделены.
Класс GridStripRenderer настраивает три характеристики внешнего вида элемента управления GridStrip:
Граница GridStrip
Граница ToolStripButton
Рисунок ToolStripButton
Imports System
Imports System.Collections.Generic
Imports System.Drawing
Imports System.Drawing.Drawing2D
Imports System.Drawing.Imaging
Imports System.Text
Imports System.Windows.Forms
Imports System.Windows.Forms.Layout
' The following class implements a sliding-tile puzzle.
' The GridStrip control is a custom ToolStrip that arranges
' its ToolStripButton controls in a grid layout. There is
' one empty cell, into which the user can slide an adjacent
' tile with a drag-and-drop operation. Tiles that are eligible
' for moving are highlighted.
Public Class GridStrip
Inherits ToolStrip
' The button that is the drag source.
Private dragButton As ToolStripButton = Nothing
' Settings for the ToolStrip control's TableLayoutPanel.
' This provides access to the cell position of each
' ToolStripButton.
Private tableSettings As TableLayoutSettings = Nothing
' The empty cell. ToolStripButton controls that are
' adjacent to this button can be moved to this button's
' cell position.
Private emptyCellButton As ToolStripButton = Nothing
' The dimensions of each tile. A tile is represented
' by a ToolStripButton controls.
Private tileSize As New Size(128, 128)
' The number of rows in the GridStrip control.
Private rows As Integer = 5
' The number of columns in the GridStrip control.
Private columns As Integer = 5
' The one-time initialzation behavior is enforced
' with this field. For more information, see the
' OnPaint method.
Private firstTime As Boolean = False
' This is a required by the Windows Forms designer.
Private components As System.ComponentModel.IContainer
' The default constructor.
Public Sub New()
MyBase.New()
Me.InitializeComponent()
Me.InitializeTableLayoutSettings()
End Sub
' This property exposes the empty cell to the
' GridStripRenderer class.
Friend ReadOnly Property EmptyCell() As ToolStripButton
Get
Return Me.emptyCellButton
End Get
End Property
' This utility method initializes the TableLayoutPanel
' which contains the ToolStripButton controls.
Private Sub InitializeTableLayoutSettings()
' Specify the numbers of rows and columns in the GridStrip control.
Me.tableSettings = CType(MyBase.LayoutSettings, TableLayoutSettings)
Me.tableSettings.ColumnCount = Me.rows
Me.tableSettings.RowCount = Me.columns
' Create a dummy bitmap with the dimensions of each tile.
' The GridStrip control sizes itself based on these dimensions.
Dim b As New Bitmap(tileSize.Width, tileSize.Height)
' Populate the GridStrip control with ToolStripButton controls.
Dim i As Integer
For i = 0 To (Me.tableSettings.ColumnCount) - 1
Dim j As Integer
For j = 0 To (Me.tableSettings.RowCount) - 1
' Create a new ToolStripButton control.
Dim btn As New ToolStripButton()
btn.DisplayStyle = ToolStripItemDisplayStyle.Image
btn.Image = b
btn.ImageAlign = ContentAlignment.MiddleCenter
btn.ImageScaling = ToolStripItemImageScaling.None
btn.Margin = System.Windows.Forms.Padding.Empty
btn.Padding = System.Windows.Forms.Padding.Empty
' Add the new ToolStripButton control to the GridStrip.
Me.Items.Add(btn)
' Set the cell position of the ToolStripButton control.
Dim cellPos As New TableLayoutPanelCellPosition(i, j)
Me.tableSettings.SetCellPosition(btn, cellPos)
' If this is the ToolStripButton control at cell (0,0),
' assign it as the empty cell button.
If i = 0 AndAlso j = 0 Then
btn.Text = "Empty Cell"
btn.Image = b
Me.emptyCellButton = btn
End If
Next j
Next i
End Sub
' This method defines the Paint event behavior.
' The GridStripRenderer requires that the GridStrip
' be fully layed out when it is renders, so this
' initialization code cannot be placed in the
' GridStrip constructor. By the time the Paint
' event is raised, the control layout has been
' completed, so the GridStripRenderer can paint
' correctly. This one-time initialization is
' implemented with the firstTime field.
Protected Overrides Sub OnPaint(e As PaintEventArgs)
MyBase.OnPaint(e)
If Not Me.firstTime Then
Me.Renderer = New GridStripRenderer()
' Comment this line to see the unscrambled image.
Me.ScrambleButtons()
Me.firstTime = True
End If
End Sub
' This utility method changes the ToolStripButton control
' positions in the TableLayoutPanel. This scrambles the
' buttons to initialize the puzzle.
Private Sub ScrambleButtons()
Dim i As Integer = 0
Dim lastElement As Integer = Me.Items.Count - 1
While i <> lastElement AndAlso lastElement - i > 1
Dim pos1 As TableLayoutPanelCellPosition = _
Me.tableSettings.GetCellPosition(Me.Items(i))
Dim pos2 As TableLayoutPanelCellPosition = _
Me.tableSettings.GetCellPosition(Me.Items(lastElement))
Me.tableSettings.SetCellPosition(Me.Items(i), pos2)
i += 1
Me.tableSettings.SetCellPosition(Me.Items(lastElement), pos1)
lastElement -= 1
End While
End Sub
' This method defines the MouseDown event behavior.
' If the user has clicked on a valid drag source,
' the drag operation starts.
Protected Overrides Sub OnMouseDown(mea As MouseEventArgs)
MyBase.OnMouseDown(mea)
Dim btn As ToolStripButton = CType(Me.GetItemAt(mea.Location), ToolStripButton)
If (btn IsNot Nothing) Then
If Me.IsValidDragSource(btn) Then
Me.dragButton = btn
End If
End If
End Sub
' This method defines the MouseMove event behavior.
Protected Overrides Sub OnMouseMove(mea As MouseEventArgs)
MyBase.OnMouseMove(mea)
' Is a drag operation pending?
If (Me.dragButton IsNot Nothing) Then
' A drag operation is pending. Call DoDragDrop to
' determine the disposition of the operation.
Dim dropEffect As DragDropEffects = Me.DoDragDrop(New DataObject(Me.dragButton), DragDropEffects.Move)
End If
End Sub
' This method defines the DragOver event behavior.
Protected Overrides Sub OnDragOver(dea As DragEventArgs)
MyBase.OnDragOver(dea)
' Get the ToolStripButton control
' at the given mouse position.
Dim p As New Point(dea.X, dea.Y)
Dim item As ToolStripButton = CType(Me.GetItemAt(Me.PointToClient(p)), ToolStripButton)
' If the ToolStripButton control is the empty cell,
' indicate that the move operation is valid.
If item Is Me.emptyCellButton Then
' Set the drag operation to indicate a valid move.
dea.Effect = DragDropEffects.Move
End If
End Sub
' This method defines the DragDrop event behavior.
Protected Overrides Sub OnDragDrop(dea As DragEventArgs)
MyBase.OnDragDrop(dea)
' Did a valid move operation occur?
If dea.Effect = DragDropEffects.Move Then
' The move operation is valid. Adjust the state
' of the GridStrip control's TableLayoutPanel,
' by swapping the positions of the source button
' and the empty cell button.
' Get the cell of the control to move.
Dim sourcePos As TableLayoutPanelCellPosition = tableSettings.GetCellPosition(Me.dragButton)
' Get the cell of the emptyCellButton.
Dim dropPos As TableLayoutPanelCellPosition = tableSettings.GetCellPosition(Me.emptyCellButton)
' Move the control to the empty cell.
tableSettings.SetCellPosition(Me.dragButton, dropPos)
' Set the position of the empty cell to
' that of the previously occupied cell.
tableSettings.SetCellPosition(Me.emptyCellButton, sourcePos)
' Reset the drag operation.
Me.dragButton = Nothing
End If
End Sub
' This method defines the DragLeave event behavior.
' If the mouse leaves the client area of the GridStrip
' control, the drag operation is canceled.
Protected Overrides Sub OnDragLeave(e As EventArgs)
MyBase.OnDragLeave(e)
' Reset the drag operation.
Me.dragButton = Nothing
End Sub
' This method defines the ueryContinueDrag event behavior.
' If the mouse leaves the client area of the GridStrip
' control, the drag operation is canceled.
Protected Overrides Sub OnQueryContinueDrag(qcdevent As QueryContinueDragEventArgs)
MyBase.OnQueryContinueDrag(qcdevent)
' Get the current mouse position, in screen coordinates.
Dim mousePos As Point = Me.PointToClient(Control.MousePosition)
' If the mouse position is outside the GridStrip control's
' client area, cancel the drag operation. Be sure to
' transform the mouse's screen coordinates to client coordinates.
If Not Me.ClientRectangle.Contains(mousePos) Then
qcdevent.Action = DragAction.Cancel
End If
End Sub
' This utility method determines if a button
' is positioned relative to the empty cell
' such that it can be dragged into the empty cell.
Overloads Private Function IsValidDragSource(b As ToolStripButton) As Boolean
Dim sourcePos As TableLayoutPanelCellPosition = tableSettings.GetCellPosition(b)
Dim emptyPos As TableLayoutPanelCellPosition = tableSettings.GetCellPosition(Me.emptyCellButton)
Return IsValidDragSource(sourcePos, emptyPos)
End Function
' This utility method determines if a cell position
' is adjacent to the empty cell.
Friend Overloads Shared Function IsValidDragSource( _
ByVal sourcePos As TableLayoutPanelCellPosition, _
ByVal emptyPos As TableLayoutPanelCellPosition) As Boolean
Dim returnValue As Boolean = False
' A cell is considered to be a valid drag source if it
' is adjacent to the empty cell. Cells that are positioned
' on a diagonal are not valid.
If sourcePos.Column = emptyPos.Column - 1 AndAlso sourcePos.Row = emptyPos.Row OrElse _
(sourcePos.Column = emptyPos.Column + 1 AndAlso sourcePos.Row = emptyPos.Row) OrElse _
(sourcePos.Column = emptyPos.Column AndAlso sourcePos.Row = emptyPos.Row - 1) OrElse _
(sourcePos.Column = emptyPos.Column AndAlso sourcePos.Row = emptyPos.Row + 1) Then
returnValue = True
End If
Return returnValue
End Function
' This class implements a custom ToolStripRenderer for the
' GridStrip control. It customizes three aspects of the
' GridStrip control's appearance: GridStrip border,
' ToolStripButton border, and ToolStripButton image.
Friend Class GridStripRenderer
Inherits ToolStripRenderer
' The style of the empty cell's text.
Private Shared style As New StringFormat()
' The thickness (width or height) of a
' ToolStripButton control's border.
Private Shared borderThickness As Integer = 2
' The main bitmap that is the source for the
' subimagesthat are assigned to individual
' ToolStripButton controls.
Private bmp As Bitmap = Nothing
' The brush that paints the background of
' the GridStrip control.
Private backgroundBrush As Brush = Nothing
' This is the static constructor. It initializes the
' StringFormat for drawing the text in the empty cell.
Shared Sub New()
style.Alignment = StringAlignment.Center
style.LineAlignment = StringAlignment.Center
End Sub
' This method initializes the GridStripRenderer by
' creating the image that is used as the source for
' the individual button images.
Protected Overrides Sub Initialize(ts As ToolStrip)
MyBase.Initialize(ts)
Me.InitializeBitmap(ts)
End Sub
' This method initializes an individual ToolStripButton
' control. It copies a subimage from the GridStripRenderer's
' main image, according to the position and size of
' the ToolStripButton.
Protected Overrides Sub InitializeItem(item As ToolStripItem)
MyBase.InitializeItem(item)
Dim gs As GridStrip = item.Owner
' The empty cell does not receive a subimage.
If ((TypeOf (item) Is ToolStripButton) And _
(item IsNot gs.EmptyCell)) Then
' Copy the subimage from the appropriate
' part of the main image.
Dim subImage As Bitmap = bmp.Clone(item.Bounds, PixelFormat.Undefined)
' Assign the subimage to the ToolStripButton
' control's Image property.
item.Image = subImage
End If
End Sub
' This utility method creates the main image that
' is the source for the subimages of the individual
' ToolStripButton controls.
Private Sub InitializeBitmap(toolStrip As ToolStrip)
' Create the main bitmap, into which the image is drawn.
Me.bmp = New Bitmap(toolStrip.Size.Width, toolStrip.Size.Height)
' Draw a fancy pattern. This could be any image or drawing.
Dim g As Graphics = Graphics.FromImage(bmp)
Try
' Draw smoothed lines.
g.SmoothingMode = SmoothingMode.AntiAlias
' Draw the image. In this case, it is
' a number of concentric ellipses.
Dim i As Integer
For i = 0 To toolStrip.Size.Width - 8 Step 8
g.DrawEllipse(Pens.Blue, 0, 0, i, i)
Next i
Finally
g.Dispose()
End Try
End Sub
' This method draws a border around the GridStrip control.
Protected Overrides Sub OnRenderToolStripBorder(e As ToolStripRenderEventArgs)
MyBase.OnRenderToolStripBorder(e)
ControlPaint.DrawFocusRectangle(e.Graphics, e.AffectedBounds, SystemColors.ControlDarkDark, SystemColors.ControlDarkDark)
End Sub
' This method renders the GridStrip control's background.
Protected Overrides Sub OnRenderToolStripBackground(e As ToolStripRenderEventArgs)
MyBase.OnRenderToolStripBackground(e)
' This late initialization is a workaround. The gradient
' depends on the bounds of the GridStrip control. The bounds
' are dependent on the layout engine, which hasn't fully
' performed layout by the time the Initialize method runs.
If Me.backgroundBrush Is Nothing Then
Me.backgroundBrush = New LinearGradientBrush(e.ToolStrip.ClientRectangle, SystemColors.ControlLightLight, SystemColors.ControlDark, 90, True)
End If
' Paint the GridStrip control's background.
e.Graphics.FillRectangle(Me.backgroundBrush, e.AffectedBounds)
End Sub
' This method draws a border around the button's image. If the background
' to be rendered belongs to the empty cell, a string is drawn. Otherwise,
' a border is drawn at the edges of the button.
Protected Overrides Sub OnRenderButtonBackground(e As ToolStripItemRenderEventArgs)
MyBase.OnRenderButtonBackground(e)
' Define some local variables for convenience.
Dim g As Graphics = e.Graphics
Dim gs As GridStrip = e.ToolStrip
Dim gsb As ToolStripButton = e.Item
' Calculate the rectangle around which the border is painted.
Dim imageRectangle As New Rectangle(borderThickness, borderThickness, e.Item.Width - 2 * borderThickness, e.Item.Height - 2 * borderThickness)
' If rendering the empty cell background, draw an
' explanatory string, centered in the ToolStripButton.
If gsb Is gs.EmptyCell Then
e.Graphics.DrawString("Drag to here", gsb.Font, SystemBrushes.ControlDarkDark, imageRectangle, style)
Else
' If the button can be a drag source, paint its border red.
' otherwise, paint its border a dark color.
Dim b As Brush = IIf(gs.IsValidDragSource(gsb), Brushes.Red, SystemBrushes.ControlDarkDark)
' Draw the top segment of the border.
Dim borderSegment As New Rectangle(0, 0, e.Item.Width, imageRectangle.Top)
g.FillRectangle(b, borderSegment)
' Draw the right segment.
borderSegment = New Rectangle(imageRectangle.Right, 0, e.Item.Bounds.Right - imageRectangle.Right, imageRectangle.Bottom)
g.FillRectangle(b, borderSegment)
' Draw the left segment.
borderSegment = New Rectangle(0, 0, imageRectangle.Left, e.Item.Height)
g.FillRectangle(b, borderSegment)
' Draw the bottom segment.
borderSegment = New Rectangle(0, imageRectangle.Bottom, e.Item.Width, e.Item.Bounds.Bottom - imageRectangle.Bottom)
g.FillRectangle(b, borderSegment)
End If
End Sub
End Class
#Region "Windows Forms Designer generated code"
Private Sub InitializeComponent()
Me.components = New System.ComponentModel.Container()
Me.SuspendLayout()
'
' GridStrip
'
Me.AllowDrop = True
Me.BackgroundImageLayout = System.Windows.Forms.ImageLayout.None
Me.CanOverflow = False
Me.Dock = System.Windows.Forms.DockStyle.None
Me.GripStyle = System.Windows.Forms.ToolStripGripStyle.Hidden
Me.LayoutStyle = System.Windows.Forms.ToolStripLayoutStyle.Table
Me.ResumeLayout(False)
End Sub 'InitializeComponent
#End Region
End Class
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.Text;
using System.Windows.Forms;
using System.Windows.Forms.Layout;
namespace GridStripLib
{
// The following class implements a sliding-tile puzzle.
// The GridStrip control is a custom ToolStrip that arranges
// its ToolStripButton controls in a grid layout. There is
// one empty cell, into which the user can slide an adjacent
// tile with a drag-and-drop operation. Tiles that are eligible
// for moving are highlighted.
public class GridStrip : ToolStrip
{
// The button that is the drag source.
private ToolStripButton dragButton = null;
// Settings for the ToolStrip control's TableLayoutPanel.
// This provides access to the cell position of each
// ToolStripButton.
private TableLayoutSettings tableSettings = null;
// The empty cell. ToolStripButton controls that are
// adjacent to this button can be moved to this button's
// cell position.
private ToolStripButton emptyCellButton = null;
// The dimensions of each tile. A tile is represented
// by a ToolStripButton controls.
private Size tileSize = new Size(128, 128);
// The number of rows in the GridStrip control.
private readonly int rows = 5;
// The number of columns in the GridStrip control.
private readonly int columns = 5;
// The one-time initialzation behavior is enforced
// with this field. For more information, see the
// OnPaint method.
private bool firstTime = false;
// This is a required by the Windows Forms designer.
private System.ComponentModel.IContainer components;
// The default constructor.
public GridStrip()
{
this.InitializeComponent();
this.InitializeTableLayoutSettings();
}
// This property exposes the empty cell to the
// GridStripRenderer class.
internal ToolStripButton EmptyCell
{
get
{
return this.emptyCellButton;
}
}
// This utility method initializes the TableLayoutPanel
// which contains the ToolStripButton controls.
private void InitializeTableLayoutSettings()
{
// Specify the numbers of rows and columns in the GridStrip control.
this.tableSettings = base.LayoutSettings as TableLayoutSettings;
this.tableSettings.ColumnCount = this.rows;
this.tableSettings.RowCount = this.columns;
// Create a dummy bitmap with the dimensions of each tile.
// The GridStrip control sizes itself based on these dimensions.
Bitmap b = new Bitmap(tileSize.Width, tileSize.Height);
// Populate the GridStrip control with ToolStripButton controls.
for (int i = 0; i < this.tableSettings.ColumnCount; i++)
{
for (int j = 0; j < this.tableSettings.RowCount; j++)
{
// Create a new ToolStripButton control.
ToolStripButton btn = new ToolStripButton();
btn.DisplayStyle = ToolStripItemDisplayStyle.Image;
btn.Image = b;
btn.ImageAlign = ContentAlignment.MiddleCenter;
btn.ImageScaling = ToolStripItemImageScaling.None;
btn.Margin = Padding.Empty;
btn.Padding = Padding.Empty;
// Add the new ToolStripButton control to the GridStrip.
this.Items.Add(btn);
// Set the cell position of the ToolStripButton control.
TableLayoutPanelCellPosition cellPos = new TableLayoutPanelCellPosition(i, j);
this.tableSettings.SetCellPosition(btn, cellPos);
// If this is the ToolStripButton control at cell (0,0),
// assign it as the empty cell button.
if( i == 0 && j == 0 )
{
btn.Text = "Empty Cell";
btn.Image = b;
this.emptyCellButton = btn;
}
}
}
}
// This method defines the Paint event behavior.
// The GridStripRenderer requires that the GridStrip
// be fully layed out when it is renders, so this
// initialization code cannot be placed in the
// GridStrip constructor. By the time the Paint
// event is raised, the control layout has been
// completed, so the GridStripRenderer can paint
// correctly. This one-time initialization is
// implemented with the firstTime field.
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
if (!this.firstTime)
{
this.Renderer = new GridStripRenderer();
// Comment this line to see the unscrambled image.
this.ScrambleButtons();
this.firstTime = true;
}
}
// This utility method changes the ToolStripButton control
// positions in the TableLayoutPanel. This scrambles the
// buttons to initialize the puzzle.
private void ScrambleButtons()
{
int i = 0;
int lastElement = this.Items.Count - 1;
while ( (i != lastElement ) &&
(lastElement - i > 1) )
{
TableLayoutPanelCellPosition pos1 =
this.tableSettings.GetCellPosition(this.Items[i]);
TableLayoutPanelCellPosition pos2 =
this.tableSettings.GetCellPosition(this.Items[lastElement]);
this.tableSettings.SetCellPosition(
this.Items[i++],
pos2);
this.tableSettings.SetCellPosition(
this.Items[lastElement--],
pos1);
}
}
// This method defines the MouseDown event behavior.
// If the user has clicked on a valid drag source,
// the drag operation starts.
protected override void OnMouseDown(MouseEventArgs mea)
{
base.OnMouseDown(mea);
ToolStripButton btn = this.GetItemAt(mea.Location) as ToolStripButton;
if (btn != null)
{
if (this.IsValidDragSource(btn))
{
this.dragButton = btn;
}
}
}
// This method defines the MouseMove event behavior.
protected override void OnMouseMove(MouseEventArgs mea)
{
base.OnMouseMove(mea);
// Is a drag operation pending?
if (this.dragButton != null)
{
// A drag operation is pending. Call DoDragDrop to
// determine the disposition of the operation.
DragDropEffects dropEffect = this.DoDragDrop(
new DataObject(this.dragButton),
DragDropEffects.Move);
}
}
// This method defines the DragOver event behavior.
protected override void OnDragOver(DragEventArgs dea)
{
base.OnDragOver(dea);
// Get the ToolStripButton control
// at the given mouse position.
Point p = new Point(dea.X, dea.Y);
ToolStripButton item = this.GetItemAt(
this.PointToClient(p)) as ToolStripButton;
// If the ToolStripButton control is the empty cell,
// indicate that the move operation is valid.
if( item == this.emptyCellButton )
{
// Set the drag operation to indicate a valid move.
dea.Effect = DragDropEffects.Move;
}
}
// This method defines the DragDrop event behavior.
protected override void OnDragDrop(DragEventArgs dea)
{
base.OnDragDrop(dea);
// Did a valid move operation occur?
if (dea.Effect == DragDropEffects.Move)
{
// The move operation is valid. Adjust the state
// of the GridStrip control's TableLayoutPanel,
// by swapping the positions of the source button
// and the empty cell button.
// Get the cell of the control to move.
TableLayoutPanelCellPosition sourcePos =
tableSettings.GetCellPosition(this.dragButton);
// Get the cell of the emptyCellButton.
TableLayoutPanelCellPosition dropPos =
tableSettings.GetCellPosition(this.emptyCellButton);
// Move the control to the empty cell.
tableSettings.SetCellPosition(this.dragButton, dropPos);
// Set the position of the empty cell to
// that of the previously occupied cell.
tableSettings.SetCellPosition(this.emptyCellButton, sourcePos);
// Reset the drag operation.
this.dragButton = null;
}
}
// This method defines the DragLeave event behavior.
// If the mouse leaves the client area of the GridStrip
// control, the drag operation is canceled.
protected override void OnDragLeave(EventArgs e)
{
base.OnDragLeave(e);
// Reset the drag operation.
this.dragButton = null;
}
// This method defines the ueryContinueDrag event behavior.
// If the mouse leaves the client area of the GridStrip
// control, the drag operation is canceled.
protected override void OnQueryContinueDrag(QueryContinueDragEventArgs qcdevent)
{
base.OnQueryContinueDrag(qcdevent);
// Get the current mouse position, in screen coordinates.
Point mousePos = this.PointToClient(Control.MousePosition);
// If the mouse position is outside the GridStrip control's
// client area, cancel the drag operation. Be sure to
// transform the mouse's screen coordinates to client coordinates.
if (!this.ClientRectangle.Contains(mousePos))
{
qcdevent.Action = DragAction.Cancel;
}
}
// This utility method determines if a button
// is positioned relative to the empty cell
// such that it can be dragged into the empty cell.
private bool IsValidDragSource(ToolStripButton b)
{
TableLayoutPanelCellPosition sourcePos =
tableSettings.GetCellPosition(b);
TableLayoutPanelCellPosition emptyPos =
tableSettings.GetCellPosition(this.emptyCellButton);
return (IsValidDragSource(sourcePos, emptyPos));
}
// This utility method determines if a cell position
// is adjacent to the empty cell.
internal static bool IsValidDragSource(
TableLayoutPanelCellPosition sourcePos,
TableLayoutPanelCellPosition emptyPos)
{
bool returnValue = false;
// A cell is considered to be a valid drag source if it
// is adjacent to the empty cell. Cells that are positioned
// on a diagonal are not valid.
if (((sourcePos.Column == emptyPos.Column - 1) && (sourcePos.Row == emptyPos.Row)) ||
((sourcePos.Column == emptyPos.Column + 1) && (sourcePos.Row == emptyPos.Row)) ||
((sourcePos.Column == emptyPos.Column) && (sourcePos.Row == emptyPos.Row - 1)) ||
((sourcePos.Column == emptyPos.Column) && (sourcePos.Row == emptyPos.Row + 1)))
{
returnValue = true;
}
return returnValue;
}
// This class implements a custom ToolStripRenderer for the
// GridStrip control. It customizes three aspects of the
// GridStrip control's appearance: GridStrip border,
// ToolStripButton border, and ToolStripButton image.
internal class GridStripRenderer : ToolStripRenderer
{
// The style of the empty cell's text.
private static StringFormat style = new StringFormat();
// The thickness (width or height) of a
// ToolStripButton control's border.
static int borderThickness = 2;
// The main bitmap that is the source for the
// subimagesthat are assigned to individual
// ToolStripButton controls.
private Bitmap bmp = null;
// The brush that paints the background of
// the GridStrip control.
private Brush backgroundBrush = null;
// This is the static constructor. It initializes the
// StringFormat for drawing the text in the empty cell.
static GridStripRenderer()
{
style.Alignment = StringAlignment.Center;
style.LineAlignment = StringAlignment.Center;
}
// This method initializes the GridStripRenderer by
// creating the image that is used as the source for
// the individual button images.
protected override void Initialize(ToolStrip ts)
{
base.Initialize(ts);
this.InitializeBitmap(ts);
}
// This method initializes an individual ToolStripButton
// control. It copies a subimage from the GridStripRenderer's
// main image, according to the position and size of
// the ToolStripButton.
protected override void InitializeItem(ToolStripItem item)
{
base.InitializeItem(item);
GridStrip gs = item.Owner as GridStrip;
// The empty cell does not receive a subimage.
if ((item is ToolStripButton) &&
(item != gs.EmptyCell))
{
// Copy the subimage from the appropriate
// part of the main image.
Bitmap subImage = bmp.Clone(
item.Bounds,
PixelFormat.Undefined);
// Assign the subimage to the ToolStripButton
// control's Image property.
item.Image = subImage;
}
}
// This utility method creates the main image that
// is the source for the subimages of the individual
// ToolStripButton controls.
private void InitializeBitmap(ToolStrip toolStrip)
{
// Create the main bitmap, into which the image is drawn.
this.bmp = new Bitmap(
toolStrip.Size.Width,
toolStrip.Size.Height);
// Draw a fancy pattern. This could be any image or drawing.
using (Graphics g = Graphics.FromImage(bmp))
{
// Draw smoothed lines.
g.SmoothingMode = SmoothingMode.AntiAlias;
// Draw the image. In this case, it is
// a number of concentric ellipses.
for (int i = 0; i < toolStrip.Size.Width; i += 8)
{
g.DrawEllipse(Pens.Blue, 0, 0, i, i);
}
}
}
// This method draws a border around the GridStrip control.
protected override void OnRenderToolStripBorder(
ToolStripRenderEventArgs e)
{
base.OnRenderToolStripBorder(e);
ControlPaint.DrawFocusRectangle(
e.Graphics,
e.AffectedBounds,
SystemColors.ControlDarkDark,
SystemColors.ControlDarkDark);
}
// This method renders the GridStrip control's background.
protected override void OnRenderToolStripBackground(
ToolStripRenderEventArgs e)
{
base.OnRenderToolStripBackground(e);
// This late initialization is a workaround. The gradient
// depends on the bounds of the GridStrip control. The bounds
// are dependent on the layout engine, which hasn't fully
// performed layout by the time the Initialize method runs.
if (this.backgroundBrush == null)
{
this.backgroundBrush = new LinearGradientBrush(
e.ToolStrip.ClientRectangle,
SystemColors.ControlLightLight,
SystemColors.ControlDark,
90,
true);
}
// Paint the GridStrip control's background.
e.Graphics.FillRectangle(
this.backgroundBrush,
e.AffectedBounds);
}
// This method draws a border around the button's image. If the background
// to be rendered belongs to the empty cell, a string is drawn. Otherwise,
// a border is drawn at the edges of the button.
protected override void OnRenderButtonBackground(
ToolStripItemRenderEventArgs e)
{
base.OnRenderButtonBackground(e);
// Define some local variables for convenience.
Graphics g = e.Graphics;
GridStrip gs = e.ToolStrip as GridStrip;
ToolStripButton gsb = e.Item as ToolStripButton;
// Calculate the rectangle around which the border is painted.
Rectangle imageRectangle = new Rectangle(
borderThickness,
borderThickness,
e.Item.Width - 2 * borderThickness,
e.Item.Height - 2 * borderThickness);
// If rendering the empty cell background, draw an
// explanatory string, centered in the ToolStripButton.
if (gsb == gs.EmptyCell)
{
e.Graphics.DrawString(
"Drag to here",
gsb.Font,
SystemBrushes.ControlDarkDark,
imageRectangle, style);
}
else
{
// If the button can be a drag source, paint its border red.
// otherwise, paint its border a dark color.
Brush b = gs.IsValidDragSource(gsb) ? b =
Brushes.Red : SystemBrushes.ControlDarkDark;
// Draw the top segment of the border.
Rectangle borderSegment = new Rectangle(
0,
0,
e.Item.Width,
imageRectangle.Top);
g.FillRectangle(b, borderSegment);
// Draw the right segment.
borderSegment = new Rectangle(
imageRectangle.Right,
0,
e.Item.Bounds.Right - imageRectangle.Right,
imageRectangle.Bottom);
g.FillRectangle(b, borderSegment);
// Draw the left segment.
borderSegment = new Rectangle(
0,
0,
imageRectangle.Left,
e.Item.Height);
g.FillRectangle(b, borderSegment);
// Draw the bottom segment.
borderSegment = new Rectangle(
0,
imageRectangle.Bottom,
e.Item.Width,
e.Item.Bounds.Bottom - imageRectangle.Bottom);
g.FillRectangle(b, borderSegment);
}
}
}
#region Windows Forms Designer generated code
private void InitializeComponent()
{
this.components = new System.ComponentModel.Container();
this.SuspendLayout();
//
// GridStrip
//
this.AllowDrop = true;
this.BackgroundImageLayout = System.Windows.Forms.ImageLayout.None;
this.CanOverflow = false;
this.Dock = System.Windows.Forms.DockStyle.None;
this.GripStyle = System.Windows.Forms.ToolStripGripStyle.Hidden;
this.LayoutStyle = System.Windows.Forms.ToolStripLayoutStyle.Table;
this.ResumeLayout(false);
}
#endregion
}
}
Компиляция кода
Для этого примера необходимы следующие условия.
- Ссылки на сборки System.Drawing и System.Windows.Forms.
Дополнительные сведения о построении этого примера из командной строки для Visual Basic или Visual C# см. в разделе Построение из командной строки (Visual Basic) или Построение из командной строки с помощью csc.exe. Можно также построить этот пример в Visual Studio путем вставки кода в новый проект. Дополнительные сведения см. в разделах Практическое руководство. Компиляция и выполнение откомпилированного примера кода формы Windows Forms с помощью Visual Studio и Практическое руководство. Компиляция и выполнение откомпилированного примера кода формы Windows Forms с помощью Visual Studio и Практическое руководство. Компиляция и выполнение откомпилированного примера кода формы Windows Forms с помощью Visual Studio.