İzlenecek yol: tasarım zamanı özelliklerinden faydalanan bir denetim oluşturma

Özel bir denetim için tasarım zamanı deneyimi, ilişkili bir özel tasarımcı Authoring ile geliştirilebilir.

Bu makalede özel bir denetim için nasıl özel tasarımcı oluşturacağınız açıklanmıştır. MarqueeControlAdlı bir tür ve ilişkili tasarımcı sınıfını uygulayacaksınız MarqueeControlRootDesigner .

MarqueeControlTürü, animasyonlu ışıklar ve yanıp sönen metinle benzer bir tiyatro çerçevesine benzer bir ekran uygular.

Bu denetim için tasarımcı, tasarım ortamıyla birlikte etkileşimde bulunur ve özel bir tasarım zamanı deneyimi sağlar. Özel tasarımcı sayesinde, bir özel MarqueeControl uygulamayı animasyonlu ışıklar ve yanıp sönen metni birçok birleşimde bir araya getirebilirsiniz. bir form üzerinde, diğer Windows Forms denetimleri gibi, birleştirilmiş denetimi kullanabilirsiniz.

Bu yönergeyi tamamladığınızda, özel denetiminiz aşağıdakine benzer şekilde görünür:

The app showing a marquee saying Text and a Start and Stop buttons.

tüm kod listesi için, bkz. nasıl yapılır: Windows Forms denetimi oluşturma Design-Time özelliklerden faydalanır.

Önkoşullar

Bu izlenecek yolu tamamlamak için Visual Studio gerekir.

Proje oluşturma

İlk adım uygulama projesini oluşturmaktır. Bu projeyi, özel denetimi barındıran uygulamayı oluşturmak için kullanacaksınız.

Visual Studio, yeni bir Windows Forms uygulama projesi oluşturun ve bunu MarqueeControlTestolarak adlandırın.

Denetim kitaplığı projesi oluşturma

  1. çözüme bir Windows Forms denetim kitaplığı projesi ekleyin. Projeyi MarqueeControlLibraryolarak adlandırın.

  2. Çözüm Gezginikullanarak, tercih ettiğiniz dile bağlı olarak "UserControl1. cs" veya "UserControl1. vb" adlı kaynak dosyayı silerek projenin varsayılan denetimini silin.

  3. Projeye yeni bir UserControl öğe ekleyin MarqueeControlLibrary . Yeni kaynak dosyasına MarqueeControltemel adını verin.

  4. Çözüm Gezginikullanarak projede yeni bir klasör oluşturun .

  5. Tasarım klasörüne sağ tıklayın ve yeni bir sınıf ekleyin. MarqueeControlRootDesignerolarak adlandırın.

  6. System.Design derlemesinden türler kullanmanız gerekir, bu nedenle bu başvuruyu MarqueeControlLibrary projeye ekleyin.

Özel denetime başvur Project

Bu MarqueeControlTest projeyi, özel denetimi test etmek için kullanacaksınız. Derlemeye bir proje başvurusu eklediğinizde test projesi özel denetimden haberdar olur MarqueeControlLibrary .

MarqueeControlTestProjede, derlemeye bir proje başvurusu ekleyin MarqueeControlLibrary . Derlemeye doğrudan başvurmak yerine Başvuru Ekle Iletişim kutusundaki Projeler sekmesini kullandığınızdan emin olun .

Özel bir denetim ve kendi özel tasarımcısını tanımlama

Özel denetiminiz sınıfından türecektir UserControl . Bu, denetiminizin diğer denetimleri içermesini sağlar ve denetimi, varsayılan işlevselliğin harika olmasını sağlar.

Özel denetiminizin ilişkili bir özel Tasarımcısı olacak. Bu, özel denetiminiz için özel olarak uyarlanmış benzersiz bir tasarım deneyimi oluşturmanıza olanak sağlar.

Sınıfını kullanarak denetimi Tasarımcı ile ilişkilendirirsiniz DesignerAttribute . Özel denetiminizin tüm tasarım zamanı davranışını geliştirmekte olduğunuzdan, özel tasarımcı IRootDesigner arabirimini uygular.

Özel bir denetim ve özel tasarımcı tanımlamak için

  1. MarqueeControlKaynak dosyasını MarqueeControlaçın. Dosyanın en üstüne aşağıdaki ad alanlarını içeri aktarın:

    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
    
  2. DesignerAttributeMarqueeControl Sınıfını sınıf bildirimine ekleyin. Bu, özel denetimi Tasarımcı ile ilişkilendirir.

    [Designer( typeof( MarqueeControlLibrary.Design.MarqueeControlRootDesigner ), typeof( IRootDesigner ) )]
    public class MarqueeControl : UserControl
    {
    
    <Designer(GetType(MarqueeControlLibrary.Design.MarqueeControlRootDesigner), _
     GetType(IRootDesigner))> _
    Public Class MarqueeControl
        Inherits UserControl
    
  3. MarqueeControlRootDesignerKaynak dosyasını MarqueeControlRootDesigneraçın. Dosyanın en üstüne aşağıdaki ad alanlarını içeri aktarın:

    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
    
  4. Bildirimini MarqueeControlRootDesigner sınıfından devralacak şekilde değiştirin DocumentDesigner . ToolboxItemFilterAttributeToolboxItemFilterAttributeile tasarımcı etkileşimini belirtmek için öğesini uygulayın.

    Not

    Sınıfının tanımı, MarqueeControlRootDesigner MarqueeControlLibrary.Design adlı bir ad alanı içine alınmıştır. Bu bildirim, tasarımcıyı tasarımla ilgili türler için ayrılmış özel bir ad alanına koyar.

    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
        {
    
    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
    
  5. Sınıf için oluşturucuyu tanımlayın MarqueeControlRootDesigner . WriteLineOluşturucu gövdesine bir ifade ekleyin. Bu, hata ayıklama için yararlı olacaktır.

    public MarqueeControlRootDesigner()
    {
        Trace.WriteLine("MarqueeControlRootDesigner ctor");
    }
    
    Public Sub New()
        Trace.WriteLine("MarqueeControlRootDesigner ctor")
    End Sub
    

Özel denetiminizin bir örneğini oluşturma

  1. Projeye yeni bir UserControl öğe ekleyin MarqueeControlTest . Yeni kaynak dosyasına DemoMarqueeControltemel adını verin.

  2. DemoMarqueeControlDosyayı DemoMarqueeControlaçın. Dosyanın en üstünde, MarqueeControlLibrary ad alanını içeri aktarın:

    Imports MarqueeControlLibrary
    
    using MarqueeControlLibrary;
    
  3. Bildirimini DemoMarqueeControl sınıfından devralacak şekilde değiştirin MarqueeControl .

  4. Projeyi derleyin.

  5. Windows Form Tasarımcısı Form1 ' i açın.

  6. Araç kutusundaMarqueeControlTest Components sekmesini bulun ve açın. DemoMarqueeControlDemoMarqueeControl bir öğesini formunuza sürükleyin.

  7. Projeyi derleyin.

Design-Time hata ayıklama için Project ayarlama

Özel bir tasarım zamanı deneyimi geliştirirken, denetimlerinizin ve bileşenlerinizin hata ayıklaması gerekir. Projenizi tasarım zamanında hata ayıklamaya izin verecek şekilde kurmak için basit bir yol vardır. daha fazla bilgi için bkz. izlenecek yol: tasarım zamanında özel Windows Forms denetimlerinde hata ayıklama.

  1. Projeye sağ tıklayın MarqueeControlLibrary ve MarqueeControlLibrary' i seçin.

  2. MarqueeControlLibrary Özellik sayfaları Iletişim kutusunda hata ayıklama sayfasını seçin.

  3. Başlangıç eylemi bölümünde dış program Başlat' ı seçin. Visual Studio ayrı bir örneğinden hata ayıklaması yapılır, bu nedenle The Ellipsis button (...) in the Properties window of Visual Studio Visual Studio ıde 'ye gitmek için üç nokta () düğmesine tıklayın. yürütülebilir dosyanın adı devenv.exe ve varsayılan konuma yüklediyseniz, yolu % ProgramFiles (x86)% \ Microsoft Visual Studio \ 2019 \ edition >\Common7\IDE\devenv.exeolur.

  4. İletişim kutusunu kapatmak için Tamam ' ı seçin.

  5. MarqueeControlLibrary projesine sağ tıklayın ve bu hata ayıklama yapılandırmasını etkinleştirmek için başlangıç Project ayarla ' yı seçin.

Checkpoint

Artık özel denetiminizin tasarım zamanı davranışını hata ayıklamaya hazırsınız. Hata ayıklama ortamının doğru şekilde ayarlandığını belirledikten sonra, özel denetim ve özel tasarımcı arasındaki ilişkilendirmeyi test edersiniz.

Hata ayıklama ortamını ve tasarımcı ilişkilendirmesini test etmek için

  1. Kod düzenleyicisinde MarqueeControlRootDesigner kaynak dosyasını açın ve deyime bir kesme noktası yerleştirin .

  2. Hata ayıklama oturumu başlatmak için F5 tuşuna basın.

    yeni bir Visual Studio örneği oluşturulur.

  3. yeni Visual Studio örneğinde, MarqueeControlTest çözümünü açın. Dosya menüsünden son projeler ' i seçerek çözümü kolayca bulabilirsiniz. MarqueeControlTest. sln çözüm dosyası en son kullanılan dosya olarak listelenecektir.

  4. Öğesini DemoMarqueeControl tasarımcıda açın.

    Visual Studio hata ayıklama örneği, kesme noktasında odak ve yürütme işlemini alır. Hata ayıklama oturumuna devam etmek için F5 tuşuna basın.

Bu noktada, özel denetiminizi ve onunla ilişkili özel tasarımcıyı geliştirip hata ayıklamanızın her şey vardır. Bu makalenin geri kalanında, denetimin ve tasarımcının özelliklerini uygulama ayrıntılarına yoğunlaşmaktadır.

Özel denetimi uygulama

, MarqueeControl Bir UserControl Özelleştirme biraz daha vardır. İki yöntem sunar: Start , kayan yazı animasyonunu başlatan ve Stop animasyonu durduran. , MarqueeControl Arabirimini uygulayan alt denetimler içerdiğinden ve IMarqueeWidgetStartStop her bir alt denetimi numaralandırın ve öğesini StartMarqueeStopMarquee uygulayan her alt denetimde sırasıyla ve yöntemlerini çağırdığından IMarqueeWidget .

MarqueeBorderVe MarqueeText denetimlerinin görünümü düzene bağlıdır, MarqueeControl Bu nedenle OnLayout yöntemi ve PerformLayout Bu türün alt denetimlerinde yapılan çağrıları geçersiz kılar.

Bu, özelleştirmelerin bir kapsamını MarqueeControl . Çalışma zamanı özellikleri MarqueeBorder ve denetimleri tarafından uygulanır MarqueeText ve tasarım zamanı özellikleri MarqueeBorderDesigner ve sınıfları tarafından uygulanır MarqueeControlRootDesigner .

Özel denetiminizi uygulamak için

  1. Kaynak MarqueeControl dosyayı Kod MarqueeControl ve Start yöntemlerini Stop uygulama.

    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
    
  2. yöntemini geçersiz OnLayout kılın.

    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
    

Özel Denetiminiz için Alt Denetim Oluşturma

MarqueeControl, iki tür alt denetimi barındıracak: denetim ve MarqueeBorderMarqueeText denetim.

  • MarqueeBorder: Bu denetim, kenarlarının çevresindeki "ışık" kenarını boyar. Işıklar sırayla yanıp söner, bu nedenle kenarlıkta hareket ediyor gibi görünürler. Işıkların yanıp sönme hızı adlı bir özellik tarafından UpdatePeriod denetlenmektedir. Diğer bazı özel özellikler denetimin görünümünün diğer yönlerini belirler. ve adlı iki yöntem, StartMarqueeStopMarquee animasyonun ne zaman başlatı ve durdur olduğunu kontrol ediyor.

  • MarqueeText: Bu denetim, yanıp sönen bir dizeyi boyar. Denetimde MarqueeBorder olduğu gibi metnin yanıp sönme hızı özelliği tarafından UpdatePeriod denetlenmektedir. Denetimin MarqueeText denetiminde StartMarquee ortak olan ve yöntemleri StopMarquee de MarqueeBorder vardır.

Tasarım zamanında, bu MarqueeControlRootDesigner iki denetim türlerinin herhangi bir birleşimde bir'e MarqueeControl eklenmelerine olanak sağlar.

İki denetimin ortak özellikleri adlı arabirimde dikkate alınarak IMarqueeWidget kullanılır. Bu sayede MarqueeControl Marquee ile ilgili tüm alt denetimleri keşfeder ve onlara özel bir işlem sağlar.

Düzenli animasyon özelliğini uygulamak için ad alanı BackgroundWorker nesnelerini System.ComponentModel kullanırsınız. Nesneleri Timer kullanabilirsiniz, ancak birçok nesne mevcut olduğunda, tek bir kullanıcı arabirimi iş IMarqueeWidget parçacığı animasyona uyamıyor olabilir.

Özel denetiminiz için bir alt denetim oluşturmak için

  1. Projeye yeni bir sınıf öğesi MarqueeControlLibrary ekleyin. Yeni kaynak dosyaya "IMarqueeWidget" temel adını girin.

  2. Kod IMarqueeWidget Düzenleyicisi'nde kaynak IMarqueeWidget bildirimini olarak olarak classinterface değiştirebilirsiniz:

    // 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
    
  3. İki yöntemi ve seçim çerçevesi IMarqueeWidget animasyonunu yönlendiren bir özelliği ortaya çıkarmak için arabirimine aşağıdaki kodu ekleyin:

    // 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
    
  4. Projeye yeni bir Özel Denetim öğesi ekleyin. Yeni kaynak dosyaya "MarqueeText" temel adını girin.

  5. Bir bileşeni BackgroundWorker Araç BackgroundWorkerMarqueeText sürükleyin. Bu bileşen MarqueeText denetimin kendisini zaman uyumsuz olarak güncelleştirmesine olanak sağlar.

  6. Özellikler penceresinde bileşenin ve özelliklerini true olarak WorkerReportsProgressWorkerSupportsCancellationWorkerReportsProgress Bu ayarlar BackgroundWorker bileşenin düzenli aralıklarla olayı ProgressChanged oluşturmasına ve zaman uyumsuz güncelleştirmeleri iptal etmesine olanak sağlar.

    Daha fazla bilgi için bkz. BackgroundWorker Bileşeni.

  7. Kaynak MarqueeText dosyayı Kod MarqueeText Dosyanın en üstünde aşağıdaki ad alanlarını içeri aktarın:

    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
    
  8. ve 'den MarqueeText devralınarak Label bildirimini değiştirarak arabirimini IMarqueeWidget gerçekleştirin:

    [ToolboxItemFilter("MarqueeControlLibrary.MarqueeText", ToolboxItemFilterType.Require)]
    public partial class MarqueeText : Label, IMarqueeWidget
    {
    
    <ToolboxItemFilter("MarqueeControlLibrary.MarqueeText", _
    ToolboxItemFilterType.Require)> _
    Partial Public Class MarqueeText
        Inherits Label
        Implements IMarqueeWidget
    
  9. Ortaya konan özelliklere karşılık gelen örnek değişkenlerini bildirin ve oluşturucuda bunları başlatabilirsiniz. isLitalanı, metnin özelliği tarafından verilen renge boyanmış olup olmadığını LightColor belirler.

    // 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
    
  10. IMarqueeWidget arabirimini gerçekleştirin.

    ve StartMarqueeStopMarquee yöntemleri, animasyonu BackgroundWorker başlatmak ve durdurmak için RunWorkerAsyncCancelAsync bileşenin ve yöntemlerini çağırır.

    ve öznitelikleri özelliğine uygulandığı için " CategoryBrowsableUpdatePeriod Marquee" adlı özel Özellikler penceresi görünür.

    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
    
  11. Özellik erişimcilerini uygulama. İstemcilere iki özellik gösterirsiniz: LightColor ve DarkColor . ve öznitelikleri bu özelliklere uygulandığı için CategoryBrowsable özellikler, "Marquee" olarak adlandırılan Özellikler penceresi bölümde görünür.

    [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
    
  12. Bileşenin ve olayların BackgroundWorkerDoWork işleyicilerini ProgressChanged uygulama.

    Olay işleyicisi tarafından belirtilen milisaniye sayısı için uykuda olur ve ardından kodunuz çağırarak DoWorkUpdatePeriodProgressChanged animasyonu durdurana kadar olayı CancelAsync yükselter.

    Olay işleyicisi, yanıp sönme görünümünü vermek için metni açık ve koyu ProgressChanged durumu arasında iki durumlu olarak gösterir.

    // 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
    
  13. Animasyonu OnPaint etkinleştirmek için yöntemini geçersiz kılın.

    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
    
  14. Çözümü oluşturmak için F6 tuşuna basın.

MarqueeBorder Alt Denetimi Oluşturma

Denetim, MarqueeBorder denetimden biraz daha MarqueeText karmaşıktır. Daha fazla özelliği vardır ve yönteminde animasyon OnPaint daha çok söz konusu olur. Prensipte denetime oldukça MarqueeText benzerdir.

Denetimin MarqueeBorder alt denetimleri olduğundan, olayların farkında olması Layout gerekir.

MarqueeBorder denetimi oluşturmak için

  1. Projeye yeni bir Özel Denetim öğesi ekleyin. Yeni kaynak dosyaya "MarqueeBorder" temel adını girin.

  2. Bir bileşeni BackgroundWorker Araç BackgroundWorkerMarqueeBorder sürükleyin. Bu bileşen MarqueeBorder denetimin kendisini zaman uyumsuz olarak güncelleştirmesine olanak sağlar.

  3. Özellikler penceresinde bileşenin ve özelliklerini true olarak WorkerReportsProgressWorkerSupportsCancellationWorkerReportsProgress Bu ayarlar BackgroundWorker bileşenin düzenli aralıklarla olayı ProgressChanged oluşturmasına ve zaman uyumsuz güncelleştirmeleri iptal etmesine olanak sağlar. Daha fazla bilgi için bkz. BackgroundWorker Bileşeni.

  4. Özellikler penceresinde Olaylar düğmesini seçin. ve olayları için DoWorkProgressChanged işleyiciler ekleyin.

  5. Kaynak MarqueeBorder dosyayı Kod MarqueeBorder Dosyanın en üstünde aşağıdaki ad alanlarını içeri aktarın:

    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
    
  6. arabirimini uygulamak için MarqueeBorder ve 'den devralma Panel bildirimini IMarqueeWidget değiştirme.

    [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
    
  7. Denetimin durumunu yönetmek için iki sabit nokta bildirin: Kenarda "dönen" ışık yönünü belirleyen ve ışık şeklini (kare veya dairesel) belirleyen MarqueeBorderMarqueeSpinDirectionMarqueeLightShape . Bu bildirimleri sınıf bildiriminin MarqueeBorder öncesini yer.

    // 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
    
  8. Ortaya konan özelliklere karşılık gelen örnek değişkenlerini bildirin ve oluşturucuda bunları başlatabilirsiniz.

    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
    
  9. IMarqueeWidget arabirimini gerçekleştirin.

    ve StartMarqueeStopMarquee yöntemleri, animasyonu BackgroundWorker başlatmak ve durdurmak için RunWorkerAsyncCancelAsync bileşenin ve yöntemlerini çağırır.

    Denetim MarqueeBorder alt denetimler içerene kadar, yöntemi tüm alt denetimleri ve uygulayan StartMarqueeStartMarquee çağrıları IMarqueeWidget numaralar. yöntemi StopMarquee de benzer bir uygulamaya sahip.

    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
    
  10. Özellik erişimcilerini uygulama. Denetimin MarqueeBorder görünümünü denetlemek için çeşitli özellikleri vardır.

    [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
    
  11. Bileşenin ve olayların BackgroundWorkerDoWork işleyicilerini ProgressChanged uygulama.

    Olay işleyicisi tarafından belirtilen milisaniye sayısı için uykuda olur ve ardından kodunuz çağırarak DoWorkUpdatePeriodProgressChanged animasyonu durdurana kadar olayı CancelAsync yükselter.

    Olay işleyicisi, diğer lambaların açık/koyu durumunun belirlen olduğu "temel" ışığın konumunu artırır ve denetimin kendisini yeniden boyayabilecek şekilde ProgressChangedRefresh yöntemini çağırarak.

    // 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
    
  12. yardımcı yöntemlerini ve IsLitDrawLight uygulama.

    yöntemi, IsLit belirli bir konumdaki bir ışığın rengini belirler. "Açık" olan işıklar özelliği tarafından verilen renge göre, "koyu" olan ise özelliğin LightColor verdiği renge göre DarkColor çizilir.

    yöntemi DrawLight uygun rengi, şekli ve konumu kullanarak bir ışık çizer.

    // 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
    
  13. ve yöntemlerini OnLayoutOnPaint geçersiz kılın.

    yöntemi, OnPaint denetimin kenarları boyunca lambalar MarqueeBorder çizer.

    yöntemi OnPaint denetimin boyutlarına bağlı MarqueeBorder olduğundan, düzen her değiştirisinde çağırabilirsiniz. Bunu başarmak için geçersiz kılın OnLayout ve çağrısı Refresh gerçekleştirin.

    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
    

Gölge ve Filtre Özelliklerine Özel Tasarımcı Oluşturma

sınıfı, MarqueeControlRootDesigner kök tasarımcı için uygulama sağlar. üzerinde çalışan bu tasarımcıya ek MarqueeControl olarak, özellikle denetimle ilişkili özel bir tasarımcıya ihtiyacınız MarqueeBorder vardır. Bu tasarımcı, özel kök tasarımcı bağlamında uygun olan özel bir davranış sağlar.

Özellikle, MarqueeBorderDesigner denetimde belirli özellikleri "gölgeler" ve MarqueeBorder filtreler ve tasarım ortamıyla etkileşimlerini değiştirir.

Bileşenin özellik erişimcisinde çağrılara müdahale etmek "gölge oluşturma" olarak bilinir. Tasarımcının kullanıcı tarafından ayarlanmış değeri izlemesini ve isteğe bağlı olarak bu değeri tasarlanan bileşene iletir.

Bu örnekte, ve özellikleri, kullanıcının denetimi tasarım zamanında görünmez veya devre dışı bırakarak engellemesini engelleyen tarafından VisibleEnabledMarqueeBorderDesignerMarqueeBorder gölgeli hale gelecektir.

Tasarımcılar ayrıca özellik ekleyebilir ve kaldırabilir. Bu örnekte denetim, doldurmayı özelliği tarafından belirtilen ışık boyutuna göre programlı olarak ayar olduğundan tasarım zamanında PaddingMarqueeBorder özelliği LightSize kaldırılacaktır.

için temel sınıf, tasarım zamanında bir denetim tarafından ortaya çıkan öznitelikleri, özellikleri ve olayları değiştiren MarqueeBorderDesignerComponentDesigner yöntemlere sahip olan sınıfıdır:

Bu yöntemleri kullanarak bir bileşenin genel arabirimini değiştirirken şu kuralları izleyin:

  • Yalnızca yöntemlere öğe ekleme PreFilter veya kaldırma

  • Yalnızca yöntemlerdeki mevcut PostFilter öğeleri değiştirme

  • Yöntemlerde her zaman ilk olarak temel PreFilter uygulamasını çağırma

  • Yöntemlerde her zaman temel uygulamanın en PostFilter son çağrısını kullanın

Bu kurallara uyularak tasarım zamanı ortamındaki tüm tasarımcıların, tasarlanan tüm bileşenlerin tutarlı bir görünümü olur.

ComponentDesignerSınıfı, gölgelendirilmiş özelliklerin değerlerini yönetmek için bir sözlük sağlar, bu da size belirli örnek değişkenleri oluşturma gereksinimini sizi maliyetinden kurtarır.

Gölge ve filtre özelliklerine özel bir tasarımcı oluşturmak için

  1. Tasarım klasörüne sağ tıklayın ve yeni bir sınıf ekleyin. Kaynak dosyaya MarqueeBorderDesignertemel adını verin.

  2. MarqueeBorderDesigner kaynak dosyasını kod düzenleyicisindeaçın. Dosyanın en üstüne aşağıdaki ad alanlarını içeri aktarın:

    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
    
  3. Bildirimini MarqueeBorderDesigner öğesinden devralacak şekilde değiştirin ParentControlDesigner .

    MarqueeBorderDenetim alt denetimler içerebildiğinden, MarqueeBorderDesignerParentControlDesigner üst-alt etkileşimini işleyen öğesinden devralır.

    namespace MarqueeControlLibrary.Design
    {
        [System.Security.Permissions.PermissionSet(System.Security.Permissions.SecurityAction.Demand, Name = "FullTrust")]
        public class MarqueeBorderDesigner : ParentControlDesigner
        {
    
    Namespace MarqueeControlLibrary.Design
    
        <System.Security.Permissions.PermissionSetAttribute(System.Security.Permissions.SecurityAction.Demand, Name:="FullTrust")> _
        Public Class MarqueeBorderDesigner
            Inherits ParentControlDesigner
    
  4. Temel uygulamasını geçersiz kılın PreFilterProperties .

    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
    
  5. EnabledVe özelliklerini uygulayın Visible . Bu uygulamalar, denetimin özelliklerini gölgelendir.

    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
    

Bileşen değişikliklerini işle

MarqueeControlRootDesignerSınıfı, örneklerinizin özel tasarım zamanı deneyimini sağlar MarqueeControl . Tasarım zamanı işlevlerinin çoğu DocumentDesigner sınıfından devralınır. Kodunuz iki özel özelleştirme uygular: bileşen değişikliklerini işleme ve tasarımcı fiilleri ekleme.

Kullanıcılar MarqueeControl örneklerini tasarlarsa, kök tasarlayıcı, MarqueeControl ve alt denetimlerinde yapılan değişiklikleri izler. Tasarım zamanı ortamı, IComponentChangeService bileşen durumundaki değişiklikleri izlemek için uygun bir hizmet sunar.

Ortamı yöntemiyle sorgulayarak bu hizmete bir başvuru elde edersiniz GetService . Sorgu başarılı olursa, tasarımcı olay için bir işleyici iliştirebilir ComponentChanged ve tasarım zamanında tutarlı bir durumu korumak için gereken görevleri gerçekleştirebilir.

Sınıfı söz konusu olduğunda, MarqueeControlRootDesignerRefresh yöntemi tarafından içerilen her bir nesne üzerinde çağıracaksınız IMarqueeWidgetMarqueeControl . Bu, IMarqueeWidget üst öğesi gibi özellikler değiştirildiğinde nesnenin kendisini uygun şekilde yeniden görüntülemesine neden olur Size .

Bileşen değişikliklerini işlemek için

  1. MarqueeControlRootDesignerKaynak dosyasını MarqueeControlRootDesigner açın ve metodunu geçersiz kılın Initialize . İçin temel uygulamasını çağırın Initialize ve için sorgulayın 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
    
  2. OnComponentChangedOlay işleyicisini uygulayın. Gönderen bileşenin türünü test edin ve bir ise IMarqueeWidgetRefresh yöntemini çağırın.

    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
    

Özel tasarımcıya tasarımcı fiilleri ekleme

Tasarımcı fiili, olay işleyicisine bağlı bir menü komutu olur. Tasarımcı fiilleri, tasarım zamanında bir bileşenin kısayol menüsüne eklenir. Daha fazla bilgi için bkz. DesignerVerb.

Tasarımcılara iki tasarımcı fiilleri ekleyeceksiniz: Testi Çalıştır ve testi durdur. Bu fiiller, tasarım zamanının çalışma zamanı davranışını görüntülemenize olanak sağlayacak MarqueeControl . Bu fiiller öğesine eklenecektir MarqueeControlRootDesigner .

Çalıştırma testi çağrıldığında, fiil olay işleyicisi üzerinde yöntemini çağırır MarqueeControl . Testi durdur çağrıldığında, fiil olay işleyicisi üzerinde yöntemini çağırır MarqueeControl . Ve yöntemlerinin uygulaması, StartMarqueeStopMarquee uygulayan içerilen denetimler üzerinde bu yöntemleri çağırır IMarqueeWidget , bu nedenle içerilen IMarqueeWidget denetimlerin de teste katılması gerekir.

Özel tasarımcılara tasarımcı fiilleri eklemek için

  1. MarqueeControlRootDesignerSınıfında, ve adlı olay işleyicileri ekleyin OnVerbRunTestOnVerbStopTest .

    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
    
  2. bu olay işleyicilerini ilgili tasarımcı fiillerine Bağlan. MarqueeControlRootDesignerDesignerVerbCollectionkendi temel sınıfından bir devralır. İki yeni DesignerVerb nesne oluşturacak ve bu koleksiyona, yöntemi içinde ekleyecek 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)))
    

Özel bir Uııtypeınfo Düzenleyicisi oluşturun

Kullanıcılar için özel bir tasarım zamanı deneyimi oluşturduğunuzda, genellikle Özellikler penceresi özel bir etkileşim oluşturmak tercih edilir. Bunu oluşturarak yapabilirsiniz UITypeEditor .

MarqueeBorderDenetim Özellikler penceresi çeşitli özellikleri kullanıma sunar. Bu özelliklerden ikisi MarqueeSpinDirection ve MarqueeLightShape numaralandırmalar tarafından temsil edilir. Kullanıcı arabirimi türü düzenleyicisinin kullanımını göstermek için, MarqueeLightShape özelliği ilişkili bir sınıfa sahip olur UITypeEditor .

Özel bir kullanıcı arabirimi tür Düzenleyicisi oluşturmak için

  1. MarqueeBorderKaynak dosyasını MarqueeBorderaçın.

  2. MarqueeBorderSınıfının tanımında, öğesinden türetilen adlı bir sınıf bildirin LightShapeEditorUITypeEditor .

    // 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
    
  3. Adlı bir IWindowsFormsEditorService örnek değişkeni bildirin editorService .

    private IWindowsFormsEditorService editorService = null;
    
    Private editorService As IWindowsFormsEditorService = Nothing
    
  4. Yöntemini geçersiz kılın GetEditStyle . Bu uygulama, DropDown tasarım ortamına nasıl görüntüleneceğini bildiren, öğesini döndürür LightShapeEditor .

    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
    
    
  5. Yöntemini geçersiz kılın EditValue . Bu uygulama, bir nesnenin tasarım ortamını sorgular IWindowsFormsEditorService . Başarılı olursa, bir oluşturur LightShapeSelectionControl . DropDownControlYöntemi başlatmak için çağrılır LightShapeEditor . Bu çağrıdan gelen dönüş değeri tasarım ortamına döndürülür.

    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
    

Özel Uııtypeınfo Düzenleyicisi için bir görünüm denetimi oluşturma

MarqueeLightShapeÖzelliği iki tür açık şekli destekler: Square ve Circle . Yalnızca bu değerleri Özellikler penceresi grafik olarak görüntülemek için kullanılan özel bir denetim oluşturacaksınız. Bu özel denetim UITypeEditor , Özellikler penceresi etkileşimde bulunmak için sizin tarafınızdan kullanılacaktır.

Özel UI türü düzenleyiciniz için bir görünüm denetimi oluşturmak için

  1. Projeye yeni bir UserControl öğe ekleyin MarqueeControlLibrary . Yeni kaynak dosyasına, Açık Shapeselectioncontroltemel adını verin.

  2. PanelPanel iki denetimi üzerine sürükleyin LightShapeSelectionControl . Onları ve olarak adlandırın squarePanelcirclePanel . Yan yana düzenleyin. SizeHer iki Panel denetimin özelliğini Sizeolarak ayarlayın. LocationsquarePanel Denetimin özelliğini Locationolarak ayarlayın. LocationcirclePanel Denetimin özelliğini Locationolarak ayarlayın. Son olarak, Size öğesinin özelliğini olarak ayarlayın LightShapeSelectionControlSize.

  3. LightShapeSelectionControlKaynak dosyasını LightShapeSelectionControlaçın. Dosyanın en üstünde, System.Windows.Forms.Design ad alanını içeri aktarın:

    Imports System.Windows.Forms.Design
    
    using System.Windows.Forms.Design;
    
  4. ClickVe denetimleri için olay işleyicileri squarePanel uygulayın circlePanel . Bu yöntemler CloseDropDown , özel Düzenle oturumunu sona erdirmek için çağırır UITypeEditor .

    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
    
  5. Adlı bir IWindowsFormsEditorService örnek değişkeni bildirin editorService .

    Private editorService As IWindowsFormsEditorService
    
    private IWindowsFormsEditorService editorService;
    
  6. Adlı bir MarqueeLightShape örnek değişkeni bildirin lightShapeValue .

    private MarqueeLightShape lightShapeValue = MarqueeLightShape.Square;
    
    Private lightShapeValue As MarqueeLightShape = MarqueeLightShape.Square
    
  7. LightShapeSelectionControlOluşturucuda, Click olay işleyicilerini squarePanel ve circlePanel denetimlerin Click olaylarına ekleyin. Ayrıca, MarqueeLightShape Tasarım ortamından alana değeri atayan bir Oluşturucu aşırı yüklemesi tanımlayın lightShapeValue .

    // 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
    
  8. DisposeYönteminde Click olay işleyicilerini ayırın.

    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
    
  9. Çözüm Gezgini, tüm dosyaları göster düğmesine tıklayın. LightShapeSelectionControl. Designer. cs veya LightShapeSelectionControl. Designer. vb dosyasını açın ve yönteminin varsayılan tanımını kaldırın Dispose .

  10. Özelliğini uygulayın 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
    
  11. Yöntemini geçersiz kılın OnPaint . Bu uygulama doldurulmuş bir kare ve daire çizecek. Ayrıca, bir şekil ya da diğeri etrafında kenarlık çizerek seçili değeri vurgulayacaktır.

    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
    

Tasarımcıda özel denetiminizi test etme

Bu noktada, projeyi derleyebilirsiniz MarqueeControlLibrary . Sınıfından devralan bir denetim oluşturarak MarqueeControl ve onu bir formda kullanarak uygulamanızı test edin.

Özel bir MarqueeControl uygulamasını oluşturmak için

  1. DemoMarqueeControlWindows Form Tasarımcısı açın. Bu, türün bir örneğini oluşturur DemoMarqueeControl ve türün bir örneğinde görüntüler MarqueeControlRootDesigner .

  2. Araç kutusundaMarqueeControlLibrary Components sekmesini açın. Seçim için kullanılabilir ve denetimleri görürsünüz.

  3. Denetimin bir örneğini MarqueeBorderDemoMarqueeControl tasarım yüzeyine sürükleyin. Bu MarqueeBorder denetimi üst denetime yerleştir.

  4. Denetimin bir örneğini MarqueeTextDemoMarqueeControl tasarım yüzeyine sürükleyin.

  5. Çözümü derleyin.

  6. Sağ tıklayıp DemoMarqueeControl kısayol menüsünden, animasyonu başlatmak Için DemoMarqueeControl seçeneğini belirleyin. Animasyonu durdurmak için Sınamayı Durdur ' a tıklayın.

  7. Form1'i Tasarım görünümü.

  8. Forma Button iki denetim yer alıyor. Bunları ve startButtonstopButton olarak, özellik Text değerlerini sırasıyla startButtonstopButtondeğiştirebilirsiniz.

  9. Her Click iki denetim için de olay işleyicileri Button uygulama.

  10. Araç KutusundaMarqueeControlTest Bileşenleri sekmesini açın. Kullanılabilir seçiminin olduğunu göreceğiz.

  11. Bir örneğini DemoMarqueeControlDemoMarqueeControl sürükleyin.

  12. Olay Click işleyicileri üzerinde Start ve Stop yöntemlerini DemoMarqueeControl çağırır.

    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();
    }
    
  13. Projeyi MarqueeControlTest başlangıç projesi olarak ayarlayın ve çalıştırın. Form'nizin görüntüleniyor olduğunu DemoMarqueeControl göreceğiniz. Animasyonu başlatmak için Başlat düğmesini seçin. Metnin yanıp sönmesini ve kenarlığı hareket ettiren lambalar görüyor gerekir.

Sonraki adımlar

, MarqueeControlLibrary özel denetimlerin ve ilişkili tasarımcıların basit bir uygulamasını gösterir. Bu örneği çeşitli yollarla daha karmaşık hale ebilirsiniz:

  • tasarımcıda için özellik DemoMarqueeControl değerlerini değiştirme. Daha fazla MarqueBorder denetim ekleyin ve iç içe geçmiş bir etki oluşturmak için bunları üst örneklerine yerleştirin. ve ile ilgili özellikler için UpdatePeriod farklı ayarlarla denemeler yapın.

  • Kendi uygulamalarınızı IMarqueeWidget yazma. Örneğin, yanıp sönen bir "neon işareti" veya birden çok görüntü içeren animasyonlu bir işaret oluşturabilirsiniz.

  • Tasarım zamanı deneyimini daha fazla özelleştirin. ve 'den daha fazla özelliği Enabled gölgeleyebilirsiniz Visible ve yeni özellikler eklersiniz. Alt denetimleri yerleştirme gibi yaygın görevleri basitleştirmek için yeni tasarımcı fiilleri ekleyin.

  • lisansını MarqueeControl alın.

  • Denetimlerinizin seri hale nasıl getirileceklerini ve bunlar için kodun nasıl üretileceklerini denetleme. Daha fazla bilgi için bkz. Dinamik Kaynak Kodu Oluşturma ve Derleme.

Ayrıca bkz.