Notitie
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen u aan te melden of de directory te wijzigen.
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen de mappen te wijzigen.
De ontwerptijdservaring voor een aangepast besturingselement kan worden verbeterd door een bijbehorende aangepaste ontwerper te creëren.
Voorzichtigheid
Deze inhoud is geschreven voor .NET Framework. Als u .NET 6 of een latere versie gebruikt, gebruikt u deze inhoud voorzichtig. Het ontwerpsysteem is gewijzigd voor Windows Forms en het is belangrijk dat u de Designer-wijzigingen bekijkt sinds .NET Framework artikel.
In dit artikel wordt uitgelegd hoe u een aangepaste ontwerpfunctie maakt voor een aangepast besturingselement. U implementeert een MarqueeControl
type en een bijbehorende ontwerpklasse met de naam MarqueeControlRootDesigner
.
Het MarqueeControl
type implementeert een display dat vergelijkbaar is met een theaterkader met geanimeerde lichten en knipperende tekst.
De ontwerper voor dit besturingselement communiceert met de ontwerpomgeving om tijdens het ontwerpen een aangepaste ontwerpervaring te bieden. Met de aangepaste ontwerpfunctie kunt u een aangepaste MarqueeControl
implementatie samenstellen met geanimeerde lichten en knipperende tekst in veel combinaties. U kunt het samengestelde besturingselement op een formulier zoals elk ander Besturingselement voor Windows Forms gebruiken.
Wanneer u klaar bent met deze handleiding, ziet uw aangepaste besturingselement er ongeveer als volgt uit:
Zie Procedure: Een Windows Forms-besturingselement maken dat gebruikmaakt van Design-Time functiesvoor de volledige lijst met code.
Voorwaarden
Als u dit scenario wilt voltooien, hebt u Visual Studio nodig.
Het project maken
De eerste stap is het maken van het toepassingsproject. U gebruikt dit project om de toepassing te bouwen die als host fungeert voor het aangepaste besturingselement.
Maak in Visual Studio een nieuw Windows Forms-toepassingsproject en noem het MarqueeControlTest.
Het besturingselementbibliotheekproject maken
Voeg een Windows Forms-besturingselementbibliotheekproject toe aan de oplossing. Geef het project de naam MarqueeControlLibrary.
Verwijder met Solution Explorerhet standaardbeheer van het project door het bronbestand met de naam 'UserControl1.cs' of 'UserControl1.vb' te verwijderen, afhankelijk van uw gewenste taal.
Voeg een nieuw UserControl-item toe aan het
MarqueeControlLibrary
project. Geef het nieuwe bronbestand een basisnaam van MarqueeControl.Maak met Solution Explorereen nieuwe map in het
MarqueeControlLibrary
project.Klik met de rechtermuisknop op de map Ontwerp en voeg een nieuwe klasse toe. Geef de naam MarqueeControlRootDesigner.
U moet typen uit de System.Design assembly gebruiken, dus voeg deze verwijzing toe aan het
MarqueeControlLibrary
project.
Verwijs naar het project voor aangepaste besturingselementen.
U gebruikt het project MarqueeControlTest
om de aangepaste controle te testen. Het testproject wordt op de hoogte van het aangepaste besturingselement wanneer u een projectreferentie toevoegt aan de MarqueeControlLibrary
assembly.
Voeg in het MarqueeControlTest
project een projectreferentie toe aan de MarqueeControlLibrary
assembly. Zorg ervoor dat u het tabblad Projecten in het dialoogvenster Verwijzing toevoegen gebruikt in plaats van rechtstreeks naar de MarqueeControlLibrary
assembly te verwijzen.
Een aangepaste controle en zijn aangepaste ontwerper definiëren
Je aangepaste besturing wordt afgeleid van de UserControl-klasse. Hierdoor kan uw besturingselement andere besturingselementen bevatten en biedt uw besturingselement veel standaardfunctionaliteit.
Uw aangepaste besturingselement heeft een bijbehorende aangepaste ontwerper. Hiermee kunt u een unieke ontwerpervaring maken die speciaal is afgestemd op uw aangepaste controle.
U koppelt het besturingselement aan de ontwerpfunctie met behulp van de DesignerAttribute-klasse. Omdat u het volledige ontwerpgedrag tijdens het ontwerpen van uw aangepaste controle ontwikkelt, implementeert de aangepaste ontwerper de interface IRootDesigner.
Om een aangepast besturingselement en de bijbehorende aangepaste ontwerper te definiëren
Open het
MarqueeControl
bronbestand in de Code-editor. Importeer bovenaan het bestand de volgende naamruimten:using System; using System.Collections; using System.ComponentModel; using System.ComponentModel.Design; using System.Drawing; using System.Windows.Forms; using System.Windows.Forms.Design;
Imports System.Collections Imports System.ComponentModel Imports System.ComponentModel.Design Imports System.Drawing Imports System.Windows.Forms Imports System.Windows.Forms.Design
Voeg de DesignerAttribute toe aan de
MarqueeControl
klassedeclaratie. Hiermee koppelt u het aangepaste besturingselement aan de ontwerper.[Designer( typeof( MarqueeControlLibrary.Design.MarqueeControlRootDesigner ), typeof( IRootDesigner ) )] public class MarqueeControl : UserControl {
<Designer(GetType(MarqueeControlLibrary.Design.MarqueeControlRootDesigner), _ GetType(IRootDesigner))> _ Public Class MarqueeControl Inherits UserControl
Open het
MarqueeControlRootDesigner
bronbestand in de Code-editor. Importeer bovenaan het bestand de volgende naamruimten: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;
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
Wijzig de declaratie van
MarqueeControlRootDesigner
om over te nemen van de DocumentDesigner-klasse. Pas de ToolboxItemFilterAttribute toe om de interactie van de ontwerper met de Gereedschapskistte specificeren.Notitie
De definitie voor de klasse
MarqueeControlRootDesigner
is ingesloten in een naamruimte met de naam MarqueeControlLibrary.Design. Deze declaratie plaatst de ontwerper in een speciale naamruimte die is gereserveerd voor ontwerpgerelateerde typen.namespace MarqueeControlLibrary.Design { [ToolboxItemFilter("MarqueeControlLibrary.MarqueeBorder", ToolboxItemFilterType.Require)] [ToolboxItemFilter("MarqueeControlLibrary.MarqueeText", ToolboxItemFilterType.Require)] public class MarqueeControlRootDesigner : DocumentDesigner {
Namespace MarqueeControlLibrary.Design <ToolboxItemFilter("MarqueeControlLibrary.MarqueeBorder", _ ToolboxItemFilterType.Require), _ ToolboxItemFilter("MarqueeControlLibrary.MarqueeText", _ ToolboxItemFilterType.Require)> _ Public Class MarqueeControlRootDesigner Inherits DocumentDesigner
Definieer de constructor voor de klasse
MarqueeControlRootDesigner
. Voeg een WriteLine instructie in de hoofdtekst van de constructor in. Dit is handig voor foutopsporing.public MarqueeControlRootDesigner() { Trace.WriteLine("MarqueeControlRootDesigner ctor"); }
Public Sub New() Trace.WriteLine("MarqueeControlRootDesigner ctor") End Sub
Een exemplaar van uw aangepaste besturingselement maken
Voeg een nieuw UserControl-item toe aan het
MarqueeControlTest
project. Geef het nieuwe bronbestand een basisnaam van DemoMarqueeControl-.Open het
DemoMarqueeControl
bestand in de Code-editor. Importeer bovenaan het bestand deMarqueeControlLibrary
naamruimte:Imports MarqueeControlLibrary
using MarqueeControlLibrary;
Wijzig de declaratie van
DemoMarqueeControl
om over te nemen van deMarqueeControl
-klasse.Bouw het project.
Open Form1 in de Windows Forms-ontwerper.
Zoek het tabblad MarqueeControlTest Components in de Werkset en open het. Sleep een
DemoMarqueeControl
van de Werkset naar het formulier.Bouw het project.
Het project instellen voor Design-Time foutopsporing
Wanneer u een aangepaste ontwerptijdervaring ontwikkelt, moet u fouten opsporen in uw besturingselementen en onderdelen. Er is een eenvoudige manier om uw project in te stellen om foutopsporing tijdens het ontwerp mogelijk te maken. Voor meer informatie, zie Walkthrough: Foutopsporing van aangepaste Windows Forms Controls tijdens het ontwerpen.
Klik met de rechtermuisknop op het
MarqueeControlLibrary
project en selecteer Eigenschappen.Selecteer in het dialoogvenster MarqueeControlLibrary Property Pages de Debug pagina.
Selecteer in de sectie Start actie de optie Extern programma starten. U gaat foutopsporing uitvoeren van een aparte instantie van Visual Studio. Klik daarom op het beletselteken (
) om naar de Visual Studio IDE te bladeren. De naam van het uitvoerbare bestand wordt devenv.exeen als u op de standaardlocatie hebt geïnstalleerd, is het pad %ProgramFiles(x86)%\Microsoft Visual Studio\2019\<-editie>\Common7\IDE\devenv.exe.
Selecteer OK- om het dialoogvenster te sluiten.
Klik met de rechtermuisknop op het project MarqueeControlLibrary en selecteer Instellen als opstartproject om deze foutopsporingsconfiguratie in te schakelen.
Doorlaatpost
U bent nu klaar om het ontwerptijdgedrag van uw aangepaste controle-element te debuggen. Zodra u hebt vastgesteld dat de foutopsporingsomgeving correct is ingesteld, test u de koppeling tussen het aangepaste besturingselement en de aangepaste ontwerpfunctie.
Om de foutopsporingsomgeving en de ontwerpkoppeling te testen
Open het bronbestand MarqueeControlRootDesigner in de Code-editor en plaats een onderbrekingspunt op de WriteLine-instructie.
Druk op F5- om de foutopsporingssessie te starten.
Er wordt een nieuw exemplaar van Visual Studio gemaakt.
Open in het nieuwe exemplaar van Visual Studio de oplossing MarqueeControlTest. U kunt de oplossing eenvoudig vinden door recente projecten te selecteren in het menu Bestand. Het MarqueeControlTest.sln oplossingsbestand wordt vermeld als het meest recent gebruikte bestand.
Open de
DemoMarqueeControl
in de ontwerpfunctie.De foutopsporingsinstantie van Visual Studio krijgt de focus en de uitvoering stopt bij je onderbrekingspunt. Druk op F5- om door te gaan met de foutopsporingssessie.
Op dit moment is alles aanwezig om uw aangepaste besturingselement en de bijbehorende aangepaste ontwerpfunctie te ontwikkelen en er fouten in op te sporen. De rest van dit artikel is gericht op de details van het implementeren van functies van het besturingselement en de ontwerper.
Het aangepaste besturingselement implementeren
De MarqueeControl
is een UserControl met een beetje aanpassing. Er worden twee methoden weergegeven: Start
, waarmee de animatie wordt gestart en Stop
, waardoor de animatie wordt gestopt. Omdat de MarqueeControl
onderliggende besturingselementen bevat die de IMarqueeWidget
-interface implementeren, Start
en Stop
elk onderliggend besturingselement opsommen en respectievelijk de methoden StartMarquee
en StopMarquee
aanroepen op elk onderliggend besturingselement dat IMarqueeWidget
implementeert.
Het uiterlijk van de besturingselementen MarqueeBorder
en MarqueeText
is afhankelijk van de indeling, dus overschrijft MarqueeControl
de methode OnLayout en roept PerformLayout aan op onderliggende besturingselementen van dit type.
Dit is de omvang van de MarqueeControl
aanpassingen. De runtimefuncties worden geïmplementeerd door de besturingselementen MarqueeBorder
en MarqueeText
en de ontwerpfuncties worden geïmplementeerd door de MarqueeBorderDesigner
- en MarqueeControlRootDesigner
klassen.
Uw aangepaste besturingselement implementeren
Open het
MarqueeControl
bronbestand in de Code-editor. Implementeer de methodenStart
enStop
.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(); } } }
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
Overschrijf de methode OnLayout.
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(); } } }
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
Maak een kindcontrole voor uw aangepaste controle
De MarqueeControl
zal twee soorten onderliggende besturingselementen hosten: het besturingselement MarqueeBorder
en het besturingselement MarqueeText
.
MarqueeBorder
: Met dit controle-element wordt een rand van 'lichten' rond de randen weergegeven. De lichten knipperen in een volgorde, zodat ze over de omtrek lijken te bewegen. De snelheid waarmee de lichten knipperen wordt geregeld door een eigenschap genaamdUpdatePeriod
. Verschillende andere aangepaste eigenschappen bepalen andere aspecten van het uiterlijk van het besturingselement. Twee methoden,StartMarquee
enStopMarquee
genoemd, bepalen wanneer de animatie wordt gestart en gestopt.MarqueeText
: Deze controle toont een knipperende tekst. Net als hetMarqueeBorder
besturingselement wordt de snelheid waarmee de tekst knippert, bepaald door de eigenschapUpdatePeriod
. HetMarqueeText
besturingselement heeft ook deStartMarquee
- enStopMarquee
methoden die gemeenschappelijk zijn met het besturingselementMarqueeBorder
.
Tijdens het ontwerp kunnen deze MarqueeControlRootDesigner
twee besturingstypen worden toegevoegd aan een MarqueeControl
in elke combinatie.
Algemene functies van de twee besturingselementen worden in een interface met de naam IMarqueeWidget
meegerekend. Hierdoor kan de MarqueeControl
alle aan Marquee gerelateerde kindercontroles ontdekken en hen een speciale behandeling geven.
Als u de periodieke animatiefunctie wilt implementeren, gebruikt u BackgroundWorker objecten uit de System.ComponentModel naamruimte. U kunt Timer objecten gebruiken, maar als er veel IMarqueeWidget
objecten aanwezig zijn, kan de enkele UI-thread de animatie mogelijk niet bijhouden.
Om een onderliggend controle-element te maken voor uw aangepaste besturingselement
Voeg een nieuw klasse-item toe aan het
MarqueeControlLibrary
project. Geef het nieuwe bronbestand een basisnaam van 'IMarqueeWidget'.Open het
IMarqueeWidget
bronbestand in de Code-editor en wijzig de declaratie vanclass
ininterface
:// This interface defines the contract for any class that is to // be used in constructing a MarqueeControl. public interface IMarqueeWidget {
' This interface defines the contract for any class that is to ' be used in constructing a MarqueeControl. Public Interface IMarqueeWidget
Voeg de volgende code toe aan de
IMarqueeWidget
-interface om twee methoden en een eigenschap beschikbaar te maken die de animatie van de marqueetekst bewerken.// 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; } }
' 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
Voeg een nieuw aangepast besturingselement toe aan het
MarqueeControlLibrary
project. Geef het nieuwe bronbestand een basisnaam van 'MarqueeText'.Sleep een BackgroundWorker-component van de Gereedschapskist naar uw
MarqueeText
-controle. Met dit onderdeel kan hetMarqueeText
besturingselement zichzelf asynchroon bijwerken.Stel in het venster Eigenschappen de eigenschappen van het BackgroundWorker onderdeel
WorkerReportsProgress
en WorkerSupportsCancellation in op true. Met deze instellingen kan het BackgroundWorker onderdeel periodiek de ProgressChanged gebeurtenis genereren en asynchrone updates annuleren.Zie BackgroundWorker Componentvoor meer informatie.
Open het
MarqueeText
bronbestand in de Code-editor. Importeer bovenaan het bestand de volgende naamruimten: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;
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
Wijzig de declaratie van
MarqueeText
om over te nemen van Label en deIMarqueeWidget
-interface te implementeren:[ToolboxItemFilter("MarqueeControlLibrary.MarqueeText", ToolboxItemFilterType.Require)] public partial class MarqueeText : Label, IMarqueeWidget {
<ToolboxItemFilter("MarqueeControlLibrary.MarqueeText", _ ToolboxItemFilterType.Require)> _ Partial Public Class MarqueeText Inherits Label Implements IMarqueeWidget
Declareer de instantievariabelen die overeenkomen met de weergegeven eigenschappen en initialiseer deze in de constructor. Het
isLit
veld bepaalt of de tekst moet worden geschilderd in de kleur die wordt opgegeven door de eigenschapLightColor
.// 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); }
' 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
Implementeer de
IMarqueeWidget
-interface.Met de methoden
StartMarquee
enStopMarquee
worden de RunWorkerAsync en CancelAsync methoden van het BackgroundWorker onderdeel aangeroepen om de animatie te starten en te stoppen.De kenmerken Category en Browsable worden toegepast op de eigenschap
UpdatePeriod
, zodat deze wordt weergegeven in een aangepaste sectie van het venster Eigenschappen met de naam 'Marquee'.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"); } } }
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
Implementeer de eigenschapstoegangen. U maakt twee eigenschappen beschikbaar voor clients:
LightColor
enDarkColor
. De kenmerken Category en Browsable worden toegepast op deze eigenschappen, zodat de eigenschappen worden weergegeven in een aangepaste sectie van het venster Eigenschappen met de naam 'Marquee'.[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 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
Implementeer de handlers voor de DoWork- en ProgressChanged-gebeurtenissen van het BackgroundWorker onderdeel.
De DoWork gebeurtenis-handler pauzeert voor het aantal milliseconden dat door
UpdatePeriod
is opgegeven, roept vervolgens de ProgressChanged gebeurtenis aan totdat je code de animatie stopt door CancelAsyncaan te roepen.De ProgressChanged gebeurtenis-handler schakelt de tekst tussen de lichte en donkere toestand om het uiterlijk van knipperen te geven.
// 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(); }
' 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
Overschrijf de methode OnPaint om de animatie in te schakelen.
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); }
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
Druk op F6- om de oplossing te bouwen.
Het kindbesturingselement MarqueeBorder maken
Het MarqueeBorder
besturingselement is iets geavanceerder dan het MarqueeText
besturingselement. Het heeft meer eigenschappen en de animatie in de OnPaint methode is meer betrokken. In principe lijkt het op de MarqueeText
controle.
Omdat het besturingselement MarqueeBorder
onderliggende besturingselementen kan hebben, moet het rekening houden met Layout gebeurtenissen.
Het besturingselement MarqueeBorder maken
Voeg een nieuw aangepast besturingselement toe aan het
MarqueeControlLibrary
project. Geef het nieuwe bronbestand een basisnaam van 'MarqueeBorder'.Sleep een BackgroundWorker onderdeel van de Werkbalk naar uw
MarqueeBorder
controle. Met dit onderdeel kan hetMarqueeBorder
besturingselement zichzelf asynchroon bijwerken.Stel in het venster Eigenschappen de eigenschappen van het BackgroundWorker onderdeel
WorkerReportsProgress
en WorkerSupportsCancellation in op true. Met deze instellingen kan het BackgroundWorker onderdeel periodiek de ProgressChanged gebeurtenis genereren en asynchrone updates annuleren. Zie BackgroundWorker Componentvoor meer informatie.Selecteer in het venster Eigenschappen de knop Gebeurtenissen. Koppel handlers voor de gebeurtenissen DoWork en ProgressChanged.
Open het
MarqueeBorder
bronbestand in de Code-editor. Importeer bovenaan het bestand de volgende naamruimten: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;
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
Wijzig de declaratie van
MarqueeBorder
om over te nemen van Panel en deIMarqueeWidget
-interface te implementeren.[Designer(typeof(MarqueeControlLibrary.Design.MarqueeBorderDesigner ))] [ToolboxItemFilter("MarqueeControlLibrary.MarqueeBorder", ToolboxItemFilterType.Require)] public partial class MarqueeBorder : Panel, IMarqueeWidget {
<Designer(GetType(MarqueeControlLibrary.Design.MarqueeBorderDesigner)), _ ToolboxItemFilter("MarqueeControlLibrary.MarqueeBorder", _ ToolboxItemFilterType.Require)> _ Partial Public Class MarqueeBorder Inherits Panel Implements IMarqueeWidget
Declareer twee opsommingen voor het beheren van de status van de
MarqueeBorder
controle:MarqueeSpinDirection
, die de richting bepaalt waarin de lichten rond de rand draaien enMarqueeLightShape
, die de vorm van de lichten (vierkant of cirkelvormig) bepaalt. Plaats deze verklaringen vóór deMarqueeBorder
klassedeclaratie.// 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 }
' 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
Declareer de instantievariabelen die overeenkomen met de weergegeven eigenschappen en initialiseer deze in de constructor.
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); }
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
Implementeer de
IMarqueeWidget
-interface.Met de methoden
StartMarquee
enStopMarquee
worden de RunWorkerAsync en CancelAsync methoden van het BackgroundWorker onderdeel aangeroepen om de animatie te starten en te stoppen.Omdat het besturingselement
MarqueeBorder
onderliggende besturingselementen kan bevatten, worden met deStartMarquee
methode alle onderliggende besturingselementen opgesomd en wordenStartMarquee
aangeroepen voor besturingselementen dieIMarqueeWidget
implementeren. De methodeStopMarquee
heeft een vergelijkbare implementatie.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"); } } }
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
Implementeer de eigenschapstoegangsors. Het besturingselement
MarqueeBorder
heeft verschillende eigenschappen voor het controleren van het uiterlijk.[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; } }
<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
Implementeer de handlers voor de DoWork- en ProgressChanged-gebeurtenissen van het BackgroundWorker onderdeel.
De DoWork gebeurtenishandler slaapt gedurende het aantal milliseconden dat is opgegeven door
UpdatePeriod
, vervolgens wordt de ProgressChanged gebeurtenis verhoogd, totdat je code de animatie stopt door aanroep van CancelAsync.De ProgressChanged gebeurtenishandler verhoogt de positie van het "basislicht", van waaruit de licht-/donkere toestand van de andere lichten wordt bepaald, en roept de Refresh-methode aan om het besturingselement zichzelf opnieuw te laten tekenen.
// 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 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
Implementeer de helpermethoden,
IsLit
enDrawLight
.De methode
IsLit
bepaalt de kleur van een licht op een bepaalde positie. Lichten die 'verlicht' zijn, worden getekend in de kleur die wordt gegeven door de eigenschapLightColor
en de lichten die 'donker' zijn, worden getekend in de kleur die wordt gegeven door de eigenschapDarkColor
.De methode
DrawLight
tekent een licht met de juiste kleur, vorm en positie.// 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 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
Overschrijf de methoden OnLayout en OnPaint.
De methode OnPaint tekent de lichten langs de randen van de
MarqueeBorder
controle.Omdat de methode OnPaint afhankelijk is van de afmetingen van het besturingselement
MarqueeBorder
, moet u deze aanroepen wanneer de indeling verandert. Hiervoor overschrijft u OnLayout en roept u Refreshaan.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++; } } }
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
Een aangepaste ontwerpfunctie maken voor schaduw- en filtereigenschappen
De MarqueeControlRootDesigner
-klasse biedt de implementatie voor de hoofdontwerper. Naast deze ontwerper, die fungeert op de MarqueeControl
, hebt u een aangepaste ontwerper nodig die specifiek is gekoppeld aan de MarqueeBorder
controle. Deze ontwerper biedt aangepast gedrag dat geschikt is in de context van de aangepaste root-ontwerper.
Met name zal de MarqueeBorderDesigner
bepaalde eigenschappen van het MarqueeBorder
besturingselement 'schaduwen' en filteren, waardoor de interactie met de ontwerpomgeving verandert.
Het onderscheppen van aanroepen naar de eigenschapstoegang van een component wordt 'schaduwen' genoemd. Hiermee kan een ontwerper de waarde bewaken die door de gebruiker is ingesteld en optioneel die waarde doorgeven aan de component die wordt ontworpen.
In dit voorbeeld worden de eigenschappen Visible en Enabled in de schaduw geplaatst door de MarqueeBorderDesigner
, waardoor de gebruiker het MarqueeBorder
besturingselement niet zichtbaar of uitgeschakeld kan maken tijdens de ontwerptijd.
Ontwerpers kunnen ook eigenschappen toevoegen en verwijderen. In dit voorbeeld wordt de eigenschap Padding tijdens het ontwerp verwijderd, omdat de MarqueeBorder
de opvulling programmatisch instelt op basis van de grootte van de lichten die zijn opgegeven door de eigenschap LightSize
.
De basisklasse voor MarqueeBorderDesigner
is ComponentDesigner, die methoden bevat waarmee de kenmerken, eigenschappen en gebeurtenissen die tijdens het ontwerp door een besturingselement worden weergegeven, kunnen worden gewijzigd:
Wanneer u de openbare interface van een onderdeel wijzigt met behulp van deze methoden, volgt u deze regels:
Items toevoegen aan of verwijderen uit de
PreFilter
methoden alleen.Bestaande items in de
PostFilter
-methoden alleen wijzigenRoep altijd eerst de basis-implementatie aan in de
PreFilter
methodenDe basis-implementatie altijd het laatst aanroepen in de
PostFilter
-methoden
Het naleven van deze regels zorgt ervoor dat alle ontwerpers in de ontwerpomgeving een consistente weergave hebben van alle onderdelen die worden ontworpen.
De ComponentDesigner-klasse biedt een woordenlijst voor het beheren van de waarden van schaduweigenschappen, waardoor u geen specifieke instantievariabelen hoeft te maken.
Een aangepaste ontwerpfunctie maken voor schaduw- en filtereigenschappen
Klik met de rechtermuisknop op de map Ontwerp en voeg een nieuwe klasse toe. Geef het bronbestand een basisnaam van MarqueeBorderDesigner.
Open het bronbestand MarqueeBorderDesigner in de Code-editor. Importeer bovenaan het bestand de volgende naamruimten:
using System; using System.Collections; using System.ComponentModel; using System.ComponentModel.Design; using System.Diagnostics; using System.Windows.Forms; using System.Windows.Forms.Design;
Imports System.Collections Imports System.ComponentModel Imports System.ComponentModel.Design Imports System.Diagnostics Imports System.Windows.Forms Imports System.Windows.Forms.Design
Wijzig de declaratie van
MarqueeBorderDesigner
om over te nemen van ParentControlDesigner.Omdat het
MarqueeBorder
besturingselement onderliggende besturingselementen kan bevatten, erftMarqueeBorderDesigner
van ParentControlDesigner, waarmee de interactie tussen ouder- en kindelementen wordt verwerkt.namespace MarqueeControlLibrary.Design { public class MarqueeBorderDesigner : ParentControlDesigner {
Namespace MarqueeControlLibrary.Design Public Class MarqueeBorderDesigner Inherits ParentControlDesigner
De basisimplementatie van PreFilterPropertiesoverschrijven.
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]); }
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
Implementeer de eigenschappen Enabled en Visible. Deze implementaties schaduwen de eigenschappen van het besturingselement.
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; } }
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
Wijzigingen in onderdelen verwerken
De MarqueeControlRootDesigner
-klasse biedt de aangepaste ontwerpomgeving voor uw MarqueeControl
-exemplaren. De meeste ontwerptijdfunctionaliteit wordt overgenomen van de DocumentDesigner-klasse. Uw code implementeert twee specifieke aanpassingen: het afhandelen van wijzigingen in onderdelen en het toevoegen van ontwerpopdrachten.
Wanneer gebruikers hun MarqueeControl
-exemplaren ontwerpen, zal uw hoofdontwerper wijzigingen bijhouden in de MarqueeControl
en de onderliggende controles. De ontwerpomgeving biedt een handige service, IComponentChangeService, voor het bijhouden van wijzigingen in de onderdeelstatus.
U verkrijgt een verwijzing naar deze service door een query uit te voeren op de omgeving met de methode GetService. Als de query is geslaagd, kan uw ontwerper een handler koppelen voor de ComponentChanged event en de taken uitvoeren die nodig zijn om een consistente toestand te behouden tijdens de ontwerpperiode.
In het geval van de klasse MarqueeControlRootDesigner
roept u de Refresh methode aan voor elk IMarqueeWidget
object dat is opgenomen in de MarqueeControl
. Hierdoor wordt het IMarqueeWidget
-object op de juiste wijze hersteld wanneer eigenschappen zoals die van de ouder, Size, worden gewijzigd.
Onderdelenwijzigingen afhandelen
Open het
MarqueeControlRootDesigner
bronbestand in de Code-editor en overschrijf de methode Initialize. Roep de basis-implementatie van Initialize aan en voer een query uit voor de IComponentChangeService.base.Initialize(component); IComponentChangeService cs = GetService(typeof(IComponentChangeService)) as IComponentChangeService; if (cs != null) { cs.ComponentChanged += new ComponentChangedEventHandler(OnComponentChanged); }
MyBase.Initialize(component) Dim cs As IComponentChangeService = _ CType(GetService(GetType(IComponentChangeService)), _ IComponentChangeService) If (cs IsNot Nothing) Then AddHandler cs.ComponentChanged, AddressOf OnComponentChanged End If
Implementeer de OnComponentChanged gebeurtenisafhandelaar. Test het type van het verzendende onderdeel en roep de Refresh methode aan als het een
IMarqueeWidget
is.private void OnComponentChanged( object sender, ComponentChangedEventArgs e) { if (e.Component is IMarqueeWidget) { this.Control.Refresh(); } }
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
Designer-werkwoorden toevoegen aan uw Custom Designer
Een ontwerpwerkwoord is een menuopdracht die is gekoppeld aan een gebeurtenis-handler. Designer-werkwoorden worden toegevoegd aan het snelmenu van een onderdeel tijdens het ontwerp. Zie DesignerVerbvoor meer informatie.
U voegt twee ontwerpopdrachten toe aan uw ontwerpers: Test uitvoeren en Test stoppen. Met deze werkwoorden kunt u het runtime-gedrag van de MarqueeControl
bekijken tijdens de ontwerptijd. Deze werkwoorden worden toegevoegd aan MarqueeControlRootDesigner
.
Wanneer Test uitvoeren wordt aangeroepen, roept de gebeurtenis-handler voor werkwoorden de StartMarquee
methode aan op de MarqueeControl
. Wanneer Stop Test wordt aangeroepen, zal de gebeurtenis-handler de methode StopMarquee
aanroepen op de MarqueeControl
. De implementatie van de StartMarquee
- en StopMarquee
methoden roepen deze methoden aan op ingesloten besturingselementen die IMarqueeWidget
implementeren, zodat alle ingesloten IMarqueeWidget
besturingselementen ook deelnemen aan de test.
Ontwerpwerkwoorden toevoegen aan uw aangepaste ontwerpers
Voeg in de
MarqueeControlRootDesigner
klasse gebeurtenis-handlers toe met de naamOnVerbRunTest
enOnVerbStopTest
.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(); }
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
Verbind deze eventhandlers met hun bijbehorende ontwerperwerkwoorden.
MarqueeControlRootDesigner
neemt een DesignerVerbCollection over van de basisklasse. U maakt twee nieuwe DesignerVerb-objecten en voegt deze toe aan deze verzameling in de methode Initialize.this.Verbs.Add( new DesignerVerb("Run Test", new EventHandler(OnVerbRunTest)) ); this.Verbs.Add( new DesignerVerb("Stop Test", new EventHandler(OnVerbStopTest)) );
Me.Verbs.Add(New DesignerVerb("Run Test", _ New EventHandler(AddressOf OnVerbRunTest))) Me.Verbs.Add(New DesignerVerb("Stop Test", _ New EventHandler(AddressOf OnVerbStopTest)))
Een Custom UITypeEditor maken
Wanneer u een aangepaste ontwerptijdervaring voor gebruikers creëert, is het vaak wenselijk om een aangepaste interactie met het venster Eigenschappen te maken. U kunt dit doen door een UITypeEditorte maken.
Het besturingselement MarqueeBorder
toont verschillende eigenschappen in het venster Eigenschappen. Twee van deze eigenschappen, MarqueeSpinDirection
en MarqueeLightShape
worden vertegenwoordigd door opsommingen. Om het gebruik van een ui-typeeditor te illustreren, heeft de eigenschap MarqueeLightShape
een gekoppelde UITypeEditor-klasse.
Een aangepaste UI-type-editor maken
Open het
MarqueeBorder
bronbestand in de Code-editor.Declareer in de definitie van de klasse
MarqueeBorder
een klasse met de naamLightShapeEditor
die is afgeleid van UITypeEditor.// 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 {
' 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
Declareer een IWindowsFormsEditorService exemplaarvariabele met de naam
editorService
.private IWindowsFormsEditorService editorService = null;
Private editorService As IWindowsFormsEditorService = Nothing
Overschrijf de methode GetEditStyle. Deze implementatie retourneert DropDown, waarmee de ontwerpomgeving wordt aangegeven hoe de
LightShapeEditor
moet worden weergegeven.public override UITypeEditorEditStyle GetEditStyle( System.ComponentModel.ITypeDescriptorContext context) { return UITypeEditorEditStyle.DropDown; }
Public Overrides Function GetEditStyle( _ ByVal context As System.ComponentModel.ITypeDescriptorContext) _ As UITypeEditorEditStyle Return UITypeEditorEditStyle.DropDown End Function
Overschrijf de methode EditValue. Met deze implementatie wordt een query uitgevoerd op de ontwerpomgeving voor een IWindowsFormsEditorService-object. Als dit lukt, wordt er een
LightShapeSelectionControl
gemaakt. De methode DropDownControl wordt aangeroepen om deLightShapeEditor
te starten. De retourwaarde van deze aanroep wordt geretourneerd naar de ontwerpomgeving.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; }
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
Een weergavebesturingselement maken voor uw Custom UITypeEditor
De eigenschap MarqueeLightShape
ondersteunt twee soorten lichte vormen: Square
en Circle
. U maakt een aangepast besturingselement dat uitsluitend wordt gebruikt voor het grafisch weergeven van deze waarden in het venster Eigenschappen. Dit aangepaste besturingselement wordt door uw UITypeEditor gebruikt om met het eigenschappenvenster te communiceren.
Een weergavecontrole maken voor uw aangepaste UI-type-editor
Voeg een nieuw UserControl-item toe aan het
MarqueeControlLibrary
project. Geef het nieuwe bronbestand een basisnaam van LightShapeSelectionControl.Sleep twee Panel besturingselementen uit de Werkset naar de
LightShapeSelectionControl
. Noem zesquarePanel
encirclePanel
. Rangschik ze naast elkaar. Stel de eigenschap Size van beide Panel besturingselementen in op (60, 60). Stel de eigenschap Location van het besturingselementsquarePanel
in op (8, 10). Stel de eigenschap Location van het besturingselementcirclePanel
in op (80, 10). Stel ten slotte de eigenschap Size van deLightShapeSelectionControl
in op (150, 80).Open het
LightShapeSelectionControl
bronbestand in de Code-editor. Importeer bovenaan het bestand de System.Windows.Forms.Design naamruimte:Imports System.Windows.Forms.Design
using System.Windows.Forms.Design;
Implementeer Click gebeurtenishandlers voor de
squarePanel
encirclePanel
bedieningselementen. Met deze methoden wordt CloseDropDown aangeroepen om de aangepaste UITypeEditor bewerkingssessie te beëindigen.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(); }
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
Declareer een IWindowsFormsEditorService exemplaarvariabele met de naam
editorService
.Private editorService As IWindowsFormsEditorService
private IWindowsFormsEditorService editorService;
Declareer een
MarqueeLightShape
exemplaarvariabele met de naamlightShapeValue
.private MarqueeLightShape lightShapeValue = MarqueeLightShape.Square;
Private lightShapeValue As MarqueeLightShape = MarqueeLightShape.Square
Koppel in de
LightShapeSelectionControl
constructor de Click gebeurtenis-handlers aan desquarePanel
- encirclePanel
-controls voor de Click-gebeurtenissen. Definieer ook een overbelasting van een constructor die deMarqueeLightShape
waarde uit de ontwerpomgeving toewijst aan hetlightShapeValue
veld.// 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); }
' 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
Ontkoppel de Click gebeurtenis-handlers in de Dispose methode.
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 ); }
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
Klik in Solution Explorerop de knop Alle bestanden weergeven. Open het LightShapeSelectionControl.Designer.cs- of LightShapeSelectionControl.Designer.vb-bestand en verwijder de standaarddefinitie van de Dispose methode.
Implementeer de eigenschap
LightShape
.// 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; } } }
' 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
Overschrijf de methode OnPaint. Deze implementatie tekent een gevuld vierkant en een cirkel. De geselecteerde waarde wordt ook gemarkeerd door een rand rond de ene of de andere vorm te tekenen.
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); } } }
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
Test uw aangepaste controle in de ontwerpfunctie
Op dit moment kunt u het MarqueeControlLibrary
project bouwen. Test uw implementatie door een besturingselement te maken dat wordt overgenomen van de MarqueeControl
-klasse en deze op een formulier gebruikt.
Een aangepaste MarqueeControl-implementatie maken
Open
DemoMarqueeControl
in de Windows Forms Designer. Hiermee maakt u een exemplaar van hetDemoMarqueeControl
type en wordt dit weergegeven in een exemplaar van hetMarqueeControlRootDesigner
type.Open de MarqueeControlLibrary Components tab in de Werkset. U ziet de
MarqueeBorder
enMarqueeText
bedieningselementen die beschikbaar zijn voor selectie.Sleep een exemplaar van het besturingselement
MarqueeBorder
naar het ontwerpoppervlak van deDemoMarqueeControl
. Plaats ditMarqueeBorder
besturingselement op het bovenliggende besturingselement.Sleep een exemplaar van het besturingselement
MarqueeText
naar het ontwerpoppervlak van deDemoMarqueeControl
.Bouw de oplossing.
Klik met de rechtermuisknop op de
DemoMarqueeControl
en selecteer in het snelmenu de optie Test uitvoeren om de animatie te starten. Klik op Stop Test om de animatie te stoppen.Open Form1- in de ontwerpweergave.
Plaats twee Button bedieningselementen op het formulier. Noem ze
startButton
enstopButton
en wijzig respectievelijk de eigenschapswaarden van de Text in Start en Stop.Implementeer Click event handlers voor beide Button besturingselementen.
Open in de Werksethet tabblad MarqueeControlTest Components. U ziet dat
DemoMarqueeControl
beschikbaar is voor selectie.Sleep een exemplaar van
DemoMarqueeControl
naar het ontwerpoppervlak Form1.Roep in de Click gebeurtenis-handlers de methoden
Start
enStop
op deDemoMarqueeControl
aan.Private Sub startButton_Click(sender As Object, e As System.EventArgs) Me.demoMarqueeControl1.Start() End Sub 'startButton_Click Private Sub stopButton_Click(sender As Object, e As System.EventArgs) Me.demoMarqueeControl1.Stop() End Sub 'stopButton_Click
private void startButton_Click(object sender, System.EventArgs e) { this.demoMarqueeControl1.Start(); } private void stopButton_Click(object sender, System.EventArgs e) { this.demoMarqueeControl1.Stop(); }
Stel het
MarqueeControlTest
project in als het opstartproject en voer het uit. U ziet het formulier dat uwDemoMarqueeControl
weergeeft. Selecteer de knop Start om de animatie te starten. U ziet dat de tekst knippert en de lichten langs de rand bewegen.
Volgende stappen
De MarqueeControlLibrary
toont een eenvoudige implementatie van aangepaste besturingselementen en bijbehorende ontwerpers. U kunt dit voorbeeld op verschillende manieren geavanceerder maken:
Wijzig de eigenschapswaarden voor de
DemoMarqueeControl
in de ontwerpfunctie. Voeg meerMarqueBorder
-besturingselementen toe en dock ze binnen hun bovenliggende instanties om een geneste structuur te creëren. Experimenteer met verschillende instellingen voor deUpdatePeriod
en de lichtgerelateerde eigenschappen.Uw eigen implementaties van
IMarqueeWidget
ontwerpen. U kunt bijvoorbeeld een knipperend 'neonteken' of een geanimeerde teken met meerdere afbeeldingen maken.Pas de ontwerp-tijdervaring verder aan. U kunt meer eigenschappen proberen te schaduwen dan Enabled en Visible, en u kunt nieuwe eigenschappen toevoegen. Voeg nieuwe ontwerpcommando's toe om veelvoorkomende taken te vereenvoudigen, zoals het verankeren van subbesturingselementen.
Geef een licentie aan
MarqueeControl
.Bepalen hoe uw besturingselementen worden geserialiseerd en hoe code voor deze besturingselementen wordt gegenereerd. Zie dynamische broncode genereren en compilerenvoor meer informatie.
Zie ook
.NET Desktop feedback