Поделиться через


Практическое руководство. Реализация пользовательского класса, производного от ToolStripRenderer

Обновлен: Ноябрь 2007

Можно настроить внешний вид элемента управления ToolStrip путем реализации класса, производного от ToolStripRenderer. Таким образом, можно добиться гибкости в создании внешнего вида, отличного от внешнего вида, который обеспечивается классами ToolStripProfessionalRenderer и ToolStripSystemRenderer.

Пример

В следующем примере показано, как реализовать пользовательский класс ToolStripRenderer. В этом примере элемент управления GridStrip реализует головоломку со скользящими мозаиками, которая позволяет пользователю перемещать плитки в структуре таблицы для формирования изображения. Пользовательский элемент управления ToolStrip выстраивает свои элементы управления ToolStripButton в структуре сетки. В структуре присутствует одна пустая ячейка, в которую при помощи перетаскивания можно сдвинуть соседнюю мозаику. Мозаики, которые можно сдвигать, выделены.

Класс GridStripRenderer настраивает три характеристики внешнего вида элемента управления GridStrip:

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.

См. также

Ссылки

MenuStrip

ToolStrip

ToolStripRenderer

ToolStripProfessionalRenderer

ToolStripSystemRenderer

StatusStrip

Другие ресурсы

Элемент управления ToolStrip (Windows Forms)