Практическое руководство. Создание элемента управления Windows Forms, в котором используются преимущества функций, применяемых во время разработки
Обновлен: Ноябрь 2007
В следующем примере демонстрируется, как создать пользовательский элемент управления и связанный с ним пользовательский конструктор. После создания библиотеки можно будет построить пользовательские реализации MarqueeControl, выполняемые в форме.
В Visual Studio предусмотрена расширенная поддержка этой задачи.
Пример
Imports System
' This interface defines the contract for any class that is to
' be used in constructing a MarqueeControl.
Public Interface IMarqueeWidget
' This method starts the animation. If the control can
' contain other classes that implement IMarqueeWidget as
' children, the control should call StartMarquee on all
' its IMarqueeWidget child controls.
Sub StartMarquee()
' This method stops the animation. If the control can
' contain other classes that implement IMarqueeWidget as
' children, the control should call StopMarquee on all
' its IMarqueeWidget child controls.
Sub StopMarquee()
' This method specifies the refresh rate for the animation,
' in milliseconds.
Property UpdatePeriod() As Integer
End Interface
using System;
namespace MarqueeControlLibrary
{
// This interface defines the contract for any class that is to
// be used in constructing a MarqueeControl.
public interface IMarqueeWidget
{
// This method starts the animation. If the control can
// contain other classes that implement IMarqueeWidget as
// children, the control should call StartMarquee on all
// its IMarqueeWidget child controls.
void StartMarquee();
// This method stops the animation. If the control can
// contain other classes that implement IMarqueeWidget as
// children, the control should call StopMarquee on all
// its IMarqueeWidget child controls.
void StopMarquee();
// This method specifies the refresh rate for the animation,
// in milliseconds.
int UpdatePeriod
{
get;
set;
}
}
}
Imports System
Imports System.ComponentModel
Imports System.ComponentModel.Design
Imports System.Diagnostics
Imports System.Drawing
Imports System.Drawing.Design
Imports System.Threading
Imports System.Windows.Forms
Imports System.Windows.Forms.Design
' This defines the possible values for the MarqueeBorder
' control's SpinDirection property.
Public Enum MarqueeSpinDirection
CW
CCW
End Enum
' This defines the possible values for the MarqueeBorder
' control's LightShape property.
Public Enum MarqueeLightShape
Square
Circle
End Enum
<Designer(GetType(MarqueeControlLibrary.Design.MarqueeBorderDesigner)), _
ToolboxItemFilter("MarqueeControlLibrary.MarqueeBorder", _
ToolboxItemFilterType.Require)> _
Partial Public Class MarqueeBorder
Inherits Panel
Implements IMarqueeWidget
Public Shared MaxLightSize As Integer = 10
' These fields back the public properties.
Private updatePeriodValue As Integer = 50
Private lightSizeValue As Integer = 5
Private lightPeriodValue As Integer = 3
Private lightSpacingValue As Integer = 1
Private lightColorValue As Color
Private darkColorValue As Color
Private spinDirectionValue As MarqueeSpinDirection = MarqueeSpinDirection.CW
Private lightShapeValue As MarqueeLightShape = MarqueeLightShape.Square
' These brushes are used to paint the light and dark
' colors of the marquee lights.
Private lightBrush As Brush
Private darkBrush As Brush
' This field tracks the progress of the "first" light as it
' "travels" around the marquee border.
Private currentOffset As Integer = 0
' This component updates the control asynchronously.
Private WithEvents backgroundWorker1 As System.ComponentModel.BackgroundWorker
Public Sub New()
' This call is required by the Windows.Forms Form Designer.
InitializeComponent()
' Initialize light and dark colors
' to the control's default values.
Me.lightColorValue = Me.ForeColor
Me.darkColorValue = Me.BackColor
Me.lightBrush = New SolidBrush(Me.lightColorValue)
Me.darkBrush = New SolidBrush(Me.darkColorValue)
' The MarqueeBorder control manages its own padding,
' because it requires that any contained controls do
' not overlap any of the marquee lights.
Dim pad As Integer = 2 * (Me.lightSizeValue + Me.lightSpacingValue)
Me.Padding = New Padding(pad, pad, pad, pad)
SetStyle(ControlStyles.OptimizedDoubleBuffer, True)
End Sub
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
#Region "IMarqueeWidget implementation"
Public Overridable Sub StartMarquee() _
Implements IMarqueeWidget.StartMarquee
' The MarqueeBorder control may contain any number of
' controls that implement IMarqueeWidget, so find
' each IMarqueeWidget child and call its StartMarquee
' method.
Dim cntrl As Control
For Each cntrl In Me.Controls
If TypeOf cntrl Is IMarqueeWidget Then
Dim widget As IMarqueeWidget = CType(cntrl, IMarqueeWidget)
widget.StartMarquee()
End If
Next cntrl
' Start the updating thread and pass it the UpdatePeriod.
Me.backgroundWorker1.RunWorkerAsync(Me.UpdatePeriod)
End Sub
Public Overridable Sub StopMarquee() _
Implements IMarqueeWidget.StopMarquee
' The MarqueeBorder control may contain any number of
' controls that implement IMarqueeWidget, so find
' each IMarqueeWidget child and call its StopMarquee
' method.
Dim cntrl As Control
For Each cntrl In Me.Controls
If TypeOf cntrl Is IMarqueeWidget Then
Dim widget As IMarqueeWidget = CType(cntrl, IMarqueeWidget)
widget.StopMarquee()
End If
Next cntrl
' Stop the updating thread.
Me.backgroundWorker1.CancelAsync()
End Sub
<Category("Marquee"), Browsable(True)> _
Public Overridable Property UpdatePeriod() As Integer _
Implements IMarqueeWidget.UpdatePeriod
Get
Return Me.updatePeriodValue
End Get
Set(ByVal Value As Integer)
If Value > 0 Then
Me.updatePeriodValue = Value
Else
Throw New ArgumentOutOfRangeException("UpdatePeriod", _
"must be > 0")
End If
End Set
End Property
#End Region
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
#Region "Public Properties"
<Category("Marquee"), Browsable(True)> _
Public Property LightSize() As Integer
Get
Return Me.lightSizeValue
End Get
Set(ByVal Value As Integer)
If Value > 0 AndAlso Value <= MaxLightSize Then
Me.lightSizeValue = Value
Me.DockPadding.All = 2 * Value
Else
Throw New ArgumentOutOfRangeException("LightSize", _
"must be > 0 and < MaxLightSize")
End If
End Set
End Property
<Category("Marquee"), Browsable(True)> _
Public Property LightPeriod() As Integer
Get
Return Me.lightPeriodValue
End Get
Set(ByVal Value As Integer)
If Value > 0 Then
Me.lightPeriodValue = Value
Else
Throw New ArgumentOutOfRangeException("LightPeriod", _
"must be > 0 ")
End If
End Set
End Property
<Category("Marquee"), Browsable(True)> _
Public Property LightColor() As Color
Get
Return Me.lightColorValue
End Get
Set(ByVal Value As Color)
' The LightColor property is only changed if the
' client provides a different value. Comparing values
' from the ToArgb method is the recommended test for
' equality between Color structs.
If Me.lightColorValue.ToArgb() <> Value.ToArgb() Then
Me.lightColorValue = Value
Me.lightBrush = New SolidBrush(Value)
End If
End Set
End Property
<Category("Marquee"), Browsable(True)> _
Public Property DarkColor() As Color
Get
Return Me.darkColorValue
End Get
Set(ByVal Value As Color)
' The DarkColor property is only changed if the
' client provides a different value. Comparing values
' from the ToArgb method is the recommended test for
' equality between Color structs.
If Me.darkColorValue.ToArgb() <> Value.ToArgb() Then
Me.darkColorValue = Value
Me.darkBrush = New SolidBrush(Value)
End If
End Set
End Property
<Category("Marquee"), Browsable(True)> _
Public Property LightSpacing() As Integer
Get
Return Me.lightSpacingValue
End Get
Set(ByVal Value As Integer)
If Value >= 0 Then
Me.lightSpacingValue = Value
Else
Throw New ArgumentOutOfRangeException("LightSpacing", _
"must be >= 0")
End If
End Set
End Property
<Category("Marquee"), Browsable(True), _
EditorAttribute(GetType(LightShapeEditor), _
GetType(System.Drawing.Design.UITypeEditor))> _
Public Property LightShape() As MarqueeLightShape
Get
Return Me.lightShapeValue
End Get
Set(ByVal Value As MarqueeLightShape)
Me.lightShapeValue = Value
End Set
End Property
<Category("Marquee"), Browsable(True)> _
Public Property SpinDirection() As MarqueeSpinDirection
Get
Return Me.spinDirectionValue
End Get
Set(ByVal Value As MarqueeSpinDirection)
Me.spinDirectionValue = Value
End Set
End Property
#End Region
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
#Region "Implementation"
Protected Overrides Sub OnLayout(ByVal levent As LayoutEventArgs)
MyBase.OnLayout(levent)
' Repaint when the layout has changed.
Me.Refresh()
End Sub
' This method paints the lights around the border of the
' control. It paints the top row first, followed by the
' right side, the bottom row, and the left side. The color
' of each light is determined by the IsLit method and
' depends on the light's position relative to the value
' of currentOffset.
Protected Overrides Sub OnPaint(ByVal e As PaintEventArgs)
Dim g As Graphics = e.Graphics
g.Clear(Me.BackColor)
MyBase.OnPaint(e)
' If the control is large enough, draw some lights.
If Me.Width > MaxLightSize AndAlso Me.Height > MaxLightSize Then
' The position of the next light will be incremented
' by this value, which is equal to the sum of the
' light size and the space between two lights.
Dim increment As Integer = _
Me.lightSizeValue + Me.lightSpacingValue
' Compute the number of lights to be drawn along the
' horizontal edges of the control.
Dim horizontalLights As Integer = _
(Me.Width - increment) / increment
' Compute the number of lights to be drawn along the
' vertical edges of the control.
Dim verticalLights As Integer = _
(Me.Height - increment) / increment
' These local variables will be used to position and
' paint each light.
Dim xPos As Integer = 0
Dim yPos As Integer = 0
Dim lightCounter As Integer = 0
Dim brush As Brush
' Draw the top row of lights.
Dim i As Integer
For i = 0 To horizontalLights - 1
brush = IIf(IsLit(lightCounter), Me.lightBrush, Me.darkBrush)
DrawLight(g, brush, xPos, yPos)
xPos += increment
lightCounter += 1
Next i
' Draw the lights flush with the right edge of the control.
xPos = Me.Width - Me.lightSizeValue
' Draw the right column of lights.
'Dim i As Integer
For i = 0 To verticalLights - 1
brush = IIf(IsLit(lightCounter), Me.lightBrush, Me.darkBrush)
DrawLight(g, brush, xPos, yPos)
yPos += increment
lightCounter += 1
Next i
' Draw the lights flush with the bottom edge of the control.
yPos = Me.Height - Me.lightSizeValue
' Draw the bottom row of lights.
'Dim i As Integer
For i = 0 To horizontalLights - 1
brush = IIf(IsLit(lightCounter), Me.lightBrush, Me.darkBrush)
DrawLight(g, brush, xPos, yPos)
xPos -= increment
lightCounter += 1
Next i
' Draw the lights flush with the left edge of the control.
xPos = 0
' Draw the left column of lights.
'Dim i As Integer
For i = 0 To verticalLights - 1
brush = IIf(IsLit(lightCounter), Me.lightBrush, Me.darkBrush)
DrawLight(g, brush, xPos, yPos)
yPos -= increment
lightCounter += 1
Next i
End If
End Sub
' This method determines if the marquee light at lightIndex
' should be lit. The currentOffset field specifies where
' the "first" light is located, and the "position" of the
' light given by lightIndex is computed relative to this
' offset. If this position modulo lightPeriodValue is zero,
' the light is considered to be on, and it will be painted
' with the control's lightBrush.
Protected Overridable Function IsLit(ByVal lightIndex As Integer) As Boolean
Dim directionFactor As Integer = _
IIf(Me.spinDirectionValue = MarqueeSpinDirection.CW, -1, 1)
Return (lightIndex + directionFactor * Me.currentOffset) Mod Me.lightPeriodValue = 0
End Function
Protected Overridable Sub DrawLight( _
ByVal g As Graphics, _
ByVal brush As Brush, _
ByVal xPos As Integer, _
ByVal yPos As Integer)
Select Case Me.lightShapeValue
Case MarqueeLightShape.Square
g.FillRectangle( _
brush, _
xPos, _
yPos, _
Me.lightSizeValue, _
Me.lightSizeValue)
Exit Select
Case MarqueeLightShape.Circle
g.FillEllipse( _
brush, _
xPos, _
yPos, _
Me.lightSizeValue, _
Me.lightSizeValue)
Exit Select
Case Else
Trace.Assert(False, "Unknown value for light shape.")
Exit Select
End Select
End Sub
' This method is called in the worker thread's context,
' so it must not make any calls into the MarqueeBorder
' control. Instead, it communicates to the control using
' the ProgressChanged event.
'
' The only work done in this event handler is
' to sleep for the number of milliseconds specified
' by UpdatePeriod, then raise the ProgressChanged event.
Private Sub backgroundWorker1_DoWork( _
ByVal sender As Object, _
ByVal e As System.ComponentModel.DoWorkEventArgs) _
Handles backgroundWorker1.DoWork
Dim worker As BackgroundWorker = CType(sender, BackgroundWorker)
' This event handler will run until the client cancels
' the background task by calling CancelAsync.
While Not worker.CancellationPending
' The Argument property of the DoWorkEventArgs
' object holds the value of UpdatePeriod, which
' was passed as the argument to the RunWorkerAsync
' method.
Thread.Sleep(Fix(e.Argument))
' The DoWork eventhandler does not actually report
' progress; the ReportProgress event is used to
' periodically alert the control to update its state.
worker.ReportProgress(0)
End While
End Sub
' The ProgressChanged event is raised by the DoWork method.
' This event handler does work that is internal to the
' control. In this case, the currentOffset is incremented,
' and the control is told to repaint itself.
Private Sub backgroundWorker1_ProgressChanged( _
ByVal sender As Object, _
ByVal e As System.ComponentModel.ProgressChangedEventArgs) _
Handles backgroundWorker1.ProgressChanged
Me.currentOffset += 1
Me.Refresh()
End Sub
' This class demonstrates the use of a custom UITypeEditor.
' It allows the MarqueeBorder control's LightShape property
' to be changed at design time using a customized UI element
' that is invoked by the Properties window. The UI is provided
' by the LightShapeSelectionControl class.
Friend Class LightShapeEditor
Inherits UITypeEditor
Private editorService As IWindowsFormsEditorService = Nothing
Public Overrides Function GetEditStyle( _
ByVal context As System.ComponentModel.ITypeDescriptorContext) _
As UITypeEditorEditStyle
Return UITypeEditorEditStyle.DropDown
End Function
Public Overrides Function EditValue( _
ByVal context As ITypeDescriptorContext, _
ByVal provider As IServiceProvider, _
ByVal value As Object) As Object
If (provider IsNot Nothing) Then
editorService = _
CType(provider.GetService(GetType(IWindowsFormsEditorService)), _
IWindowsFormsEditorService)
End If
If (editorService IsNot Nothing) Then
Dim selectionControl As _
New LightShapeSelectionControl( _
CType(value, MarqueeLightShape), _
editorService)
editorService.DropDownControl(selectionControl)
value = selectionControl.LightShape
End If
Return value
End Function
' This method indicates to the design environment that
' the type editor will paint additional content in the
' LightShape entry in the PropertyGrid.
Public Overrides Function GetPaintValueSupported( _
ByVal context As ITypeDescriptorContext) As Boolean
Return True
End Function
' This method paints a graphical representation of the
' selected value of the LightShpae property.
Public Overrides Sub PaintValue( _
ByVal e As PaintValueEventArgs)
Dim shape As MarqueeLightShape = _
CType(e.Value, MarqueeLightShape)
Using p As Pen = Pens.Black
If shape = MarqueeLightShape.Square Then
e.Graphics.DrawRectangle(p, e.Bounds)
Else
e.Graphics.DrawEllipse(p, e.Bounds)
End If
End Using
End Sub
End Class
Private Sub InitializeComponent()
Me.backgroundWorker1 = New System.ComponentModel.BackgroundWorker
'
' backgroundWorker1
'
Me.backgroundWorker1.WorkerReportsProgress = True
Me.backgroundWorker1.WorkerSupportsCancellation = True
End Sub
#End Region
End Class
using System;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Design;
using System.Threading;
using System.Windows.Forms;
using System.Windows.Forms.Design;
namespace MarqueeControlLibrary
{
// This defines the possible values for the MarqueeBorder
// control's SpinDirection property.
public enum MarqueeSpinDirection
{
CW,
CCW
}
// This defines the possible values for the MarqueeBorder
// control's LightShape property.
public enum MarqueeLightShape
{
Square,
Circle
}
[Designer(typeof(MarqueeControlLibrary.Design.MarqueeBorderDesigner ))]
[ToolboxItemFilter("MarqueeControlLibrary.MarqueeBorder", ToolboxItemFilterType.Require)]
public partial class MarqueeBorder : Panel, IMarqueeWidget
{
public static int MaxLightSize = 10;
// These fields back the public properties.
private int updatePeriodValue = 50;
private int lightSizeValue = 5;
private int lightPeriodValue = 3;
private int lightSpacingValue = 1;
private Color lightColorValue;
private Color darkColorValue;
private MarqueeSpinDirection spinDirectionValue = MarqueeSpinDirection.CW;
private MarqueeLightShape lightShapeValue = MarqueeLightShape.Square;
// These brushes are used to paint the light and dark
// colors of the marquee lights.
private Brush lightBrush;
private Brush darkBrush;
// This field tracks the progress of the "first" light as it
// "travels" around the marquee border.
private int currentOffset = 0;
// This component updates the control asynchronously.
private System.ComponentModel.BackgroundWorker backgroundWorker1;
public MarqueeBorder()
{
// This call is required by the Windows.Forms Form Designer.
InitializeComponent();
// Initialize light and dark colors
// to the control's default values.
this.lightColorValue = this.ForeColor;
this.darkColorValue = this.BackColor;
this.lightBrush = new SolidBrush(this.lightColorValue);
this.darkBrush = new SolidBrush(this.darkColorValue);
// The MarqueeBorder control manages its own padding,
// because it requires that any contained controls do
// not overlap any of the marquee lights.
int pad = 2 * (this.lightSizeValue + this.lightSpacingValue);
this.Padding = new Padding(pad, pad, pad, pad);
SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
}
///////////////////////////////////////////////////////////////////////
#region IMarqueeWidget implementation
public virtual void StartMarquee()
{
// The MarqueeBorder control may contain any number of
// controls that implement IMarqueeWidget, so find
// each IMarqueeWidget child and call its StartMarquee
// method.
foreach (Control cntrl in this.Controls)
{
if (cntrl is IMarqueeWidget)
{
IMarqueeWidget widget = cntrl as IMarqueeWidget;
widget.StartMarquee();
}
}
// Start the updating thread and pass it the UpdatePeriod.
this.backgroundWorker1.RunWorkerAsync(this.UpdatePeriod);
}
public virtual void StopMarquee()
{
// The MarqueeBorder control may contain any number of
// controls that implement IMarqueeWidget, so find
// each IMarqueeWidget child and call its StopMarquee
// method.
foreach (Control cntrl in this.Controls)
{
if (cntrl is IMarqueeWidget)
{
IMarqueeWidget widget = cntrl as IMarqueeWidget;
widget.StopMarquee();
}
}
// Stop the updating thread.
this.backgroundWorker1.CancelAsync();
}
[Category("Marquee")]
[Browsable(true)]
public virtual int UpdatePeriod
{
get
{
return this.updatePeriodValue;
}
set
{
if (value > 0)
{
this.updatePeriodValue = value;
}
else
{
throw new ArgumentOutOfRangeException("UpdatePeriod", "must be > 0");
}
}
}
#endregion
///////////////////////////////////////////////////////////////////////
#region Public Properties
[Category("Marquee")]
[Browsable(true)]
public int LightSize
{
get
{
return this.lightSizeValue;
}
set
{
if (value > 0 && value <= MaxLightSize)
{
this.lightSizeValue = value;
this.DockPadding.All = 2 * value;
}
else
{
throw new ArgumentOutOfRangeException("LightSize", "must be > 0 and < MaxLightSize");
}
}
}
[Category("Marquee")]
[Browsable(true)]
public int LightPeriod
{
get
{
return this.lightPeriodValue;
}
set
{
if (value > 0)
{
this.lightPeriodValue = value;
}
else
{
throw new ArgumentOutOfRangeException("LightPeriod", "must be > 0 ");
}
}
}
[Category("Marquee")]
[Browsable(true)]
public Color LightColor
{
get
{
return this.lightColorValue;
}
set
{
// The LightColor property is only changed if the
// client provides a different value. Comparing values
// from the ToArgb method is the recommended test for
// equality between Color structs.
if (this.lightColorValue.ToArgb() != value.ToArgb())
{
this.lightColorValue = value;
this.lightBrush = new SolidBrush(value);
}
}
}
[Category("Marquee")]
[Browsable(true)]
public Color DarkColor
{
get
{
return this.darkColorValue;
}
set
{
// The DarkColor property is only changed if the
// client provides a different value. Comparing values
// from the ToArgb method is the recommended test for
// equality between Color structs.
if (this.darkColorValue.ToArgb() != value.ToArgb())
{
this.darkColorValue = value;
this.darkBrush = new SolidBrush(value);
}
}
}
[Category("Marquee")]
[Browsable(true)]
public int LightSpacing
{
get
{
return this.lightSpacingValue;
}
set
{
if (value >= 0)
{
this.lightSpacingValue = value;
}
else
{
throw new ArgumentOutOfRangeException("LightSpacing", "must be >= 0");
}
}
}
[Category("Marquee")]
[Browsable(true)]
[EditorAttribute(typeof(LightShapeEditor),
typeof(System.Drawing.Design.UITypeEditor))]
public MarqueeLightShape LightShape
{
get
{
return this.lightShapeValue;
}
set
{
this.lightShapeValue = value;
}
}
[Category("Marquee")]
[Browsable(true)]
public MarqueeSpinDirection SpinDirection
{
get
{
return this.spinDirectionValue;
}
set
{
this.spinDirectionValue = value;
}
}
#endregion
///////////////////////////////////////////////////////////////////////
#region Implementation
protected override void OnLayout(LayoutEventArgs levent)
{
base.OnLayout(levent);
// Repaint when the layout has changed.
this.Refresh();
}
// This method paints the lights around the border of the
// control. It paints the top row first, followed by the
// right side, the bottom row, and the left side. The color
// of each light is determined by the IsLit method and
// depends on the light's position relative to the value
// of currentOffset.
protected override void OnPaint(PaintEventArgs e)
{
Graphics g = e.Graphics;
g.Clear(this.BackColor);
base.OnPaint(e);
// If the control is large enough, draw some lights.
if (this.Width > MaxLightSize &&
this.Height > MaxLightSize)
{
// The position of the next light will be incremented
// by this value, which is equal to the sum of the
// light size and the space between two lights.
int increment =
this.lightSizeValue + this.lightSpacingValue;
// Compute the number of lights to be drawn along the
// horizontal edges of the control.
int horizontalLights =
(this.Width - increment) / increment;
// Compute the number of lights to be drawn along the
// vertical edges of the control.
int verticalLights =
(this.Height - increment) / increment;
// These local variables will be used to position and
// paint each light.
int xPos = 0;
int yPos = 0;
int lightCounter = 0;
Brush brush;
// Draw the top row of lights.
for (int i = 0; i < horizontalLights; i++)
{
brush = IsLit(lightCounter) ? this.lightBrush : this.darkBrush;
DrawLight(g, brush, xPos, yPos);
xPos += increment;
lightCounter++;
}
// Draw the lights flush with the right edge of the control.
xPos = this.Width - this.lightSizeValue;
// Draw the right column of lights.
for (int i = 0; i < verticalLights; i++)
{
brush = IsLit(lightCounter) ? this.lightBrush : this.darkBrush;
DrawLight(g, brush, xPos, yPos);
yPos += increment;
lightCounter++;
}
// Draw the lights flush with the bottom edge of the control.
yPos = this.Height - this.lightSizeValue;
// Draw the bottom row of lights.
for (int i = 0; i < horizontalLights; i++)
{
brush = IsLit(lightCounter) ? this.lightBrush : this.darkBrush;
DrawLight(g, brush, xPos, yPos);
xPos -= increment;
lightCounter++;
}
// Draw the lights flush with the left edge of the control.
xPos = 0;
// Draw the left column of lights.
for (int i = 0; i < verticalLights; i++)
{
brush = IsLit(lightCounter) ? this.lightBrush : this.darkBrush;
DrawLight(g, brush, xPos, yPos);
yPos -= increment;
lightCounter++;
}
}
}
// This method determines if the marquee light at lightIndex
// should be lit. The currentOffset field specifies where
// the "first" light is located, and the "position" of the
// light given by lightIndex is computed relative to this
// offset. If this position modulo lightPeriodValue is zero,
// the light is considered to be on, and it will be painted
// with the control's lightBrush.
protected virtual bool IsLit(int lightIndex)
{
int directionFactor =
(this.spinDirectionValue == MarqueeSpinDirection.CW ? -1 : 1);
return (
(lightIndex + directionFactor * this.currentOffset) % this.lightPeriodValue == 0
);
}
protected virtual void DrawLight(
Graphics g,
Brush brush,
int xPos,
int yPos)
{
switch (this.lightShapeValue)
{
case MarqueeLightShape.Square:
{
g.FillRectangle(brush, xPos, yPos, this.lightSizeValue, this.lightSizeValue);
break;
}
case MarqueeLightShape.Circle:
{
g.FillEllipse(brush, xPos, yPos, this.lightSizeValue, this.lightSizeValue);
break;
}
default:
{
Trace.Assert(false, "Unknown value for light shape.");
break;
}
}
}
// This method is called in the worker thread's context,
// so it must not make any calls into the MarqueeBorder
// control. Instead, it communicates to the control using
// the ProgressChanged event.
//
// The only work done in this event handler is
// to sleep for the number of milliseconds specified
// by UpdatePeriod, then raise the ProgressChanged event.
private void backgroundWorker1_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e)
{
BackgroundWorker worker = sender as BackgroundWorker;
// This event handler will run until the client cancels
// the background task by calling CancelAsync.
while (!worker.CancellationPending)
{
// The Argument property of the DoWorkEventArgs
// object holds the value of UpdatePeriod, which
// was passed as the argument to the RunWorkerAsync
// method.
Thread.Sleep((int)e.Argument);
// The DoWork eventhandler does not actually report
// progress; the ReportProgress event is used to
// periodically alert the control to update its state.
worker.ReportProgress(0);
}
}
// The ProgressChanged event is raised by the DoWork method.
// This event handler does work that is internal to the
// control. In this case, the currentOffset is incremented,
// and the control is told to repaint itself.
private void backgroundWorker1_ProgressChanged(
object sender,
System.ComponentModel.ProgressChangedEventArgs e)
{
this.currentOffset++;
this.Refresh();
}
// This class demonstrates the use of a custom UITypeEditor.
// It allows the MarqueeBorder control's LightShape property
// to be changed at design time using a customized UI element
// that is invoked by the Properties window. The UI is provided
// by the LightShapeSelectionControl class.
internal class LightShapeEditor : UITypeEditor
{
private IWindowsFormsEditorService editorService = null;
public override UITypeEditorEditStyle GetEditStyle(
System.ComponentModel.ITypeDescriptorContext context)
{
return UITypeEditorEditStyle.DropDown;
}
public override object EditValue(
ITypeDescriptorContext context,
IServiceProvider provider,
object value)
{
if (provider != null)
{
editorService =
provider.GetService(
typeof(IWindowsFormsEditorService))
as IWindowsFormsEditorService;
}
if (editorService != null)
{
LightShapeSelectionControl selectionControl =
new LightShapeSelectionControl(
(MarqueeLightShape)value,
editorService);
editorService.DropDownControl(selectionControl);
value = selectionControl.LightShape;
}
return value;
}
// This method indicates to the design environment that
// the type editor will paint additional content in the
// LightShape entry in the PropertyGrid.
public override bool GetPaintValueSupported(
ITypeDescriptorContext context)
{
return true;
}
// This method paints a graphical representation of the
// selected value of the LightShpae property.
public override void PaintValue(PaintValueEventArgs e)
{
MarqueeLightShape shape = (MarqueeLightShape)e.Value;
using (Pen p = Pens.Black)
{
if (shape == MarqueeLightShape.Square)
{
e.Graphics.DrawRectangle(p, e.Bounds);
}
else
{
e.Graphics.DrawEllipse(p, e.Bounds);
}
}
}
}
private void InitializeComponent()
{
this.backgroundWorker1 = new System.ComponentModel.BackgroundWorker();
//
// backgroundWorker1
//
this.backgroundWorker1.WorkerReportsProgress = true;
this.backgroundWorker1.WorkerSupportsCancellation = true;
this.backgroundWorker1.ProgressChanged += new System.ComponentModel.ProgressChangedEventHandler(this.backgroundWorker1_ProgressChanged);
this.backgroundWorker1.DoWork += new System.ComponentModel.DoWorkEventHandler(this.backgroundWorker1_DoWork);
}
#endregion
}
}
Imports System
Imports System.ComponentModel
Imports System.ComponentModel.Design
Imports System.Diagnostics
Imports System.Drawing
Imports System.Threading
Imports System.Windows.Forms
Imports System.Windows.Forms.Design
<ToolboxItemFilter("MarqueeControlLibrary.MarqueeText", _
ToolboxItemFilterType.Require)> _
Partial Public Class MarqueeText
Inherits Label
Implements IMarqueeWidget
' When isLit is true, the text is painted in the light color;
' When isLit is false, the text is painted in the dark color.
' This value changes whenever the BackgroundWorker component
' raises the ProgressChanged event.
Private isLit As Boolean = True
' These fields back the public properties.
Private updatePeriodValue As Integer = 50
Private lightColorValue As Color
Private darkColorValue As Color
' These brushes are used to paint the light and dark
' colors of the text.
Private lightBrush As Brush
Private darkBrush As Brush
' This component updates the control asynchronously.
Private WithEvents backgroundWorker1 As BackgroundWorker
Public Sub New()
' This call is required by the Windows.Forms Form Designer.
InitializeComponent()
' Initialize light and dark colors
' to the control's default values.
Me.lightColorValue = Me.ForeColor
Me.darkColorValue = Me.BackColor
Me.lightBrush = New SolidBrush(Me.lightColorValue)
Me.darkBrush = New SolidBrush(Me.darkColorValue)
End Sub 'New
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
#Region "IMarqueeWidget implementation"
Public Overridable Sub StartMarquee() _
Implements IMarqueeWidget.StartMarquee
' Start the updating thread and pass it the UpdatePeriod.
Me.backgroundWorker1.RunWorkerAsync(Me.UpdatePeriod)
End Sub
Public Overridable Sub StopMarquee() _
Implements IMarqueeWidget.StopMarquee
' Stop the updating thread.
Me.backgroundWorker1.CancelAsync()
End Sub
<Category("Marquee"), Browsable(True)> _
Public Property UpdatePeriod() As Integer _
Implements IMarqueeWidget.UpdatePeriod
Get
Return Me.updatePeriodValue
End Get
Set(ByVal Value As Integer)
If Value > 0 Then
Me.updatePeriodValue = Value
Else
Throw New ArgumentOutOfRangeException("UpdatePeriod", "must be > 0")
End If
End Set
End Property
#End Region
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
#Region "Public Properties"
<Category("Marquee"), Browsable(True)> _
Public Property LightColor() As Color
Get
Return Me.lightColorValue
End Get
Set(ByVal Value As Color)
' The LightColor property is only changed if the
' client provides a different value. Comparing values
' from the ToArgb method is the recommended test for
' equality between Color structs.
If Me.lightColorValue.ToArgb() <> Value.ToArgb() Then
Me.lightColorValue = Value
Me.lightBrush = New SolidBrush(Value)
End If
End Set
End Property
<Category("Marquee"), Browsable(True)> _
Public Property DarkColor() As Color
Get
Return Me.darkColorValue
End Get
Set(ByVal Value As Color)
' The DarkColor property is only changed if the
' client provides a different value. Comparing values
' from the ToArgb method is the recommended test for
' equality between Color structs.
If Me.darkColorValue.ToArgb() <> Value.ToArgb() Then
Me.darkColorValue = Value
Me.darkBrush = New SolidBrush(Value)
End If
End Set
End Property
#End Region
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
#Region "Implementation"
Protected Overrides Sub OnPaint(ByVal e As PaintEventArgs)
' The text is painted in the light or dark color,
' depending on the current value of isLit.
Me.ForeColor = IIf(Me.isLit, Me.lightColorValue, Me.darkColorValue)
MyBase.OnPaint(e)
End Sub
' This method is called in the worker thread's context,
' so it must not make any calls into the MarqueeText control.
' Instead, it communicates to the control using the
' ProgressChanged event.
'
' The only work done in this event handler is
' to sleep for the number of milliseconds specified
' by UpdatePeriod, then raise the ProgressChanged event.
Private Sub backgroundWorker1_DoWork( _
ByVal sender As Object, _
ByVal e As System.ComponentModel.DoWorkEventArgs) _
Handles backgroundWorker1.DoWork
Dim worker As BackgroundWorker = CType(sender, BackgroundWorker)
' This event handler will run until the client cancels
' the background task by calling CancelAsync.
While Not worker.CancellationPending
' The Argument property of the DoWorkEventArgs
' object holds the value of UpdatePeriod, which
' was passed as the argument to the RunWorkerAsync
' method.
Thread.Sleep(Fix(e.Argument))
' The DoWork eventhandler does not actually report
' progress; the ReportProgress event is used to
' periodically alert the control to update its state.
worker.ReportProgress(0)
End While
End Sub
' The ProgressChanged event is raised by the DoWork method.
' This event handler does work that is internal to the
' control. In this case, the text is toggled between its
' light and dark state, and the control is told to
' repaint itself.
Private Sub backgroundWorker1_ProgressChanged( _
ByVal sender As Object, _
ByVal e As System.ComponentModel.ProgressChangedEventArgs) _
Handles backgroundWorker1.ProgressChanged
Me.isLit = Not Me.isLit
Me.Refresh()
End Sub
Private Sub InitializeComponent()
Me.backgroundWorker1 = New System.ComponentModel.BackgroundWorker
'
' backgroundWorker1
'
Me.backgroundWorker1.WorkerReportsProgress = True
Me.backgroundWorker1.WorkerSupportsCancellation = True
End Sub
#End Region
End Class
using System;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.Diagnostics;
using System.Drawing;
using System.Threading;
using System.Windows.Forms;
using System.Windows.Forms.Design;
namespace MarqueeControlLibrary
{
[ToolboxItemFilter("MarqueeControlLibrary.MarqueeText", ToolboxItemFilterType.Require)]
public partial class MarqueeText : Label, IMarqueeWidget
{
// When isLit is true, the text is painted in the light color;
// When isLit is false, the text is painted in the dark color.
// This value changes whenever the BackgroundWorker component
// raises the ProgressChanged event.
private bool isLit = true;
// These fields back the public properties.
private int updatePeriodValue = 50;
private Color lightColorValue;
private Color darkColorValue;
// These brushes are used to paint the light and dark
// colors of the text.
private Brush lightBrush;
private Brush darkBrush;
// This component updates the control asynchronously.
private BackgroundWorker backgroundWorker1;
public MarqueeText()
{
// This call is required by the Windows.Forms Form Designer.
InitializeComponent();
// Initialize light and dark colors
// to the control's default values.
this.lightColorValue = this.ForeColor;
this.darkColorValue = this.BackColor;
this.lightBrush = new SolidBrush(this.lightColorValue);
this.darkBrush = new SolidBrush(this.darkColorValue);
}
///////////////////////////////////////////////////////////////////////
#region IMarqueeWidget implementation
public virtual void StartMarquee()
{
// Start the updating thread and pass it the UpdatePeriod.
this.backgroundWorker1.RunWorkerAsync(this.UpdatePeriod);
}
public virtual void StopMarquee()
{
// Stop the updating thread.
this.backgroundWorker1.CancelAsync();
}
[Category("Marquee")]
[Browsable(true)]
public int UpdatePeriod
{
get
{
return this.updatePeriodValue;
}
set
{
if (value > 0)
{
this.updatePeriodValue = value;
}
else
{
throw new ArgumentOutOfRangeException("UpdatePeriod", "must be > 0");
}
}
}
#endregion
///////////////////////////////////////////////////////////////////////
#region Public Properties
[Category("Marquee")]
[Browsable(true)]
public Color LightColor
{
get
{
return this.lightColorValue;
}
set
{
// The LightColor property is only changed if the
// client provides a different value. Comparing values
// from the ToArgb method is the recommended test for
// equality between Color structs.
if (this.lightColorValue.ToArgb() != value.ToArgb())
{
this.lightColorValue = value;
this.lightBrush = new SolidBrush(value);
}
}
}
[Category("Marquee")]
[Browsable(true)]
public Color DarkColor
{
get
{
return this.darkColorValue;
}
set
{
// The DarkColor property is only changed if the
// client provides a different value. Comparing values
// from the ToArgb method is the recommended test for
// equality between Color structs.
if (this.darkColorValue.ToArgb() != value.ToArgb())
{
this.darkColorValue = value;
this.darkBrush = new SolidBrush(value);
}
}
}
#endregion
///////////////////////////////////////////////////////////////////////
#region Implementation
protected override void OnPaint(PaintEventArgs e)
{
// The text is painted in the light or dark color,
// depending on the current value of isLit.
this.ForeColor =
this.isLit ? this.lightColorValue : this.darkColorValue;
base.OnPaint(e);
}
// This method is called in the worker thread's context,
// so it must not make any calls into the MarqueeText control.
// Instead, it communicates to the control using the
// ProgressChanged event.
//
// The only work done in this event handler is
// to sleep for the number of milliseconds specified
// by UpdatePeriod, then raise the ProgressChanged event.
private void backgroundWorker1_DoWork(
object sender,
System.ComponentModel.DoWorkEventArgs e)
{
BackgroundWorker worker = sender as BackgroundWorker;
// This event handler will run until the client cancels
// the background task by calling CancelAsync.
while (!worker.CancellationPending)
{
// The Argument property of the DoWorkEventArgs
// object holds the value of UpdatePeriod, which
// was passed as the argument to the RunWorkerAsync
// method.
Thread.Sleep((int)e.Argument);
// The DoWork eventhandler does not actually report
// progress; the ReportProgress event is used to
// periodically alert the control to update its state.
worker.ReportProgress(0);
}
}
// The ProgressChanged event is raised by the DoWork method.
// This event handler does work that is internal to the
// control. In this case, the text is toggled between its
// light and dark state, and the control is told to
// repaint itself.
private void backgroundWorker1_ProgressChanged(object sender, System.ComponentModel.ProgressChangedEventArgs e)
{
this.isLit = !this.isLit;
this.Refresh();
}
private void InitializeComponent()
{
this.backgroundWorker1 = new System.ComponentModel.BackgroundWorker();
//
// backgroundWorker1
//
this.backgroundWorker1.WorkerReportsProgress = true;
this.backgroundWorker1.WorkerSupportsCancellation = true;
this.backgroundWorker1.ProgressChanged += new System.ComponentModel.ProgressChangedEventHandler(this.backgroundWorker1_ProgressChanged);
this.backgroundWorker1.DoWork += new System.ComponentModel.DoWorkEventHandler(this.backgroundWorker1_DoWork);
}
#endregion
}
}
Imports System
Imports System.Collections
Imports System.ComponentModel
Imports System.ComponentModel.Design
Imports System.Drawing
Imports System.Windows.Forms
Imports System.Windows.Forms.Design
<Designer(GetType(MarqueeControlLibrary.Design.MarqueeControlRootDesigner), _
GetType(IRootDesigner))> _
Public Class MarqueeControl
Inherits UserControl
' Required designer variable.
Private components As System.ComponentModel.Container = Nothing
Public Sub New()
' This call is required by the Windows.Forms Form Designer.
InitializeComponent()
' Minimize flickering during animation by enabling
' double buffering.
SetStyle(ControlStyles.OptimizedDoubleBuffer, True)
End Sub
' <summary>
' Clean up any resources being used.
' </summary>
Protected Overrides Sub Dispose(ByVal disposing As Boolean)
If disposing Then
If (components IsNot Nothing) Then
components.Dispose()
End If
End If
MyBase.Dispose(disposing)
End Sub
Public Sub Start()
' The MarqueeControl may contain any number of
' controls that implement IMarqueeWidget, so
' find each IMarqueeWidget child and call its
' StartMarquee method.
Dim cntrl As Control
For Each cntrl In Me.Controls
If TypeOf cntrl Is IMarqueeWidget Then
Dim widget As IMarqueeWidget = CType(cntrl, IMarqueeWidget)
widget.StartMarquee()
End If
Next cntrl
End Sub
Public Sub [Stop]()
' The MarqueeControl may contain any number of
' controls that implement IMarqueeWidget, so find
' each IMarqueeWidget child and call its StopMarquee
' method.
Dim cntrl As Control
For Each cntrl In Me.Controls
If TypeOf cntrl Is IMarqueeWidget Then
Dim widget As IMarqueeWidget = CType(cntrl, IMarqueeWidget)
widget.StopMarquee()
End If
Next cntrl
End Sub
Protected Overrides Sub OnLayout(ByVal levent As LayoutEventArgs)
MyBase.OnLayout(levent)
' Repaint all IMarqueeWidget children if the layout
' has changed.
Dim cntrl As Control
For Each cntrl In Me.Controls
If TypeOf cntrl Is IMarqueeWidget Then
Dim widget As IMarqueeWidget = CType(cntrl, IMarqueeWidget)
cntrl.PerformLayout()
End If
Next cntrl
End Sub
#Region "Component Designer generated code"
' <summary>
' Required method for Designer support - do not modify
' the contents of this method with the code editor.
' </summary>
Private Sub InitializeComponent()
End Sub
#End Region
End Class
using System;
using System.Collections;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.Drawing;
using System.Windows.Forms;
using System.Windows.Forms.Design;
namespace MarqueeControlLibrary
{
[Designer( typeof( MarqueeControlLibrary.Design.MarqueeControlRootDesigner ), typeof( IRootDesigner ) )]
public class MarqueeControl : UserControl
{
// Required designer variable.
private System.ComponentModel.Container components = null;
public MarqueeControl()
{
// This call is required by the Windows.Forms Form Designer.
InitializeComponent();
// Minimize flickering during animation by enabling
// double buffering.
SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
}
/// <summary>
/// Clean up any resources being used.
/// </summary>
protected override void Dispose( bool disposing )
{
if( disposing )
{
if(components != null)
{
components.Dispose();
}
}
base.Dispose( disposing );
}
public void Start()
{
// The MarqueeControl may contain any number of
// controls that implement IMarqueeWidget, so
// find each IMarqueeWidget child and call its
// StartMarquee method.
foreach( Control cntrl in this.Controls )
{
if( cntrl is IMarqueeWidget )
{
IMarqueeWidget widget = cntrl as IMarqueeWidget;
widget.StartMarquee();
}
}
}
public void Stop()
{
// The MarqueeControl may contain any number of
// controls that implement IMarqueeWidget, so find
// each IMarqueeWidget child and call its StopMarquee
// method.
foreach( Control cntrl in this.Controls )
{
if( cntrl is IMarqueeWidget )
{
IMarqueeWidget widget = cntrl as IMarqueeWidget;
widget.StopMarquee();
}
}
}
protected override void OnLayout(LayoutEventArgs levent)
{
base.OnLayout (levent);
// Repaint all IMarqueeWidget children if the layout
// has changed.
foreach( Control cntrl in this.Controls )
{
if( cntrl is IMarqueeWidget )
{
Control control = cntrl as Control;
control.PerformLayout();
}
}
}
#region Component Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
components = new System.ComponentModel.Container();
}
#endregion
}
}
Imports System
Imports System.Collections
Imports System.ComponentModel
Imports System.Drawing
Imports System.Windows.Forms
Imports System.Windows.Forms.Design
' This control provides the custom UI for the LightShape property
' of the MarqueeBorder. It is used by the LightShapeEditor.
Public Class LightShapeSelectionControl
Inherits System.Windows.Forms.UserControl
Private lightShapeValue As MarqueeLightShape = MarqueeLightShape.Square
Private editorService As IWindowsFormsEditorService
Private squarePanel As System.Windows.Forms.Panel
Private circlePanel As System.Windows.Forms.Panel
' Required designer variable.
Private components As System.ComponentModel.Container = Nothing
' This constructor takes a MarqueeLightShape value from the
' design-time environment, which will be used to display
' the initial state.
Public Sub New( _
ByVal lightShape As MarqueeLightShape, _
ByVal editorService As IWindowsFormsEditorService)
' This call is required by the Windows.Forms Form Designer.
InitializeComponent()
' Cache the light shape value provided by the
' design-time environment.
Me.lightShapeValue = lightShape
' Cache the reference to the editor service.
Me.editorService = editorService
' Handle the Click event for the two panels.
AddHandler Me.squarePanel.Click, AddressOf squarePanel_Click
AddHandler Me.circlePanel.Click, AddressOf circlePanel_Click
End Sub
Protected Overrides Sub Dispose(ByVal disposing As Boolean)
If disposing Then
' Be sure to unhook event handlers
' to prevent "lapsed listener" leaks.
RemoveHandler Me.squarePanel.Click, AddressOf squarePanel_Click
RemoveHandler Me.circlePanel.Click, AddressOf circlePanel_Click
If (components IsNot Nothing) Then
components.Dispose()
End If
End If
MyBase.Dispose(disposing)
End Sub
' LightShape is the property for which this control provides
' a custom user interface in the Properties window.
Public Property LightShape() As MarqueeLightShape
Get
Return Me.lightShapeValue
End Get
Set(ByVal Value As MarqueeLightShape)
If Me.lightShapeValue <> Value Then
Me.lightShapeValue = Value
End If
End Set
End Property
Protected Overrides Sub OnPaint(ByVal e As PaintEventArgs)
MyBase.OnPaint(e)
Dim gCircle As Graphics = Me.circlePanel.CreateGraphics()
Try
Dim gSquare As Graphics = Me.squarePanel.CreateGraphics()
Try
' Draw a filled square in the client area of
' the squarePanel control.
gSquare.FillRectangle( _
Brushes.Red, _
0, _
0, _
Me.squarePanel.Width, _
Me.squarePanel.Height)
' If the Square option has been selected, draw a
' border inside the squarePanel.
If Me.lightShapeValue = MarqueeLightShape.Square Then
gSquare.DrawRectangle( _
Pens.Black, _
0, _
0, _
Me.squarePanel.Width - 1, _
Me.squarePanel.Height - 1)
End If
' Draw a filled circle in the client area of
' the circlePanel control.
gCircle.Clear(Me.circlePanel.BackColor)
gCircle.FillEllipse( _
Brushes.Blue, _
0, _
0, _
Me.circlePanel.Width, _
Me.circlePanel.Height)
' If the Circle option has been selected, draw a
' border inside the circlePanel.
If Me.lightShapeValue = MarqueeLightShape.Circle Then
gCircle.DrawRectangle( _
Pens.Black, _
0, _
0, _
Me.circlePanel.Width - 1, _
Me.circlePanel.Height - 1)
End If
Finally
gSquare.Dispose()
End Try
Finally
gCircle.Dispose()
End Try
End Sub
Private Sub squarePanel_Click( _
ByVal sender As Object, _
ByVal e As EventArgs)
Me.lightShapeValue = MarqueeLightShape.Square
Me.Invalidate(False)
Me.editorService.CloseDropDown()
End Sub
Private Sub circlePanel_Click( _
ByVal sender As Object, _
ByVal e As EventArgs)
Me.lightShapeValue = MarqueeLightShape.Circle
Me.Invalidate(False)
Me.editorService.CloseDropDown()
End Sub
#Region "Component Designer generated code"
'/ <summary>
'/ Required method for Designer support - do not modify
'/ the contents of this method with the code editor.
'/ </summary>
Private Sub InitializeComponent()
Me.squarePanel = New System.Windows.Forms.Panel
Me.circlePanel = New System.Windows.Forms.Panel
Me.SuspendLayout()
'
' squarePanel
'
Me.squarePanel.Location = New System.Drawing.Point(8, 10)
Me.squarePanel.Name = "squarePanel"
Me.squarePanel.Size = New System.Drawing.Size(60, 60)
Me.squarePanel.TabIndex = 2
'
' circlePanel
'
Me.circlePanel.Location = New System.Drawing.Point(80, 10)
Me.circlePanel.Name = "circlePanel"
Me.circlePanel.Size = New System.Drawing.Size(60, 60)
Me.circlePanel.TabIndex = 3
'
' LightShapeSelectionControl
'
Me.Controls.Add(squarePanel)
Me.Controls.Add(circlePanel)
Me.Name = "LightShapeSelectionControl"
Me.Size = New System.Drawing.Size(150, 80)
Me.ResumeLayout(False)
End Sub
#End Region
End Class
using System;
using System.Collections;
using System.ComponentModel;
using System.Drawing;
using System.Windows.Forms;
using System.Windows.Forms.Design;
namespace MarqueeControlLibrary
{
// This control provides the custom UI for the LightShape property
// of the MarqueeBorder. It is used by the LightShapeEditor.
public class LightShapeSelectionControl : System.Windows.Forms.UserControl
{
private MarqueeLightShape lightShapeValue = MarqueeLightShape.Square;
private IWindowsFormsEditorService editorService = null;
private System.Windows.Forms.Panel squarePanel;
private System.Windows.Forms.Panel circlePanel;
// Required designer variable.
private System.ComponentModel.Container components = null;
// This constructor takes a MarqueeLightShape value from the
// design-time environment, which will be used to display
// the initial state.
public LightShapeSelectionControl(
MarqueeLightShape lightShape,
IWindowsFormsEditorService editorService )
{
// This call is required by the designer.
InitializeComponent();
// Cache the light shape value provided by the
// design-time environment.
this.lightShapeValue = lightShape;
// Cache the reference to the editor service.
this.editorService = editorService;
// Handle the Click event for the two panels.
this.squarePanel.Click += new EventHandler(squarePanel_Click);
this.circlePanel.Click += new EventHandler(circlePanel_Click);
}
protected override void Dispose( bool disposing )
{
if( disposing )
{
// Be sure to unhook event handlers
// to prevent "lapsed listener" leaks.
this.squarePanel.Click -=
new EventHandler(squarePanel_Click);
this.circlePanel.Click -=
new EventHandler(circlePanel_Click);
if(components != null)
{
components.Dispose();
}
}
base.Dispose( disposing );
}
// LightShape is the property for which this control provides
// a custom user interface in the Properties window.
public MarqueeLightShape LightShape
{
get
{
return this.lightShapeValue;
}
set
{
if( this.lightShapeValue != value )
{
this.lightShapeValue = value;
}
}
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint (e);
using(
Graphics gSquare = this.squarePanel.CreateGraphics(),
gCircle = this.circlePanel.CreateGraphics() )
{
// Draw a filled square in the client area of
// the squarePanel control.
gSquare.FillRectangle(
Brushes.Red,
0,
0,
this.squarePanel.Width,
this.squarePanel.Height
);
// If the Square option has been selected, draw a
// border inside the squarePanel.
if( this.lightShapeValue == MarqueeLightShape.Square )
{
gSquare.DrawRectangle(
Pens.Black,
0,
0,
this.squarePanel.Width-1,
this.squarePanel.Height-1);
}
// Draw a filled circle in the client area of
// the circlePanel control.
gCircle.Clear( this.circlePanel.BackColor );
gCircle.FillEllipse(
Brushes.Blue,
0,
0,
this.circlePanel.Width,
this.circlePanel.Height
);
// If the Circle option has been selected, draw a
// border inside the circlePanel.
if( this.lightShapeValue == MarqueeLightShape.Circle )
{
gCircle.DrawRectangle(
Pens.Black,
0,
0,
this.circlePanel.Width-1,
this.circlePanel.Height-1);
}
}
}
private void squarePanel_Click(object sender, EventArgs e)
{
this.lightShapeValue = MarqueeLightShape.Square;
this.Invalidate( false );
this.editorService.CloseDropDown();
}
private void circlePanel_Click(object sender, EventArgs e)
{
this.lightShapeValue = MarqueeLightShape.Circle;
this.Invalidate( false );
this.editorService.CloseDropDown();
}
#region Component Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.squarePanel = new System.Windows.Forms.Panel();
this.circlePanel = new System.Windows.Forms.Panel();
this.SuspendLayout();
//
// squarePanel
//
this.squarePanel.Location = new System.Drawing.Point(8, 10);
this.squarePanel.Name = "squarePanel";
this.squarePanel.Size = new System.Drawing.Size(60, 60);
this.squarePanel.TabIndex = 2;
//
// circlePanel
//
this.circlePanel.Location = new System.Drawing.Point(80, 10);
this.circlePanel.Name = "circlePanel";
this.circlePanel.Size = new System.Drawing.Size(60, 60);
this.circlePanel.TabIndex = 3;
//
// LightShapeSelectionControl
//
this.Controls.Add(this.squarePanel);
this.Controls.Add(this.circlePanel);
this.Name = "LightShapeSelectionControl";
this.Size = new System.Drawing.Size(150, 80);
this.ResumeLayout(false);
}
#endregion
}
}
Imports System
Imports System.Collections
Imports System.ComponentModel
Imports System.ComponentModel.Design
Imports System.Diagnostics
Imports System.Windows.Forms
Imports System.Windows.Forms.Design
Namespace MarqueeControlLibrary.Design
<System.Security.Permissions.PermissionSetAttribute(System.Security.Permissions.SecurityAction.Demand, Name:="FullTrust")> _
Public Class MarqueeBorderDesigner
Inherits ParentControlDesigner
Public Sub New()
Trace.WriteLine("MarqueeBorderDesigner")
End Sub
Public Property Visible() As Boolean
Get
Return CBool(ShadowProperties("Visible"))
End Get
Set(ByVal Value As Boolean)
Me.ShadowProperties("Visible") = Value
End Set
End Property
Public Property Enabled() As Boolean
Get
Return CBool(ShadowProperties("Enabled"))
End Get
Set(ByVal Value As Boolean)
Me.ShadowProperties("Enabled") = Value
End Set
End Property
Protected Overrides Sub PreFilterProperties( _
ByVal properties As IDictionary)
MyBase.PreFilterProperties(properties)
If properties.Contains("Padding") Then
properties.Remove("Padding")
End If
properties("Visible") = _
TypeDescriptor.CreateProperty(GetType(MarqueeBorderDesigner), _
CType(properties("Visible"), PropertyDescriptor), _
New Attribute(-1) {})
properties("Enabled") = _
TypeDescriptor.CreateProperty(GetType(MarqueeBorderDesigner), _
CType(properties("Enabled"), _
PropertyDescriptor), _
New Attribute(-1) {})
End Sub
Private Sub OnVerbRunTest( _
ByVal sender As Object, _
ByVal e As EventArgs)
Dim widget As IMarqueeWidget = CType(Me.Control, IMarqueeWidget)
widget.StartMarquee()
End Sub
Private Sub OnVerbStopTest( _
ByVal sender As Object, _
ByVal e As EventArgs)
Dim widget As IMarqueeWidget = CType(Me.Control, IMarqueeWidget)
widget.StopMarquee()
End Sub
End Class
End Namespace
using System;
using System.Collections;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.Diagnostics;
using System.Windows.Forms;
using System.Windows.Forms.Design;
namespace MarqueeControlLibrary.Design
{
[System.Security.Permissions.PermissionSet(System.Security.Permissions.SecurityAction.Demand, Name = "FullTrust")]
public class MarqueeBorderDesigner : ParentControlDesigner
{
public MarqueeBorderDesigner()
{
Trace.WriteLine("MarqueeBorderDesigner");
}
public bool Visible
{
get
{
return (bool)ShadowProperties["Visible"];
}
set
{
this.ShadowProperties["Visible"] = value;
}
}
public bool Enabled
{
get
{
return (bool)ShadowProperties["Enabled"];
}
set
{
this.ShadowProperties["Enabled"] = value;
}
}
protected override void PreFilterProperties(IDictionary properties)
{
base.PreFilterProperties(properties);
if (properties.Contains("Padding"))
{
properties.Remove("Padding");
}
properties["Visible"] = TypeDescriptor.CreateProperty(
typeof(MarqueeBorderDesigner),
(PropertyDescriptor)properties["Visible"],
new Attribute[0]);
properties["Enabled"] = TypeDescriptor.CreateProperty(
typeof(MarqueeBorderDesigner),
(PropertyDescriptor)properties["Enabled"],
new Attribute[0]);
}
private void OnVerbRunTest(object sender, EventArgs e)
{
IMarqueeWidget widget = this.Control as IMarqueeWidget;
widget.StartMarquee();
}
private void OnVerbStopTest(object sender, EventArgs e)
{
IMarqueeWidget widget = this.Control as IMarqueeWidget;
widget.StopMarquee();
}
}
}
Imports System
Imports System.Collections
Imports System.ComponentModel
Imports System.ComponentModel.Design
Imports System.Diagnostics
Imports System.Drawing.Design
Imports System.Windows.Forms
Imports System.Windows.Forms.Design
Namespace MarqueeControlLibrary.Design
<ToolboxItemFilter("MarqueeControlLibrary.MarqueeBorder", _
ToolboxItemFilterType.Require), _
ToolboxItemFilter("MarqueeControlLibrary.MarqueeText", _
ToolboxItemFilterType.Require)> _
<System.Security.Permissions.PermissionSetAttribute(System.Security.Permissions.SecurityAction.Demand, Name:="FullTrust")> _
Public Class MarqueeControlRootDesigner
Inherits DocumentDesigner
Public Sub New()
Trace.WriteLine("MarqueeControlRootDesigner ctor")
End Sub
Public Overrides Sub Initialize(ByVal component As IComponent)
MyBase.Initialize(component)
Dim cs As IComponentChangeService = _
CType(GetService(GetType(IComponentChangeService)), _
IComponentChangeService)
If (cs IsNot Nothing) Then
AddHandler cs.ComponentChanged, AddressOf OnComponentChanged
End If
Me.Verbs.Add(New DesignerVerb("Run Test", _
New EventHandler(AddressOf OnVerbRunTest)))
Me.Verbs.Add(New DesignerVerb("Stop Test", _
New EventHandler(AddressOf OnVerbStopTest)))
End Sub
Private Sub OnComponentChanged( _
ByVal sender As Object, _
ByVal e As ComponentChangedEventArgs)
If TypeOf e.Component Is IMarqueeWidget Then
Me.Control.Refresh()
End If
End Sub
Private Sub OnVerbRunTest( _
ByVal sender As Object, _
ByVal e As EventArgs)
Dim c As MarqueeControl = CType(Me.Control, MarqueeControl)
c.Start()
End Sub
Private Sub OnVerbStopTest( _
ByVal sender As Object, _
ByVal e As EventArgs)
Dim c As MarqueeControl = CType(Me.Control, MarqueeControl)
c.Stop()
End Sub
End Class
End Namespace
using System;
using System.Collections;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.Diagnostics;
using System.Drawing.Design;
using System.Windows.Forms;
using System.Windows.Forms.Design;
namespace MarqueeControlLibrary.Design
{
[ToolboxItemFilter("MarqueeControlLibrary.MarqueeBorder", ToolboxItemFilterType.Require)]
[ToolboxItemFilter("MarqueeControlLibrary.MarqueeText", ToolboxItemFilterType.Require)]
[System.Security.Permissions.PermissionSet(System.Security.Permissions.SecurityAction.Demand, Name = "FullTrust")]
public class MarqueeControlRootDesigner : DocumentDesigner
{
public MarqueeControlRootDesigner()
{
Trace.WriteLine("MarqueeControlRootDesigner ctor");
}
public override void Initialize(IComponent component)
{
base.Initialize(component);
IComponentChangeService cs =
GetService(typeof(IComponentChangeService))
as IComponentChangeService;
if (cs != null)
{
cs.ComponentChanged +=
new ComponentChangedEventHandler(OnComponentChanged);
}
this.Verbs.Add(
new DesignerVerb("Run Test",
new EventHandler(OnVerbRunTest))
);
this.Verbs.Add(
new DesignerVerb("Stop Test",
new EventHandler(OnVerbStopTest))
);
}
private void OnComponentChanged(
object sender,
ComponentChangedEventArgs e)
{
if (e.Component is IMarqueeWidget)
{
this.Control.Refresh();
}
}
private void OnVerbRunTest(object sender, EventArgs e)
{
MarqueeControl c = this.Control as MarqueeControl;
c.Start();
}
private void OnVerbStopTest(object sender, EventArgs e)
{
MarqueeControl c = this.Control as MarqueeControl;
c.Stop();
}
}
}
Компиляция кода
Создайте проект библиотеки элементов управления Windows для размещения этих файлов. Назовите проект "MarqueeControlLibrary".
Для проекта "MarqueeControlLibrary" потребуются ссылки на сборки "System.Design", "System.Drawing" и "System.Windows.Forms".