Aracılığıyla paylaş


İzlenecek yol: Tasarım zamanı özelliklerinden yararlanan bir denetim oluşturma

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

Dikkat

Bu içerik .NET Framework için yazılmıştır. .NET 6 veya sonraki bir sürümü kullanıyorsanız, bu içeriği dikkatli kullanın. Tasarımcı sistemi Windows Forms için değişmiştir ve .NET Framework makalesindeki Tasarım Aracı değişiklikleri gözden geçirmeniz önemlidir.

Bu makalede, özel denetim için özel tasarımcı oluşturma işlemi gösterilmektedir. Bir tür ve adlı MarqueeControlRootDesignerilişkili bir tasarımcı sınıfı uygulayacaksınızMarqueeControl.

Bu tür, MarqueeControl animasyonlu ışıklar ve yanıp sönen metin içeren bir tiyatro çerçevesine benzer bir ekran uygular.

Bu denetimin tasarımcısı, özel bir tasarım zamanı deneyimi sağlamak için tasarım ortamıyla etkileşim kurar. Özel tasarımcı ile, animasyonlu ışıklarla ve yanıp sönen metinlerle birçok kombinasyonda özel MarqueeControl bir uygulama oluşturabilirsiniz. Birleştirilmiş denetimi, diğer Windows Forms denetimleri gibi bir formda kullanabilirsiniz.

Bu kılavuzla işiniz bittiğinde, özel denetiminiz aşağıdakine benzer olacaktır:

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

Kod listesinin tamamı için bkz . Nasıl yapılır: Tasarım Zamanı Özelliklerinden Yararlanan Bir Windows Forms Denetimi Oluşturma.

Ön koşullar

Bu kılavuzu tamamlamak için Visual Studio gerekir.

Proje oluşturma

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

Visual Studio'da yeni bir Windows Forms Uygulaması projesi oluşturun ve projeye MarqueeControlTest adını verin.

Denetim kitaplığı projesi oluşturma

  1. Çözüme bir Windows Forms Denetim Kitaplığı projesi ekleyin. Projeye MarqueeControlLibrary adını verin.

  2. Çözüm Gezgini kullanarak, seçtiğiniz dile bağlı olarak "UserControl1.cs" veya "UserControl1.vb" adlı kaynak dosyayı silerek projenin varsayılan denetimini silin.

  3. Projeye yeni UserControl bir öğe MarqueeControlLibrary ekleyin. Yeni kaynak dosyaya MarqueeControl'ün temel adını verin.

  4. Çözüm Gezgini kullanarak projede MarqueeControlLibrary yeni bir klasör oluşturun.

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

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

Özel Denetim Projesine Başvurma

Özel denetimi test etmek için projeyi kullanacaksınız MarqueeControlTest . Test projesi, derlemeye bir proje başvurusu eklediğinizde özel denetimi fark MarqueeControlLibrary eder.

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

Özel Denetim ve Özel Tasarım Aracı Tanımlama

Özel denetiminiz sınıfından UserControl türetilir. Bu, denetiminizin başka denetimler içermesine olanak tanır ve denetiminize çok sayıda varsayılan işlevsellik sağlar.

Özel denetiminizin ilişkili bir özel tasarımcısı olacaktır. Bu, özel denetiminiz için özel olarak uyarlanmış benzersiz bir tasarım deneyimi oluşturmanıza olanak tanır.

sınıfını kullanarak denetimi tasarımcısıyla ilişkilendirirsiniz DesignerAttribute . Özel denetiminizin tasarım zamanı davranışının tamamını geliştirdiğinizden özel tasarımcı arabirimini uygular IRootDesigner .

Özel denetimi ve özel tasarımcısını tanımlamak için

  1. MarqueeControl Kaynak dosyayı Kod Düzenleyicisi'nde açın. Dosyanın en üstünde 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. DesignerAttribute öğesini sınıf bildirimine MarqueeControl ekleyin. Bu, özel denetimi tasarımcısıyla 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. MarqueeControlRootDesigner Kaynak dosyayı Kod Düzenleyicisi'nde açın. Dosyanın en üstünde 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 DocumentDesigner devralacak şekilde değiştirin. ToolboxItemFilterAttribute Araç Kutusu ile tasarımcı etkileşimini belirtmek için öğesini uygulayın.

    Dekont

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

    namespace MarqueeControlLibrary.Design
    {
        [ToolboxItemFilter("MarqueeControlLibrary.MarqueeBorder", ToolboxItemFilterType.Require)]
        [ToolboxItemFilter("MarqueeControlLibrary.MarqueeText", ToolboxItemFilterType.Require)]
        public class MarqueeControlRootDesigner : DocumentDesigner
        {
    
    Namespace MarqueeControlLibrary.Design
    
        <ToolboxItemFilter("MarqueeControlLibrary.MarqueeBorder", _
        ToolboxItemFilterType.Require), _
        ToolboxItemFilter("MarqueeControlLibrary.MarqueeText", _
        ToolboxItemFilterType.Require)> _
        Public Class MarqueeControlRootDesigner
            Inherits DocumentDesigner
    
  5. sınıfı için oluşturucuyu MarqueeControlRootDesigner tanımlayın. Oluşturucu gövdesine bir WriteLine deyim 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 UserControl bir öğe MarqueeControlTest ekleyin. Yeni kaynak dosyaya DemoMarqueeControl'ün temel adını verin.

  2. DemoMarqueeControl Dosyayı Kod Düzenleyicisi'nde açın. Dosyanın üst kısmında ad alanını içeri aktarın MarqueeControlLibrary :

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

  4. Projeyi derleyin.

  5. Windows Forms Tasarım Aracı Form1'i açın.

  6. Araç Kutusu'nda MarqueeControlTest Bileşenleri sekmesini bulun ve açın. Araç Kutusu'ndan formunuza bir DemoMarqueeControl sürükleyin.

  7. Projeyi derleyin.

Projeyi Tasarım Zamanı Hata Ayıklaması için Ayarlama

Özel bir tasarım zamanı deneyimi geliştirirken, denetimlerinizde ve bileşenlerinizde hata ayıklamanız gerekir. Projenizi tasarım zamanında hata ayıklamaya izin verecek şekilde ayarlamanın basit bir yolu vardır. Daha fazla bilgi için bkz . İzlenecek Yol: Tasarım Zamanında Özel Windows Forms Denetimlerinde Hata Ayıklama.

  1. Projeye sağ tıklayın ve Özellikler'i MarqueeControlLibraryseçin.

  2. MarqueeControlLibrary Özellik Sayfaları iletişim kutusunda Hata Ayıklama sayfasını seçin.

  3. Eylemi Başlat bölümünde Dış Programı Başlat'ı seçin. Ayrı bir Visual Studio örneğinde hata ayıklayacaksınız, bu nedenle Visual Studio IDE'ye göz atmak için üç nokta (The Ellipsis button (...) in the Properties window of Visual Studio) düğmesine tıklayın. Yürütülebilir dosyanın adı devenv.exe'dir ve varsayılan konuma yüklediyseniz yolu %ProgramFiles(x86)%\Microsoft Visual Studio\2019\<edition>\Common7\IDE\devenv.exe olur.

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

  5. Bu hata ayıklama yapılandırmasını etkinleştirmek için MarqueeControlLibrary projesine sağ tıklayın ve Başlangıç Projesi Olarak Ayarla'yı seçin.

Checkpoint

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

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

  1. Kod Düzenleyicisi'nde MarqueeControlRoot Tasarım Aracı kaynak dosyasını açın ve deyimine WriteLine bir kesme noktası yerleştirin.

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

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

  3. Visual Studio'nun yeni ö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 listelenir.

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

    Visual Studio'nun hata ayıklama örneği odağı alır ve kesme noktanızda yürütme durdurulur. Hata ayıklama oturumuna devam etmek için F5 tuşuna basın.

Bu noktada, özel denetiminizi ve ilişkili özel tasarımcısını geliştirmeniz ve hatalarını ayıklamanız için her şey hazır. Bu makalenin geri kalanında denetimin ve tasarımcının özelliklerini uygulama ayrıntılarına odaklanmaktadır.

Özel Denetimi Uygulama

MarqueeControl biraz özelleştirme içeren bir UserControl sürümüdür. İki yöntemi kullanıma sunar: Startkayan yazı animasyonunu başlatan ve Stopanimasyonu durduran . arabirimini MarqueeControlIMarqueeWidgetStart uygulayan ve her alt denetimi numaralandıran ve Stop uygulayan her alt denetimde sırasıyla ve StopMarquee yöntemlerini çağıran StartMarquee alt denetimler IMarqueeWidgetiçerdiğinden.

ve denetimlerinin MarqueeBorder görünümü düzene bağlıdır, bu nedenle MarqueeControl yöntemini geçersiz kılar ve bu türün OnLayout alt denetimlerini çağırırPerformLayout.MarqueeText

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

Özel denetiminizi uygulamak için

  1. MarqueeControl Kaynak dosyayı Kod Düzenleyicisi'nde açın. Start ve Stop yöntemlerini uygulayın.

    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. OnLayout yöntemini geçersiz 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

iki MarqueeControl tür alt denetim barındıracaktır: MarqueeBorder denetim ve MarqueeText denetim.

  • MarqueeBorder: Bu denetim kenarlarının çevresinde bir "ışık" kenarlığını boyar. Işıklar sıralı olarak yanıp söner, böylece sınırda hareket ediyor gibi görünürler. Işıkların yanıp söndüğü hız adlı UpdatePeriodbir özellik tarafından kontrol edilir. Diğer bazı özel özellikler denetimin görünümünün diğer yönlerini belirler. ve StopMarqueeadlı StartMarquee iki yöntem, animasyonların ne zaman başlatılıp durdurulduğunda denetler.

  • MarqueeText: Bu denetim yanıp sönen bir dizeyi boyar. MarqueeBorder Denetim gibi, metnin yanıp söndiği hız özelliği tarafından UpdatePeriod denetlenir. Denetimin MarqueeText denetimle MarqueeBorder ortak ve StopMarquee yöntemleri de vardırStartMarquee.

Tasarım zamanında, MarqueeControlRootDesigner bu iki denetim türünün herhangi bir birleşimde öğesine MarqueeControl eklenmesine izin verir.

İki denetimin ortak özellikleri adlı IMarqueeWidgetbir arabirime katsayılanır. Bu, Kayan MarqueeControl Yazı ile ilgili tüm çocuk denetimlerini keşfetmesine ve onlara özel muamele yapmasına olanak tanır.

Düzenli animasyon özelliğini uygulamak için ad alanından System.ComponentModel nesneleri kullanacaksınızBackgroundWorker. Nesneleri kullanabilirsiniz Timer , ancak birçok IMarqueeWidget nesne mevcut olduğunda, tek kullanıcı arabirimi iş parçacığı animasyona ayak uyduramayabilir.

Ö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ı verin.

  2. IMarqueeWidget Kod Düzenleyicisi'ndekaynak dosyayı açın ve bildirimi olarak classinterfacedeğiştirin:

    // 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 kayan yazı animasyonunu IMarqueeWidget işleyen bir özelliği kullanıma açmak için arabirime 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 MarqueeControlLibrary ekleyin. Yeni kaynak dosyaya "MarqueeText" temel adını verin.

  5. Araç Kutusundan bir BackgroundWorker bileşeni denetiminize MarqueeText sürükleyin. Bu bileşen, denetimin MarqueeText kendisini zaman uyumsuz olarak güncelleştirmesine izin verir.

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

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

  7. MarqueeText Kaynak dosyayı Kod Düzenleyicisi'nde açın. 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 Label devralacak şekilde MarqueeText ve arabirimini uygulamak için bildirimini IMarqueeWidget değiş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. Kullanıma sunulan özelliklere karşılık gelen örnek değişkenlerini bildirin ve oluşturucuda başlatın. alanı, isLit metnin özelliği tarafından LightColor verilen renge boyanacak olup olmadığını 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.

    StartMarquee ve StopMarquee yöntemleri, animasyonu BackgroundWorker başlatmak ve durdurmak için bileşenin RunWorkerAsync ve CancelAsync yöntemlerini çağırır.

    Category ve Browsable öznitelikleri özelliğine uygulandığındanUpdatePeriod, Özellikler penceresi "Kayan Yazı" adlı özel bir bölümünde 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 uygulayın. İstemcilere iki özellik göstereceksiniz: LightColor ve DarkColor. Category ve Browsable öznitelikleri bu özelliklere uygulanır, bu nedenle özellikler Özellikler penceresi "Kayan Yazı" adlı özel bir bölümünde 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 DoWork ve ProgressChanged olaylarının BackgroundWorker işleyicilerini uygulayın.

    Olay işleyicisiDoWork, tarafından belirtilen UpdatePeriod milisaniye sayısı için uyku moduna geçtiğinde, kodunuz animasyonu çağırarak CancelAsyncdurdurana kadar olayı tetiklerProgressChanged.

    Olay işleyicisi ProgressChanged , yanıp sönen bir görünüm vermek için metni açık ve koyu durumu arasında değiştirir.

    // 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ğe sahiptir ve yöntemindeki OnPaint animasyon daha fazla yer alır. Prensipte, denetime MarqueeText oldukça benzer.

Denetimin MarqueeBorder alt denetimleri olabileceğinden olayların farkında Layout olması gerekir.

MarqueeBorder denetimini oluşturmak için

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

  2. Araç Kutusundan bir BackgroundWorker bileşeni denetiminize MarqueeBorder sürükleyin. Bu bileşen, denetimin MarqueeBorder kendisini zaman uyumsuz olarak güncelleştirmesine izin verir.

  3. Özellikler penceresinde, bileşenin BackgroundWorkerWorkerReportsProgress ve WorkerSupportsCancellation özelliklerinin true olarak ayarlayın. Bu ayarlar, bileşenin BackgroundWorker olayı düzenli aralıklarla oluşturmasına ProgressChanged 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 ProgressChanged olayları için DoWork işleyiciler ekleyin.

  5. MarqueeBorder Kaynak dosyayı Kod Düzenleyicisi'nde açın. 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. ve arabirimini uygulamak için öğesinin Panel bildirimini MarqueeBorder devralacak şekilde IMarqueeWidget değiştirin.

    [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 MarqueeBorder durumunu yönetmek için iki numaralandırma bildirin: MarqueeSpinDirection, kenarlık çevresinde ışıkların "döneceği" yönü belirleyen ve MarqueeLightShapeışıkların şeklini belirleyen (kare veya dairesel). Bu bildirimleri sınıf bildiriminden MarqueeBorder önce yerleştirin.

    // 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. Kullanıma sunulan özelliklere karşılık gelen örnek değişkenlerini bildirin ve oluşturucuda başlatın.

    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.

    StartMarquee ve StopMarquee yöntemleri, animasyonu BackgroundWorker başlatmak ve durdurmak için bileşenin RunWorkerAsync ve CancelAsync yöntemlerini çağırır.

    MarqueeBorder Denetim alt denetimler içerebileceğinden yöntemi, StartMarquee uygulayan IMarqueeWidgettüm alt denetimleri ve çağrıları StartMarquee numaralandırır. yöntemi benzer StopMarquee bir uygulamaya sahiptir.

    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 uygulayın. 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 DoWork ve ProgressChanged olaylarının BackgroundWorker işleyicilerini uygulayın.

    Olay işleyicisiDoWork, tarafından belirtilen UpdatePeriod milisaniye sayısı için uyku moduna geçtiğinde, kodunuz animasyonu çağırarak CancelAsyncdurdurana kadar olayı tetiklerProgressChanged.

    Olay işleyicisi ProgressChanged , diğer ışıkların açık/koyu durumunun belirlendiği "temel" ışığın konumunu artırır ve denetimin kendisini yeniden boyamasına neden olmak için yöntemini çağırır 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 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. ve yardımcı yöntemlerini IsLitDrawLightuygulayın.

    yöntemi, IsLit belirli bir konumdaki ışığın rengini belirler. "Yanan" ışıklar özelliğin LightColor verdiği renkte, "koyu" olan ışıklar ise özelliğin verdiği DarkColor renkte çizilir.

    DrawLight yöntemi uygun renk, şekil 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. OnLayout ve OnPaint yöntemlerini geçersiz kılın.

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

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

    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ım Aracı Oluşturma

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

Özellikle, denetimdeki MarqueeBorderDesignerMarqueeBorder belirli özellikleri "gölge" ve filtreleyip tasarım ortamıyla etkileşimlerini değiştirir.

Bir bileşenin özellik erişimcisine yönelik çağrıların kesilmesi "gölgelendirme" olarak bilinir. Tasarımcının kullanıcı tarafından ayarlanan değeri izlemesine ve isteğe bağlı olarak bu değeri tasarlanan bileşene geçirmesine olanak tanır.

Bu örnekte ve özellikleri, Visible kullanıcının tasarım sırasında denetimi görünmez veya devre dışı yapmasını MarqueeBorder engelleyen tarafından gölgelenirMarqueeBorderDesigner.Enabled

Tasarım Aracı özellikleri de ekleyebilir ve kaldırabilir. Bu örnekte, denetim doldurmayı Padding özellik tarafından LightSize belirtilen ışıkların MarqueeBorder boyutuna göre program aracılığıyla ayarladığından özellik tasarım zamanında kaldırılır.

için MarqueeBorderDesigner temel sınıfı, tasarım zamanında bir denetim tarafından kullanıma sunulan öznitelikleri, özellikleri ve olayları değiştirebilen yöntemlere sahip olan sınıfıdır ComponentDesigner:

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

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

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

  • Yöntemlerde her zaman ilk olarak temel uygulamayı çağırın PreFilter

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

Bu kurallara uymak, tasarım zamanı ortamındaki tüm tasarımcıların, tasarlanan tüm bileşenlerin tutarlı bir görünümüne sahip olmasını sağlar.

sınıfı, ComponentDesigner gölgeli özelliklerin değerlerini yönetmek için bir sözlük sağlar ve bu da belirli örnek değişkenleri oluşturma gereksinimini hafifletir.

Özellikleri gölgelendirmek ve filtrelemek için ö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 MarqueeBorder Tasarım Aracı temel adını verin.

  2. Kod Düzenleyicisi'nde MarqueeBorder Tasarım Aracı kaynak dosyasını açın. Dosyanın en üstünde 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. öğesinin bildirimini MarqueeBorderDesigner öğesinden ParentControlDesignerdevralacak şekilde değiştirin.

    MarqueeBorder Denetim alt denetimler içerebileceğinden, MarqueeBorderDesigner üst-alt etkileşimi işleyen öğesinden ParentControlDesignerdevralır.

    namespace MarqueeControlLibrary.Design
    {
        public class MarqueeBorderDesigner : ParentControlDesigner
        {
    
    Namespace MarqueeControlLibrary.Design
    
        Public Class MarqueeBorderDesigner
            Inherits ParentControlDesigner
    
  4. temel uygulamasını PreFilterPropertiesgeçersiz kıl.

    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. Enabled ve Visible özelliklerini uygulayın. Bu uygulamalar denetimin özelliklerini gölgeler.

    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 İşleme

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

Kullanıcılar örneklerini tasarladıkça MarqueeControl kök tasarımcınız ve alt denetimlerindeki değişiklikleri MarqueeControl izler. Tasarım zamanı ortamı, IComponentChangeServicebileşen durumundaki değişiklikleri izlemek için uygun bir hizmet sunar.

Yöntemini kullanarak GetService ortamı sorgulayarak bu hizmete bir başvuru alırsınız. Sorgu başarılı olursa tasarımcınız olay için bir işleyici ekleyebilir ve tasarım zamanında tutarlı bir durumu korumak için ComponentChanged gereken görevleri gerçekleştirebilir.

sınıfı söz konusu olduğundaMarqueeControlRootDesigner, tarafından bulunan MarqueeControlher IMarqueeWidget nesnede yöntemini çağıracaksınızRefresh. Bu, üst Size öğesi gibi özellikler değiştirildiğinde nesnenin IMarqueeWidget kendisini uygun şekilde yeniden boyamasına neden olur.

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

  1. MarqueeControlRootDesigner Kod Düzenleyicisi'nde kaynak dosyayı açın ve yöntemini geçersiz kılınInitialize. için temel uygulamasını Initialize ve sorgusunu çağırı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. Olay işleyicisini OnComponentChanged uygulayın. Gönderen bileşenin türünü test edin ve bir ise IMarqueeWidgetyöntemini çağırın Refresh .

    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ım Aracı Tasarım Aracı Fiilleri ekleme

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

Tasarımcılarınıza iki tasarımcı fiil ekleyeceksiniz: Test Çalıştır ve Testi Durdur. Bu fiiller, tasarım zamanında çalışma zamanı davranışını MarqueeControl görüntülemenizi sağlar. Bu fiiller öğesine MarqueeControlRootDesignereklenir.

Test Çalıştır çağrıldığında, fiil olay işleyicisi üzerinde MarqueeControlyöntemini çağırırStartMarquee. Testi Durdur çağrıldığında, fiil olay işleyicisi üzerinde MarqueeControlyöntemini çağırırStopMarquee. ve StopMarquee yöntemlerinin StartMarquee uygulanması, uygulayan IMarqueeWidgetdenetimlerde bu yöntemleri çağırır, bu nedenle tüm kapsanan IMarqueeWidget denetimler de teste katılır.

Özel tasarımcılarınıza tasarımcı fiilleri eklemek için

  1. MarqueeControlRootDesigner sınıfında ve OnVerbStopTestadlı OnVerbRunTest olay işleyicileri ekleyin.

    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. MarqueeControlRootDesigner , temel sınıfından bir DesignerVerbCollection devralır. İki yeni DesignerVerb nesne oluşturacak ve bunları yönteminde bu koleksiyona Initialize ekleyebilirsiniz.

    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 UITypeEditor Oluşturma

Kullanıcılar için özel bir tasarım zamanı deneyimi oluşturduğunuzda, genellikle Özellikler penceresi özel bir etkileşim oluşturmak istenir. Bunu bir UITypeEditoroluşturarak gerçekleştirebilirsiniz.

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

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

  1. MarqueeBorder Kaynak dosyayı Kod Düzenleyicisi'nde açın.

  2. sınıfının tanımındaMarqueeBorder, öğesinden UITypeEditortüretilen adlı LightShapeEditor bir sınıf bildirin.

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

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

    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. EditValue yöntemini geçersiz kılın. Bu uygulama bir IWindowsFormsEditorService nesne için tasarım ortamını sorgular. Başarılı olursa, bir LightShapeSelectionControloluşturur. DropDownControl yöntemini başlatmak için çağrılırLightShapeEditor. Bu çağrıdan döndürülen değer 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 UITypeEditor'ınız için 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 amacıyla kullanılan özel bir denetim oluşturacaksınız. Bu özel denetim, Özellikler penceresi ile etkileşime geçmek için sizin denetiminiz UITypeEditor tarafından kullanılır.

Özel kullanıcı arabirimi türü düzenleyiciniz için görünüm denetimi oluşturmak için

  1. Projeye yeni UserControl bir öğe MarqueeControlLibrary ekleyin. Yeni kaynak dosyaya LightShapeSelectionControl temel adını verin.

  2. Araç Kutusundan üzerine LightShapeSelectionControliki Panel denetim sürükleyin. Bunları ve circlePanelolarak squarePanel adlandır. Bunları yan yana düzenleyin. Size Her iki Panel denetimin de özelliğini (60, 60) olarak ayarlayın. Denetimin LocationsquarePanelözelliğini (8, 10) olarak ayarlayın. Denetimin LocationcirclePanelözelliğini (80, 10) olarak ayarlayın. Son olarak özelliğini (150, 80) olarak ayarlayın SizeLightShapeSelectionControl.

  3. LightShapeSelectionControl Kaynak dosyayı Kod Düzenleyicisi'nde açın. Dosyanın üst kısmında ad alanını içeri aktarın System.Windows.Forms.Design :

    Imports System.Windows.Forms.Design
    
    using System.Windows.Forms.Design;
    
  4. ve circlePanel denetimleri için squarePanel olay işleyicileri uygulayınClick. Bu yöntemler özel UITypeEditor düzenleme oturumunu sonlandırmak için çağrılırCloseDropDown.

    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ı editorServicebir IWindowsFormsEditorService örnek değişkeni bildirin.

    Private editorService As IWindowsFormsEditorService
    
    private IWindowsFormsEditorService editorService;
    
  6. adlı lightShapeValuebir MarqueeLightShape örnek değişkeni bildirin.

    private MarqueeLightShape lightShapeValue = MarqueeLightShape.Square;
    
    Private lightShapeValue As MarqueeLightShape = MarqueeLightShape.Square
    
  7. OluşturucudaLightShapeSelectionControl, olay işleyicilerini ve circlePanel denetimlerinin Click olaylarına squarePanel ekleyinClick. Ayrıca, değeri tasarım ortamından MarqueeLightShapelightShapeValue alana atayan bir oluşturucu aşırı yüklemesi tanımlayın.

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

    protected override void Dispose( bool disposing )
    {
        if( disposing )
        {
            // Be sure to unhook event handlers
            // to prevent "lapsed listener" leaks.
            this.squarePanel.Click -=
                new EventHandler(squarePanel_Click);
            this.circlePanel.Click -=
                new EventHandler(circlePanel_Click);
    
            if(components != null)
            {
                components.Dispose();
            }
        }
        base.Dispose( disposing );
    }
    
    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'de Tüm Dosyaları Göster düğmesine tıklayın. LightShapeSelectionControl'i açın. Tasarım Aracı.cs veya LightShapeSelectionControl. Tasarım Aracı.vb dosyasını seçin ve yönteminin varsayılan tanımını Dispose kaldırın.

  10. LightShape özelliğini uygulayın.

    // 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. OnPaint yöntemini geçersiz kılın. Bu uygulama, dolu bir kare ve daire çizer. Ayrıca, bir şeklin veya diğerinin çevresine kenarlık çizerek seçili değeri vurgular.

    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
    

özel denetiminizi Tasarım Aracı test edin

Bu noktada projeyi oluşturabilirsiniz MarqueeControlLibrary . Sınıfından MarqueeControl devralan bir denetim oluşturup bir formda kullanarak uygulamanızı test edin.

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

  1. Windows Forms Tasarım Aracı açınDemoMarqueeControl. Bu, türün bir örneğini DemoMarqueeControl oluşturur ve türün MarqueeControlRootDesigner bir örneğinde görüntüler.

  2. Araç Kutusu'nda MarqueeControlLibrary Bileşenleri sekmesini açın. Ve denetimlerini MarqueeBorderMarqueeText seçilebilir olarak görürsünüz.

  3. Denetimin bir örneğini MarqueeBorder tasarım yüzeyine DemoMarqueeControl sürükleyin. Bu MarqueeBorder denetimi üst denetime sabitleyin.

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

  5. Çözümü oluşturun.

  6. Animasyonu DemoMarqueeControl başlatmak için kısayol menüsünde ve öğesine sağ tıklayın ve Test Çalıştır seçeneğini belirleyin. Animasyonu durdurmak için Testi Durdur'a tıklayın.

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

  8. Forma iki Button denetim yerleştirin. Bunları startButton ve stopButtonolarak adlandırın Text ve özellik değerlerini sırasıyla Başlat ve Durdur olarak değiştirin.

  9. Her iki denetim için de Button olay işleyicileri uygulayınClick.

  10. Araç Kutusu'nda MarqueeControlTest Bileşenleri sekmesini açın. Seçim için kullanılabilir öğesini DemoMarqueeControl görürsünüz.

  11. Örneğini DemoMarqueeControl Form1 tasarım yüzeyine sürükleyin.

  12. Click Olay işleyicilerinde üzerinde ve Stop yöntemlerini çağırın StartDemoMarqueeControl.

    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. MarqueeControlTest Projeyi başlangıç projesi olarak ayarlayın ve çalıştırın. formunuzun DemoMarqueeControlgörüntülendiğini görürsünüz. Animasyonu başlatmak için Başlangıç düğmesini seçin. Metnin yanıp söndüğü ve ışıkların kenarlıkta hareket ettiğini görmeniz gerekir.

Sonraki adımlar

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

  • tasarımcıda için DemoMarqueeControl özellik değerlerini değiştirin. İç içe efekt oluşturmak için daha fazla MarqueBorder denetim ekleyin ve bunları üst örneklerine sabitleyin. ve ışıkla ilgili özellikler için UpdatePeriod farklı ayarlarla denemeler yapın.

  • kendi uygulamalarınızı IMarqueeWidgetyazın. Örneğin, birden çok resim içeren yanıp sönen bir "neon işareti" veya animasyonlu bir işaret oluşturabilirsiniz.

  • Tasarım zamanı deneyimini daha da özelleştirin. ve Visibledeğerinden daha fazla özelliği Enabled gölgelendirmeyi deneyebilir ve yeni özellikler ekleyebilirsiniz. Alt denetimleri yerleştirme gibi yaygın görevleri basitleştirmek için yeni tasarımcı fiilleri ekleyin.

  • lisansını sağlayın MarqueeControl.

  • Denetimlerinizin nasıl serileştirildiği ve bunlar için kodun nasıl oluşturulduğunu denetleyin. Daha fazla bilgi için bkz . Dinamik Kaynak Kodu Oluşturma ve Derleme.

Ayrıca bkz.