방법: Windows Forms 컨트롤에서 특성 적용

디자인 환경과 올바르게 상호 작용하고 런타임에 올바르게 실행되는 구성 요소 및 컨트롤을 개발하려면 클래스와 멤버에 특성을 올바르게 적용해야 합니다.

예제

다음 코드 예제에서는 사용자 지정 컨트롤에 여러 가지 특성을 사용하는 방법을 보여 줍니다. 사용된 컨트롤에서는 간단한 로깅 기능을 보여 줍니다. 해당 컨트롤이 데이터 소스에 바인딩되면 DataGridView 컨트롤의 데이터 소스에서 보낸 값을 표시합니다. 이 값이 Threshold 속성에 의해 지정된 값을 초과하면 ThresholdExceeded 이벤트가 발생됩니다.

AttributesDemoControl은 LogEntry 클래스로 값을 기록합니다. LogEntry 클래스는 템플릿 클래스이며 기록하는 형식으로 매개 변수화됩니다. 예를 들어, AttributesDemoControl이 float 형식의 값을 기록하는 경우 다음과 같이 각 LogEntry 인스턴스가 선언되고 사용됩니다.

' This method handles the timer's Elapsed event. It queries
' the performance counter for the next value, packs the 
' value in a LogEntry object, and adds the new LogEntry to
' the list managed by the BindingSource.
 Private Sub timer1_Elapsed( _
 ByVal sender As Object, _
 ByVal e As System.Timers.ElapsedEventArgs) _
 Handles timer1.Elapsed

     ' Get the latest value from the performance counter.
     Dim val As Single = Me.performanceCounter1.NextValue()

     ' The performance counter returns values of type float, 
     ' but any type that implements the IComparable interface
     ' will work.
     Dim entry As LogEntry(Of Single) = _
     New LogEntry(Of Single)(val, DateTime.Now)

     ' Add the new LogEntry to the BindingSource list.
     Me.bindingSource1.Add(entry)
 End Sub
        // This method handles the timer's Elapsed event. It queries
        // the performance counter for the next value, packs the 
        // value in a LogEntry object, and adds the new LogEntry to
        // the list managed by the BindingSource.
        private void timer1_Elapsed(
            object sender, 
            System.Timers.ElapsedEventArgs e)
        {   
            // Get the latest value from the performance counter.
            float val = this.performanceCounter1.NextValue();

            // The performance counter returns values of type float, 
            // but any type that implements the IComparable interface
            // will work.
            LogEntry<float> entry = new LogEntry<float>(val, DateTime.Now);

            // Add the new LogEntry to the BindingSource list.
            this.bindingSource1.Add(entry);
        }

참고

LogEntry는 임의의 형식에 의해 매개 변수화되므로 매개 변수 형식에서 동작하려면 리플렉션을 사용해야 합니다. 임계값 기능이 동작하려면 매개 변수 형식 T에서 IComparable 인터페이스를 구현해야 합니다.

AttributesDemoControl을 호스팅하는 폼은 성능 카운터를 정기적으로 쿼리합니다. 각 값은 적절한 형식의 LogEntry에 패키지되며 폼의 BindingSource에 추가됩니다. AttributesDemoControl은 해당 데이터 바인딩을 통해 값을 받으며 DataGridView 컨트롤에 해당 값을 표시합니다.

Imports System
Imports System.Collections.Generic
Imports System.ComponentModel
Imports System.ComponentModel.Design
Imports System.Diagnostics
Imports System.Drawing
Imports System.Data
Imports System.Reflection
Imports System.Text
Imports System.Windows.Forms

' This sample demonstrates the use of various attributes for
' authoring a control. 
Namespace AttributesDemoControlLibrary

    ' This is the event handler delegate for the ThresholdExceeded event.
    Delegate Sub ThresholdExceededEventHandler(ByVal e As ThresholdExceededEventArgs)

    ' This control demonstrates a simple logging capability. 
    <ComplexBindingProperties("DataSource", "DataMember"), _
    DefaultBindingProperty("TitleText"), _
    DefaultEvent("ThresholdExceeded"), _
    DefaultProperty("Threshold"), _
    HelpKeywordAttribute(GetType(UserControl)), _
    ToolboxItem("System.Windows.Forms.Design.AutoSizeToolboxItem,System.Design")> _
    Public Class AttributesDemoControl
        Inherits UserControl

        ' This backs the Threshold property.
        Private thresholdValue As Object

        ' The default fore color value for DataGridView cells that
        ' contain values that exceed the threshold.
        Private Shared defaultAlertForeColorValue As Color = Color.White

        ' The default back color value for DataGridView cells that
        ' contain values that exceed the threshold.
        Private Shared defaultAlertBackColorValue As Color = Color.Red

        ' The ambient color value.
        Private Shared ambientColorValue As Color = Color.Empty

        ' The fore color value for DataGridView cells that
        ' contain values that exceed the threshold.
        Private alertForeColorValue As Color = defaultAlertForeColorValue

        ' The back color value for DataGridView cells that
        ' contain values that exceed the threshold.
        Private alertBackColorValue As Color = defaultAlertBackColorValue

        ' Child controls that comprise this UserControl.
        Private tableLayoutPanel1 As TableLayoutPanel
        Private WithEvents dataGridView1 As DataGridView
        Private label1 As Label

        ' Required for designer support.
        Private components As System.ComponentModel.IContainer = Nothing

        ' Default constructor.
        Public Sub New()
            InitializeComponent()
        End Sub

        <Category("Appearance"), _
        Description("The title of the log data."), _
        DesignerSerializationVisibility(DesignerSerializationVisibility.Visible), Localizable(True), _
        HelpKeywordAttribute("AttributesDemoControlLibrary.AttributesDemoControl.TitleText")> _
        Public Property TitleText() As String
            Get
                Return Me.label1.Text
            End Get

            Set(ByVal value As String)
                Me.label1.Text = value
            End Set
        End Property

        ' The inherited Text property is hidden at design time and 
        ' raises an exception at run time. This enforces a requirement
        ' that client code uses the TitleText property instead.
        <Browsable(False), _
        EditorBrowsable(EditorBrowsableState.Never), _
        DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)> _
        Public Overrides Property Text() As String
            Get
                Throw New NotSupportedException()
            End Get
            Set(ByVal value As String)
                Throw New NotSupportedException()
            End Set
        End Property

        <AmbientValue(GetType(Color), "Empty"), _
        Category("Appearance"), _
        DefaultValue(GetType(Color), "White"), _
        Description("The color used for painting alert text.")> _
        Public Property AlertForeColor() As Color
            Get
                If Me.alertForeColorValue = Color.Empty AndAlso (Me.Parent IsNot Nothing) Then
                    Return Parent.ForeColor
                End If

                Return Me.alertForeColorValue
            End Get

            Set(ByVal value As Color)
                Me.alertForeColorValue = value
            End Set
        End Property

        ' This method is used by designers to enable resetting the
        ' property to its default value.
        Public Sub ResetAlertForeColor()
            Me.AlertForeColor = AttributesDemoControl.defaultAlertForeColorValue
        End Sub

        ' This method indicates to designers whether the property
        ' value is different from the ambient value, in which case
        ' the designer should persist the value.
        Private Function ShouldSerializeAlertForeColor() As Boolean
            Return Me.alertForeColorValue <> AttributesDemoControl.ambientColorValue
        End Function

        <AmbientValue(GetType(Color), "Empty"), _
        Category("Appearance"), _
        DefaultValue(GetType(Color), "Red"), _
        Description("The background color for painting alert text.")> _
        Public Property AlertBackColor() As Color
            Get
                If Me.alertBackColorValue = Color.Empty AndAlso (Me.Parent IsNot Nothing) Then
                    Return Parent.BackColor
                End If

                Return Me.alertBackColorValue
            End Get

            Set(ByVal value As Color)
                Me.alertBackColorValue = value
            End Set
        End Property

        ' This method is used by designers to enable resetting the
        ' property to its default value.
        Public Sub ResetAlertBackColor()
            Me.AlertBackColor = AttributesDemoControl.defaultAlertBackColorValue
        End Sub

        ' This method indicates to designers whether the property
        ' value is different from the ambient value, in which case
        ' the designer should persist the value.
        Private Function ShouldSerializeAlertBackColor() As Boolean
            Return Me.alertBackColorValue <> AttributesDemoControl.ambientColorValue
        End Function 'ShouldSerializeAlertBackColor

        <Category("Data"), _
        Description("Indicates the source of data for the control."), _
        RefreshProperties(RefreshProperties.Repaint), _
        AttributeProvider(GetType(IListSource))> _
        Public Property DataSource() As Object
            Get
                Return Me.dataGridView1.DataSource
            End Get

            Set(ByVal value As Object)
                Me.dataGridView1.DataSource = value
            End Set
        End Property

        <Category("Data"), _
        Description("Indicates a sub-list of the data source to show in the control.")> _
        Public Property DataMember() As String
            Get
                Return Me.dataGridView1.DataMember
            End Get

            Set(ByVal value As String)
                Me.dataGridView1.DataMember = value
            End Set
        End Property

        ' This property would normally have its BrowsableAttribute 
        ' set to false, but this code demonstrates using 
        ' ReadOnlyAttribute, so BrowsableAttribute is true to show 
        ' it in any attached PropertyGrid control.
        <Browsable(True), _
        Category("Behavior"), _
        Description("The timestamp of the latest entry."), _
        ReadOnlyAttribute(True)> _
        Public Property CurrentLogTime() As DateTime
            Get
                Dim lastRowIndex As Integer = _
                Me.dataGridView1.Rows.GetLastRow(DataGridViewElementStates.Visible)

                If lastRowIndex > -1 Then
                    Dim lastRow As DataGridViewRow = Me.dataGridView1.Rows(lastRowIndex)
                    Dim lastCell As DataGridViewCell = lastRow.Cells("EntryTime")
                    Return CType(lastCell.Value, DateTime)
                Else
                    Return DateTime.MinValue
                End If
            End Get

            Set(ByVal value As DateTime)
            End Set
        End Property

        <Category("Behavior"), _
        Description("The value above which the ThresholdExceeded event will be raised.")> _
        Public Property Threshold() As Object
            Get
                Return Me.thresholdValue
            End Get

            Set(ByVal value As Object)
                Me.thresholdValue = value
            End Set
        End Property

        ' This property exists only to demonstrate the 
        ' PasswordPropertyText attribute. When this control 
        ' is attached to a PropertyGrid control, the returned 
        ' string will be displayed with obscuring characters
        ' such as asterisks. This property has no other effect.
        <Category("Security"), _
        Description("Demonstrates PasswordPropertyTextAttribute."), _
        PasswordPropertyText(True)> _
        Public ReadOnly Property Password() As String
            Get
                Return "This is a demo password."
            End Get
        End Property

        ' This property exists only to demonstrate the 
        ' DisplayName attribute. When this control 
        ' is attached to a PropertyGrid control, the
        ' property will be appear as "RenamedProperty"
        ' instead of "MisnamedProperty".
        <Description("Demonstrates DisplayNameAttribute."), _
        DisplayName("RenamedProperty")> _
        Public ReadOnly Property MisnamedProperty() As Boolean
            Get
                Return True
            End Get
        End Property

        ' This is the declaration for the ThresholdExceeded event.
        'Public Event ThresholdExceeded As ThresholdExceededEventHandler
        Public Event ThresholdExceeded(ByVal e As ThresholdExceededEventArgs)

#Region "Implementation"

        ' This is the event handler for the DataGridView control's 
        ' CellFormatting event. Handling this event allows the 
        ' AttributesDemoControl to examine the incoming log entries
        ' from the data source as they arrive.
        '
        ' If the cell for which this event is raised holds the
        ' log entry's timestamp, the cell value is formatted with 
        ' the full date/time pattern. 
        ' 
        ' Otherwise, the cell's value is assumed to hold the log 
        ' entry value. If the value exceeds the threshold value, 
        ' the cell is painted with the colors specified by the
        ' AlertForeColor and AlertBackColor properties, after which
        ' the ThresholdExceeded is raised. For this comparison to 
        ' succeed, the log entry's type must implement the IComparable 
        ' interface.
        Private Sub dataGridView1_CellFormatting( _
        ByVal sender As Object, _
        ByVal e As DataGridViewCellFormattingEventArgs) _
        Handles dataGridView1.CellFormatting
            Try
                If (e.Value IsNot Nothing) Then
                    If TypeOf e.Value Is DateTime Then
                        ' Display the log entry time with the 
                        ' full date/time pattern (long time).
                        e.CellStyle.Format = "F"
                    Else
                        ' Scroll to the most recent entry.
                        Dim row As DataGridViewRow = Me.dataGridView1.Rows(e.RowIndex)
                        Dim cell As DataGridViewCell = row.Cells(e.ColumnIndex)
                        Me.dataGridView1.FirstDisplayedCell = cell

                        If (Me.thresholdValue IsNot Nothing) Then
                            ' Get the type of the log entry.
                            Dim val As Object = e.Value
                            Dim paramType As Type = val.GetType()

                            ' Compare the log entry value to the threshold value.
                            ' Use reflection to call the CompareTo method on the
                            ' template parameter's type. 
                            Dim compareVal As Integer = _
                            Fix(paramType.InvokeMember("CompareTo", _
                            BindingFlags.Default Or BindingFlags.InvokeMethod, _
                            Nothing, _
                            e.Value, _
                            New Object() {Me.thresholdValue}, _
                            System.Globalization.CultureInfo.InvariantCulture))

                            ' If the log entry value exceeds the threshold value,
                            ' set the cell's fore color and back color properties
                            ' and raise the ThresholdExceeded event.
                            If compareVal > 0 Then
                                e.CellStyle.BackColor = Me.alertBackColorValue
                                e.CellStyle.ForeColor = Me.alertForeColorValue

                                Dim teea As New ThresholdExceededEventArgs(Me.thresholdValue, e.Value)
                                RaiseEvent ThresholdExceeded(teea)
                            End If
                        End If
                    End If
                End If
            Catch ex As Exception
                Trace.WriteLine(ex.Message)
            End Try
        End Sub

        Protected Overrides Sub Dispose(ByVal disposing As Boolean)
            If disposing AndAlso (components IsNot Nothing) Then
                components.Dispose()
            End If
            MyBase.Dispose(disposing)
        End Sub


        Private Sub InitializeComponent()
            Dim DataGridViewCellStyle1 As System.Windows.Forms.DataGridViewCellStyle = New System.Windows.Forms.DataGridViewCellStyle
            Me.tableLayoutPanel1 = New System.Windows.Forms.TableLayoutPanel
            Me.dataGridView1 = New System.Windows.Forms.DataGridView
            Me.label1 = New System.Windows.Forms.Label
            Me.tableLayoutPanel1.SuspendLayout()
            CType(Me.dataGridView1, System.ComponentModel.ISupportInitialize).BeginInit()
            Me.SuspendLayout()
            '
            'tableLayoutPanel1
            '
            Me.tableLayoutPanel1.AutoSize = True
            Me.tableLayoutPanel1.ColumnCount = 1
            Me.tableLayoutPanel1.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 100.0!))
            Me.tableLayoutPanel1.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 100.0!))
            Me.tableLayoutPanel1.Controls.Add(Me.dataGridView1, 0, 1)
            Me.tableLayoutPanel1.Controls.Add(Me.label1, 0, 0)
            Me.tableLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill
            Me.tableLayoutPanel1.Location = New System.Drawing.Point(10, 10)
            Me.tableLayoutPanel1.Name = "tableLayoutPanel1"
            Me.tableLayoutPanel1.Padding = New System.Windows.Forms.Padding(10)
            Me.tableLayoutPanel1.RowCount = 2
            Me.tableLayoutPanel1.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 10.0!))
            Me.tableLayoutPanel1.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 80.0!))
            Me.tableLayoutPanel1.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 10.0!))
            Me.tableLayoutPanel1.Size = New System.Drawing.Size(425, 424)
            Me.tableLayoutPanel1.TabIndex = 0
            '
            'dataGridView1
            '
            Me.dataGridView1.AllowUserToAddRows = False
            Me.dataGridView1.AllowUserToDeleteRows = False
            Me.dataGridView1.AllowUserToOrderColumns = True
            Me.dataGridView1.ColumnHeadersHeightSizeMode = DataGridViewColumnHeadersHeightSizeMode.AutoSize
            Me.dataGridView1.AutoSizeRowsMode = System.Windows.Forms.DataGridViewAutoSizeRowsMode.AllCells
            DataGridViewCellStyle1.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleLeft
            DataGridViewCellStyle1.BackColor = System.Drawing.SystemColors.Control
            DataGridViewCellStyle1.ForeColor = System.Drawing.SystemColors.WindowText
            DataGridViewCellStyle1.SelectionBackColor = System.Drawing.SystemColors.Highlight
            DataGridViewCellStyle1.SelectionForeColor = System.Drawing.SystemColors.HighlightText
            DataGridViewCellStyle1.WrapMode = System.Windows.Forms.DataGridViewTriState.[False]
            Me.dataGridView1.ColumnHeadersDefaultCellStyle = DataGridViewCellStyle1
            Me.dataGridView1.ColumnHeadersHeight = 4
            Me.dataGridView1.Dock = System.Windows.Forms.DockStyle.Fill
            Me.dataGridView1.Location = New System.Drawing.Point(13, 57)
            Me.dataGridView1.Name = "dataGridView1"
            Me.dataGridView1.ReadOnly = True
            Me.dataGridView1.RowHeadersVisible = False
            Me.dataGridView1.Size = New System.Drawing.Size(399, 354)
            Me.dataGridView1.TabIndex = 1
            '
            'label1
            '
            Me.label1.AutoSize = True
            Me.label1.BackColor = System.Drawing.SystemColors.Control
            Me.label1.Dock = System.Windows.Forms.DockStyle.Fill
            Me.label1.Location = New System.Drawing.Point(13, 13)
            Me.label1.Name = "label1"
            Me.label1.Size = New System.Drawing.Size(399, 38)
            Me.label1.TabIndex = 2
            Me.label1.Text = "label1"
            Me.label1.TextAlign = System.Drawing.ContentAlignment.MiddleCenter
            '
            'AttributesDemoControl
            '
            Me.Controls.Add(Me.tableLayoutPanel1)
            Me.Name = "AttributesDemoControl"
            Me.Padding = New System.Windows.Forms.Padding(10)
            Me.Size = New System.Drawing.Size(445, 444)
            Me.tableLayoutPanel1.ResumeLayout(False)
            Me.tableLayoutPanel1.PerformLayout()
            CType(Me.dataGridView1, System.ComponentModel.ISupportInitialize).EndInit()
            Me.ResumeLayout(False)
            Me.PerformLayout()

        End Sub 'InitializeComponent 

#End Region

    End Class

    ' This is the EventArgs class for the ThresholdExceeded event.
    Public Class ThresholdExceededEventArgs
        Inherits EventArgs
        Private thresholdVal As Object = Nothing
        Private exceedingVal As Object = Nothing

        Public Sub New(ByVal thresholdValue As Object, ByVal exceedingValue As Object)
            Me.thresholdVal = thresholdValue
            Me.exceedingVal = exceedingValue
        End Sub

        Public ReadOnly Property ThresholdValue() As Object
            Get
                Return Me.thresholdVal
            End Get
        End Property

        Public ReadOnly Property ExceedingValue() As Object
            Get
                Return Me.exceedingVal
            End Get
        End Property
    End Class

    ' This class encapsulates a log entry. It is a parameterized 
    ' type (also known as a template class). The parameter type T
    ' defines the type of data being logged. For threshold detection
    ' to work, this type must implement the IComparable interface.
    <TypeConverter("LogEntryTypeConverter")> _
    Public Class LogEntry(Of T As IComparable)

        Private entryValue As T

        Private entryTimeValue As DateTime


        Public Sub New(ByVal value As T, ByVal time As DateTime)
            Me.entryValue = value
            Me.entryTimeValue = time
        End Sub


        Public ReadOnly Property Entry() As T
            Get
                Return Me.entryValue
            End Get
        End Property


        Public ReadOnly Property EntryTime() As DateTime
            Get
                Return Me.entryTimeValue
            End Get
        End Property

        ' This is the TypeConverter for the LogEntry class.  
        Public Class LogEntryTypeConverter
            Inherits TypeConverter

            Public Overrides Function CanConvertFrom( _
            ByVal context As ITypeDescriptorContext, _
            ByVal sourceType As Type) As Boolean
                If sourceType Is GetType(String) Then
                    Return True
                End If

                Return MyBase.CanConvertFrom(context, sourceType)
            End Function

            Public Overrides Function ConvertFrom( _
            ByVal context As ITypeDescriptorContext, _
            ByVal culture As System.Globalization.CultureInfo, _
            ByVal value As Object) As Object
                If TypeOf value Is String Then
                    Dim v As String() = CStr(value).Split(New Char() {"|"c})

                    Dim paramType As Type = GetType(T)
                    Dim entryValue As T = CType(paramType.InvokeMember("Parse", BindingFlags.Static Or BindingFlags.Public Or BindingFlags.InvokeMethod, Nothing, Nothing, New String() {v(0)}, culture), T)

                    Return New LogEntry(Of T)(entryValue, DateTime.Parse(v(2))) '

                End If

                Return MyBase.ConvertFrom(context, culture, value)
            End Function

            Public Overrides Function ConvertTo(ByVal context As ITypeDescriptorContext, ByVal culture As System.Globalization.CultureInfo, ByVal value As Object, ByVal destinationType As Type) As Object
                If destinationType Is GetType(String) Then

                    Dim le As LogEntry(Of T) = CType(value, LogEntry(Of T))

                    Dim stringRepresentation As String = String.Format("{0} | {1}", le.Entry, le.EntryTime)

                    Return stringRepresentation
                End If

                Return MyBase.ConvertTo(context, culture, value, destinationType)
            End Function
        End Class
    End Class

End Namespace
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.Diagnostics;
using System.Drawing;
using System.Data;
using System.Reflection;
using System.Text;
using System.Windows.Forms;

// This sample demonstrates the use of various attributes for
// authoring a control. 
namespace AttributesDemoControlLibrary
{
    // This is the event handler delegate for the ThresholdExceeded event.
    public delegate void ThresholdExceededEventHandler(ThresholdExceededEventArgs e);

    // This control demonstrates a simple logging capability. 
    [ComplexBindingProperties("DataSource", "DataMember")]
    [DefaultBindingProperty("TitleText")]
    [DefaultEvent("ThresholdExceeded")]
    [DefaultProperty("Threshold")]
    [HelpKeywordAttribute(typeof(UserControl))]
    [ToolboxItem("System.Windows.Forms.Design.AutoSizeToolboxItem,System.Design")]
    public class AttributesDemoControl : UserControl
    {

        // This backs the Threshold property.
        private object thresholdValue;

        // The default fore color value for DataGridView cells that
        // contain values that exceed the threshold.
        private static Color defaultAlertForeColorValue = Color.White;

        // The default back color value for DataGridView cells that
        // contain values that exceed the threshold.
        private static Color defaultAlertBackColorValue = Color.Red;

        // The ambient color value.
        private static Color ambientColorValue = Color.Empty;

        // The fore color value for DataGridView cells that
        // contain values that exceed the threshold.
        private Color alertForeColorValue = defaultAlertForeColorValue;

        // The back color value for DataGridView cells that
        // contain values that exceed the threshold.
        private Color alertBackColorValue = defaultAlertBackColorValue;

        // Child controls that comprise this UserControl.
        private TableLayoutPanel tableLayoutPanel1;
        private DataGridView dataGridView1;
        private Label label1;

        // Required for designer support.
        private System.ComponentModel.IContainer components = null;

        // Default constructor.
        public AttributesDemoControl()
        {
            InitializeComponent();
        }

        [Category("Appearance")]
        [Description("The title of the log data.")]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
        [Localizable(true)]
        [HelpKeywordAttribute("AttributesDemoControlLibrary.AttributesDemoControl.TitleText")]
        public string TitleText
        {
            get
            {
                return this.label1.Text;
            }

            set
            {
                this.label1.Text = value;
            }
        }

        // The inherited Text property is hidden at design time and 
        // raises an exception at run time. This enforces a requirement
        // that client code uses the TitleText property instead.
        [Browsable(false)]
        [EditorBrowsable(EditorBrowsableState.Never)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public override string Text
        {
            get
            {
                throw new NotSupportedException();
            }
            set
            {
                throw new NotSupportedException();
            }
        }

        [AmbientValue(typeof(Color), "Empty")]
        [Category("Appearance")]
        [DefaultValue(typeof(Color), "White")]
        [Description("The color used for painting alert text.")]
        public Color AlertForeColor
        {
            get
            {
                if (this.alertForeColorValue == Color.Empty &&
                    this.Parent != null)
                {
                    return Parent.ForeColor;
                }

                return this.alertForeColorValue;
            }

            set
            {
                this.alertForeColorValue = value;
            }
        }

        // This method is used by designers to enable resetting the
        // property to its default value.
        public void ResetAlertForeColor()
        {
            this.AlertForeColor = AttributesDemoControl.defaultAlertForeColorValue;
        }

        // This method indicates to designers whether the property
        // value is different from the ambient value, in which case
        // the designer should persist the value.
        private bool ShouldSerializeAlertForeColor()
        {
            return (this.alertForeColorValue != AttributesDemoControl.ambientColorValue);
        }

        [AmbientValue(typeof(Color), "Empty")]
        [Category("Appearance")]
        [DefaultValue(typeof(Color), "Red")]
        [Description("The background color for painting alert text.")]
        public Color AlertBackColor
        {
            get
            {
                if (this.alertBackColorValue == Color.Empty &&
                    this.Parent != null)
                {
                    return Parent.BackColor;
                }

                return this.alertBackColorValue;
            }

            set
            {
                this.alertBackColorValue = value;
            }
        }

        // This method is used by designers to enable resetting the
        // property to its default value.
        public void ResetAlertBackColor()
        {
            this.AlertBackColor = AttributesDemoControl.defaultAlertBackColorValue;
        }

        // This method indicates to designers whether the property
        // value is different from the ambient value, in which case
        // the designer should persist the value.
        private bool ShouldSerializeAlertBackColor()
        {
            return (this.alertBackColorValue != AttributesDemoControl.ambientColorValue);
        }

        [Category("Data")]
        [Description("Indicates the source of data for the control.")]
        [RefreshProperties(RefreshProperties.Repaint)]
        [AttributeProvider(typeof(IListSource))]
        public object DataSource
        {
            get
            {
                return this.dataGridView1.DataSource;
            }

            set
            {
                this.dataGridView1.DataSource = value;
            }
        }

        [Category("Data")]
        [Description("Indicates a sub-list of the data source to show in the control.")]
        public string DataMember
        {
            get
            {
                return this.dataGridView1.DataMember;
            }

            set
            {
                this.dataGridView1.DataMember = value;
            }
        }

        // This property would normally have its BrowsableAttribute 
        // set to false, but this code demonstrates using 
        // ReadOnlyAttribute, so BrowsableAttribute is true to show 
        // it in any attached PropertyGrid control.
        [Browsable(true)]
        [Category("Behavior")]
        [Description("The timestamp of the latest entry.")]
        [ReadOnly(true)]
        public DateTime CurrentLogTime
        {
            get
            {
                int lastRowIndex = 
                    this.dataGridView1.Rows.GetLastRow(
                    DataGridViewElementStates.Visible);

                if (lastRowIndex > -1)
                {
                    DataGridViewRow lastRow = this.dataGridView1.Rows[lastRowIndex];
                    DataGridViewCell lastCell = lastRow.Cells["EntryTime"];
                    return ((DateTime)lastCell.Value);
                }
                else
                {
                    return DateTime.MinValue;
                }
            }

            set
            {
            }
        }

        [Category("Behavior")]
        [Description("The value above which the ThresholdExceeded event will be raised.")]
        public object Threshold
        {
            get
            {
                return this.thresholdValue;
            }

            set
            {
                this.thresholdValue = value;
            }
        }

        // This property exists only to demonstrate the 
        // PasswordPropertyText attribute. When this control 
        // is attached to a PropertyGrid control, the returned 
        // string will be displayed with obscuring characters
        // such as asterisks. This property has no other effect.
        [Category("Security")]
        [Description("Demonstrates PasswordPropertyTextAttribute.")]
        [PasswordPropertyText(true)]
        public string Password
        {
            get
            {
                return "This is a demo password.";
            }
        }

        // This property exists only to demonstrate the 
        // DisplayName attribute. When this control 
        // is attached to a PropertyGrid control, the
        // property will be appear as "RenamedProperty"
        // instead of "MisnamedProperty".
        [Description("Demonstrates DisplayNameAttribute.")]
        [DisplayName("RenamedProperty")]
        public bool MisnamedProperty
        {
            get
            {
                return true;
            }
        }

        // This is the declaration for the ThresholdExceeded event.
        public event ThresholdExceededEventHandler ThresholdExceeded;

        #region Implementation

        // This is the event handler for the DataGridView control's 
        // CellFormatting event. Handling this event allows the 
        // AttributesDemoControl to examine the incoming log entries
        // from the data source as they arrive.
        //
        // If the cell for which this event is raised holds the
        // log entry's timestamp, the cell value is formatted with 
        // the full date/time pattern. 
        // 
        // Otherwise, the cell's value is assumed to hold the log 
        // entry value. If the value exceeds the threshold value, 
        // the cell is painted with the colors specified by the
        // AlertForeColor and AlertBackColor properties, after which
        // the ThresholdExceeded is raised. For this comparison to 
        // succeed, the log entry's type must implement the IComparable 
        // interface.
        private void dataGridView1_CellFormatting(
            object sender,
            DataGridViewCellFormattingEventArgs e)
        {
            try
            {
                if (e.Value != null)
                {
                    if (e.Value is DateTime)
                    {
                        // Display the log entry time with the 
                        // full date/time pattern (long time).
                        e.CellStyle.Format = "F";
                    }
                    else
                    {
                        // Scroll to the most recent entry.
                        DataGridViewRow row = this.dataGridView1.Rows[e.RowIndex];
                        DataGridViewCell cell = row.Cells[e.ColumnIndex];
                        this.dataGridView1.FirstDisplayedCell = cell;

                        if (this.thresholdValue != null)
                        {
                            // Get the type of the log entry.
                            object val = e.Value;
                            Type paramType = val.GetType();

                            // Compare the log entry value to the threshold value.
                            // Use reflection to call the CompareTo method on the
                            // template parameter's type. 
                            int compareVal = (int)paramType.InvokeMember(
                                "CompareTo",
                                BindingFlags.Default | BindingFlags.InvokeMethod,
                                null,
                                e.Value,
                                new object[] { this.thresholdValue },
                                System.Globalization.CultureInfo.InvariantCulture);

                            // If the log entry value exceeds the threshold value,
                            // set the cell's fore color and back color properties
                            // and raise the ThresholdExceeded event.
                            if (compareVal > 0)
                            {
                                e.CellStyle.BackColor = this.alertBackColorValue;
                                e.CellStyle.ForeColor = this.alertForeColorValue;

                                ThresholdExceededEventArgs teea =
                                    new ThresholdExceededEventArgs(
                                    this.thresholdValue,
                                    e.Value);
                                this.ThresholdExceeded(teea);
                            }
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                Trace.WriteLine(ex.Message);
            }
        }

        protected override void Dispose(bool disposing)
        {
            if (disposing && (components != null))
            {
                components.Dispose();
            }
            base.Dispose(disposing);
        }

        private void InitializeComponent()
        {
            System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle1 = new System.Windows.Forms.DataGridViewCellStyle();
            this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel();
            this.dataGridView1 = new System.Windows.Forms.DataGridView();
            this.label1 = new System.Windows.Forms.Label();
            this.tableLayoutPanel1.SuspendLayout();
            ((System.ComponentModel.ISupportInitialize)(this.dataGridView1)).BeginInit();
            this.SuspendLayout();
            // 
            // tableLayoutPanel1
            // 
            this.tableLayoutPanel1.AutoSize = true;
            this.tableLayoutPanel1.ColumnCount = 1;
            this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 100F));
            this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 100F));
            this.tableLayoutPanel1.Controls.Add(this.dataGridView1, 0, 1);
            this.tableLayoutPanel1.Controls.Add(this.label1, 0, 0);
            this.tableLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill;
            this.tableLayoutPanel1.Location = new System.Drawing.Point(10, 10);
            this.tableLayoutPanel1.Name = "tableLayoutPanel1";
            this.tableLayoutPanel1.Padding = new System.Windows.Forms.Padding(10);
            this.tableLayoutPanel1.RowCount = 2;
            this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 10F));
            this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 80F));
            this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 10F));
            this.tableLayoutPanel1.Size = new System.Drawing.Size(425, 424);
            this.tableLayoutPanel1.TabIndex = 0;
            // 
            // dataGridView1
            // 
            this.dataGridView1.AllowUserToAddRows = false;
            this.dataGridView1.AllowUserToDeleteRows = false;
            this.dataGridView1.AllowUserToOrderColumns = true;
            this.dataGridView1.ColumnHeadersHeightSizeMode = DataGridViewColumnHeadersHeightSizeMode.AutoSize;
            this.dataGridView1.AutoSizeRowsMode = System.Windows.Forms.DataGridViewAutoSizeRowsMode.AllCells;
            dataGridViewCellStyle1.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleLeft;
            dataGridViewCellStyle1.BackColor = System.Drawing.SystemColors.Control;
            dataGridViewCellStyle1.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
            dataGridViewCellStyle1.ForeColor = System.Drawing.SystemColors.WindowText;
            dataGridViewCellStyle1.SelectionBackColor = System.Drawing.SystemColors.Highlight;
            dataGridViewCellStyle1.SelectionForeColor = System.Drawing.SystemColors.HighlightText;
            dataGridViewCellStyle1.WrapMode = System.Windows.Forms.DataGridViewTriState.False;
            this.dataGridView1.ColumnHeadersDefaultCellStyle = dataGridViewCellStyle1;
            this.dataGridView1.ColumnHeadersHeight = 4;
            this.dataGridView1.Dock = System.Windows.Forms.DockStyle.Fill;
            this.dataGridView1.Location = new System.Drawing.Point(13, 57);
            this.dataGridView1.Name = "dataGridView1";
            this.dataGridView1.ReadOnly = true;
            this.dataGridView1.RowHeadersVisible = false;
            this.dataGridView1.Size = new System.Drawing.Size(399, 354);
            this.dataGridView1.TabIndex = 1;
            this.dataGridView1.CellFormatting += new System.Windows.Forms.DataGridViewCellFormattingEventHandler(this.dataGridView1_CellFormatting);
            // 
            // label1
            // 
            this.label1.AutoSize = true;
            this.label1.BackColor = System.Drawing.SystemColors.Control;
            this.label1.Dock = System.Windows.Forms.DockStyle.Fill;
            this.label1.Location = new System.Drawing.Point(13, 13);
            this.label1.Name = "label1";
            this.label1.Size = new System.Drawing.Size(399, 38);
            this.label1.TabIndex = 2;
            this.label1.Text = "label1";
            this.label1.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
            // 
            // AttributesDemoControl
            // 
            this.Controls.Add(this.tableLayoutPanel1);
            this.Name = "AttributesDemoControl";
            this.Padding = new System.Windows.Forms.Padding(10);
            this.Size = new System.Drawing.Size(445, 444);
            this.tableLayoutPanel1.ResumeLayout(false);
            this.tableLayoutPanel1.PerformLayout();
            ((System.ComponentModel.ISupportInitialize)(this.dataGridView1)).EndInit();
            this.ResumeLayout(false);
            this.PerformLayout();

        }

        #endregion
    }

    // This is the EventArgs class for the ThresholdExceeded event.
    public class ThresholdExceededEventArgs : EventArgs
    {
        private object thresholdValue = null;
        private object exceedingValue = null;

        public ThresholdExceededEventArgs(
            object thresholdValue,
            object exceedingValue)
        {
            this.thresholdValue = thresholdValue;
            this.exceedingValue = exceedingValue;
        }

        public object ThresholdValue
        {
            get
            {
                return this.thresholdValue;
            }
        }

        public object ExceedingValue
        {
            get
            {
                return this.exceedingValue;
            }
        }
    }

    // This class encapsulates a log entry. It is a parameterized 
    // type (also known as a template class). The parameter type T
    // defines the type of data being logged. For threshold detection
    // to work, this type must implement the IComparable interface.
    [TypeConverter("LogEntryTypeConverter")]
    public class LogEntry<T> where T : IComparable
    {
        private T entryValue;
        private DateTime entryTimeValue;

        public LogEntry(
            T value,
            DateTime time)
        {
            this.entryValue = value;
            this.entryTimeValue = time;
        }

        public T Entry
        {
            get
            {
                return this.entryValue;
            }
        }

        public DateTime EntryTime
        {
            get
            {
                return this.entryTimeValue;
            }
        }

        // This is the TypeConverter for the LogEntry class.
        public class LogEntryTypeConverter : TypeConverter
        {
            public override bool CanConvertFrom(
                ITypeDescriptorContext context,
                Type sourceType)
            {
                if (sourceType == typeof(string))
                {
                    return true;
                }

                return base.CanConvertFrom(context, sourceType);
            }

            public override object ConvertFrom(
                ITypeDescriptorContext context,
                System.Globalization.CultureInfo culture,
                object value)
            {
                if (value is string)
                {
                    string[] v = ((string)value).Split(new char[] { '|' });

                    Type paramType = typeof(T);
                    T entryValue = (T)paramType.InvokeMember(
                        "Parse",
                        BindingFlags.Static | BindingFlags.Public | BindingFlags.InvokeMethod,
                        null,
                        null,
                        new string[] { v[0] },
                        culture);

                    return new LogEntry<T>(
                        entryValue,
                        DateTime.Parse(v[2]));
                }

                return base.ConvertFrom(context, culture, value);
            }

            public override object ConvertTo(
                ITypeDescriptorContext context,
                System.Globalization.CultureInfo culture,
                object value,
                Type destinationType)
            {
                if (destinationType == typeof(string))
                {
                    LogEntry<T> le = value as LogEntry<T>;

                    string stringRepresentation =
                        String.Format("{0} | {1}",
                        le.Entry,
                        le.EntryTime);

                    return stringRepresentation;
                }

                return base.ConvertTo(context, culture, value, destinationType);
            }
        }
    }
}
Imports System
Imports System.Collections.Generic
Imports System.ComponentModel
Imports System.Diagnostics
Imports System.Drawing
Imports System.Windows.Forms
Imports AttributesDemoControlLibrary

' This sample demonstrates using the AttributesDemoControl to log
' data from a data source. 
Public Class Form1
   Inherits Form
   Private bindingSource1 As BindingSource
   Private performanceCounter1 As System.Diagnostics.PerformanceCounter
   Private WithEvents startButton As Button
   Private WithEvents stopButton As Button
   Private WithEvents timer1 As System.Timers.Timer
    Private statusStrip1 As ToolStripStatusLabel
    Private statusStripPanel1 As ToolStripStatusLabel
   Private WithEvents numericUpDown1 As NumericUpDown
   Private groupBox1 As GroupBox
   Private groupBox2 As GroupBox
   Private tableLayoutPanel1 As TableLayoutPanel
    Private WithEvents attributesDemoControl1 As AttributesDemoControl
   Private components As System.ComponentModel.IContainer = Nothing

   ' This form uses an AttributesDemoControl to display a stream
   ' of LogEntry objects. The data stream is generated by polling
   ' a performance counter and communicating the counter values 
   ' to the control with data binding.
   Public Sub New()
      InitializeComponent()

      ' Set the initial value of the threshold up/down control 
      ' to the control's threshold value.
        Me.numericUpDown1.Value = _
        System.Convert.ToDecimal( _
        System.Convert.ToSingle(Me.attributesDemoControl1.Threshold))

      ' Assign the performance counter's name to the control's 
      ' title text.
        Me.attributesDemoControl1.TitleText = _
        Me.performanceCounter1.CounterName
    End Sub


   ' This method handles the ThresholdExceeded event. It posts
   ' a the value that exceeded the threshold to the status strip.  
    Private Sub attributesDemoControl1_ThresholdExceeded( _
    ByVal e As ThresholdExceededEventArgs) _
    Handles attributesDemoControl1.ThresholdExceeded
        Dim msg As String = String.Format( _
        "{0}: Value {1} exceeded threshold {2}", _
        Me.attributesDemoControl1.CurrentLogTime, _
        e.ExceedingValue, _
        e.ThresholdValue)

        Me.ReportStatus(msg)
    End Sub

   ' This method handles the timer's Elapsed event. It queries
   ' the performance counter for the next value, packs the 
   ' value in a LogEntry object, and adds the new LogEntry to
   ' the list managed by the BindingSource.
    Private Sub timer1_Elapsed( _
    ByVal sender As Object, _
    ByVal e As System.Timers.ElapsedEventArgs) _
    Handles timer1.Elapsed

        ' Get the latest value from the performance counter.
        Dim val As Single = Me.performanceCounter1.NextValue()

        ' The performance counter returns values of type float, 
        ' but any type that implements the IComparable interface
        ' will work.
        Dim entry As LogEntry(Of Single) = _
        New LogEntry(Of Single)(val, DateTime.Now)

        ' Add the new LogEntry to the BindingSource list.
        Me.bindingSource1.Add(entry)
    End Sub

    Private Sub numericUpDown1_ValueChanged( _
    ByVal sender As Object, _
    ByVal e As EventArgs) _
    Handles numericUpDown1.ValueChanged

        Me.attributesDemoControl1.Threshold = _
        System.Convert.ToSingle(Me.numericUpDown1.Value)

        Dim msg As String = String.Format( _
        "Threshold changed to {0}", _
        Me.attributesDemoControl1.Threshold)

        Me.ReportStatus(msg)
    End Sub

    Private Sub startButton_Click( _
    ByVal sender As Object, _
    ByVal e As EventArgs) _
    Handles startButton.Click

        Me.ReportStatus((DateTime.Now.ToString() + ": Starting"))

        Me.timer1.Start()
    End Sub


    Private Sub stopButton_Click( _
    ByVal sender As Object, _
    ByVal e As EventArgs) _
    Handles stopButton.Click

        Me.ReportStatus((DateTime.Now.ToString() + ": Stopping"))

        Me.timer1.Stop()
    End Sub


   Private Sub ReportStatus(msg As String)
      If (msg IsNot Nothing) Then
         Me.statusStripPanel1.Text = msg
      End If
    End Sub


   <STAThread()>  _
   Shared Sub Main()
      Application.EnableVisualStyles()
      Application.Run(New Form1())
    End Sub


   Protected Overrides Sub Dispose(disposing As Boolean)
      If disposing AndAlso (components IsNot Nothing) Then
         components.Dispose()
      End If
      MyBase.Dispose(disposing)
    End Sub


   Private Sub InitializeComponent()
      Me.components = New System.ComponentModel.Container()
      Me.bindingSource1 = New System.Windows.Forms.BindingSource(Me.components)
      Me.performanceCounter1 = New System.Diagnostics.PerformanceCounter()
      Me.startButton = New System.Windows.Forms.Button()
      Me.stopButton = New System.Windows.Forms.Button()
      Me.timer1 = New System.Timers.Timer()
        Me.statusStripPanel1 = New System.Windows.Forms.ToolStripStatusLabel()
      Me.numericUpDown1 = New System.Windows.Forms.NumericUpDown()
      Me.groupBox1 = New System.Windows.Forms.GroupBox()
      Me.groupBox2 = New System.Windows.Forms.GroupBox()
      Me.tableLayoutPanel1 = New System.Windows.Forms.TableLayoutPanel()
      Me.attributesDemoControl1 = New AttributesDemoControlLibrary.AttributesDemoControl()
      CType(Me.bindingSource1, System.ComponentModel.ISupportInitialize).BeginInit()
      CType(Me.performanceCounter1, System.ComponentModel.ISupportInitialize).BeginInit()
      CType(Me.timer1, System.ComponentModel.ISupportInitialize).BeginInit()
      CType(Me.numericUpDown1, System.ComponentModel.ISupportInitialize).BeginInit()
      Me.groupBox1.SuspendLayout()
      Me.groupBox2.SuspendLayout()
      Me.tableLayoutPanel1.SuspendLayout()
      Me.SuspendLayout()
      ' 
      ' performanceCounter1
      ' 
      Me.performanceCounter1.CategoryName = ".NET CLR Memory"
      Me.performanceCounter1.CounterName = "Gen 0 heap size"
      Me.performanceCounter1.InstanceName = "_Global_"
      ' 
      ' startButton
      ' 
      Me.startButton.Location = New System.Drawing.Point(31, 25)
      Me.startButton.Name = "startButton"
      Me.startButton.TabIndex = 1
      Me.startButton.Text = "Start"
      ' 
      ' stopButton
      ' 
      Me.stopButton.Location = New System.Drawing.Point(112, 25)
      Me.stopButton.Name = "stopButton"
      Me.stopButton.TabIndex = 2
      Me.stopButton.Text = "Stop"
      ' 
      ' timer1
      ' 
      Me.timer1.Interval = 1000
      Me.timer1.SynchronizingObject = Me
      ' 
      ' statusStripPanel1
      ' 
      Me.statusStripPanel1.BorderStyle = System.Windows.Forms.Border3DStyle.SunkenOuter
      Me.statusStripPanel1.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Text
      Me.statusStripPanel1.Name = "statusStripPanel1"
      Me.statusStripPanel1.Text = "Ready"
      ' 
      ' numericUpDown1
      ' 
      Me.numericUpDown1.Location = New System.Drawing.Point(37, 29)
      Me.numericUpDown1.Maximum = New Decimal(New Integer() {1410065408, 2, 0, 0})
      Me.numericUpDown1.Name = "numericUpDown1"
      Me.numericUpDown1.TabIndex = 7
      ' 
      ' groupBox1
      ' 
      Me.groupBox1.Anchor = System.Windows.Forms.AnchorStyles.None
      Me.groupBox1.Controls.Add(Me.numericUpDown1)
      Me.groupBox1.Location = New System.Drawing.Point(280, 326)
      Me.groupBox1.Name = "groupBox1"
      Me.groupBox1.Size = New System.Drawing.Size(200, 70)
      Me.groupBox1.TabIndex = 13
      Me.groupBox1.TabStop = False
      Me.groupBox1.Text = "Threshold Value"
      ' 
      ' groupBox2
      ' 
      Me.groupBox2.Anchor = System.Windows.Forms.AnchorStyles.None
      Me.groupBox2.Controls.Add(Me.startButton)
      Me.groupBox2.Controls.Add(Me.stopButton)
      Me.groupBox2.Location = New System.Drawing.Point(26, 327)
      Me.groupBox2.Name = "groupBox2"
      Me.groupBox2.Size = New System.Drawing.Size(214, 68)
      Me.groupBox2.TabIndex = 14
      Me.groupBox2.TabStop = False
      Me.groupBox2.Text = "Logging"
      ' 
      ' tableLayoutPanel1
      ' 
      Me.tableLayoutPanel1.ColumnCount = 2
      Me.tableLayoutPanel1.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50F))
      Me.tableLayoutPanel1.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50F))
      Me.tableLayoutPanel1.Controls.Add(Me.groupBox2, 0, 1)
      Me.tableLayoutPanel1.Controls.Add(Me.groupBox1, 1, 1)
      Me.tableLayoutPanel1.Controls.Add(Me.attributesDemoControl1, 0, 0)
      Me.tableLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill
      Me.tableLayoutPanel1.Location = New System.Drawing.Point(0, 0)
      Me.tableLayoutPanel1.Name = "tableLayoutPanel1"
      Me.tableLayoutPanel1.Padding = New System.Windows.Forms.Padding(10)
      Me.tableLayoutPanel1.RowCount = 2
      Me.tableLayoutPanel1.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 80F))
      Me.tableLayoutPanel1.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 20F))
      Me.tableLayoutPanel1.Size = New System.Drawing.Size(514, 411)
      Me.tableLayoutPanel1.TabIndex = 15
      ' 
      ' attributesDemoControl1
      ' 
      Me.tableLayoutPanel1.SetColumnSpan(Me.attributesDemoControl1, 2)
      Me.attributesDemoControl1.DataMember = ""
      Me.attributesDemoControl1.DataSource = Me.bindingSource1
      Me.attributesDemoControl1.Dock = System.Windows.Forms.DockStyle.Fill
      Me.attributesDemoControl1.Location = New System.Drawing.Point(13, 13)
      Me.attributesDemoControl1.Name = "attributesDemoControl1"
      Me.attributesDemoControl1.Padding = New System.Windows.Forms.Padding(10)
      Me.attributesDemoControl1.Size = New System.Drawing.Size(488, 306)
      Me.attributesDemoControl1.TabIndex = 0
      Me.attributesDemoControl1.Threshold = 200000F
      Me.attributesDemoControl1.TitleText = "TITLE"
      ' 
      ' Form1
      '
      Me.BackColor = System.Drawing.SystemColors.Control
      Me.ClientSize = New System.Drawing.Size(514, 430)
      Me.Controls.Add(tableLayoutPanel1)
      Me.Name = "Form1"
      Me.Text = "Form1"
      CType(Me.bindingSource1, System.ComponentModel.ISupportInitialize).EndInit()
      CType(Me.performanceCounter1, System.ComponentModel.ISupportInitialize).EndInit()
      CType(Me.timer1, System.ComponentModel.ISupportInitialize).EndInit()
      CType(Me.numericUpDown1, System.ComponentModel.ISupportInitialize).EndInit()
      Me.groupBox1.ResumeLayout(False)
      Me.groupBox2.ResumeLayout(False)
      Me.tableLayoutPanel1.ResumeLayout(False)
      Me.ResumeLayout(False)
      Me.PerformLayout()
    End Sub
End Class
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Drawing;
using System.Windows.Forms;
using AttributesDemoControlLibrary;

// This sample demonstrates using the AttributesDemoControl to log
// data from a data source. 
namespace AttributesDemoControlTest
{
    public class Form1 : Form
    {   
        private BindingSource bindingSource1;
        private System.Diagnostics.PerformanceCounter performanceCounter1;
        private Button startButton;
        private Button stopButton;
        private System.Timers.Timer timer1;
        private ToolStripStatusLabel statusStripPanel1;
        private NumericUpDown numericUpDown1;
        private GroupBox groupBox1;
        private GroupBox groupBox2;
        private TableLayoutPanel tableLayoutPanel1;
        private AttributesDemoControl attributesDemoControl1;
        private System.ComponentModel.IContainer components = null;

        // This form uses an AttributesDemoControl to display a stream
        // of LogEntry objects. The data stream is generated by polling
        // a performance counter and communicating the counter values 
        // to the control with data binding.
        public Form1()
        {
            InitializeComponent();

            // Set the initial value of the threshold up/down control 
            // to the control's threshold value.
            this.numericUpDown1.Value = 
                (decimal)(float)this.attributesDemoControl1.Threshold;

            // Assign the performance counter's name to the control's 
            // title text.
            this.attributesDemoControl1.TitleText = 
                this.performanceCounter1.CounterName;
        }

        // This method handles the ThresholdExceeded event. It posts
        // the value that exceeded the threshold to the status strip.  
        private void attributesDemoControl1_ThresholdExceeded(
            ThresholdExceededEventArgs e)
        {
            string msg = String.Format(
                "{0}: Value {1} exceeded threshold {2}", 
                this.attributesDemoControl1.CurrentLogTime, 
                e.ExceedingValue, 
                e.ThresholdValue);

            this.ReportStatus( msg );
        }

        // This method handles the timer's Elapsed event. It queries
        // the performance counter for the next value, packs the 
        // value in a LogEntry object, and adds the new LogEntry to
        // the list managed by the BindingSource.
        private void timer1_Elapsed(
            object sender, 
            System.Timers.ElapsedEventArgs e)
        {   
            // Get the latest value from the performance counter.
            float val = this.performanceCounter1.NextValue();

            // The performance counter returns values of type float, 
            // but any type that implements the IComparable interface
            // will work.
            LogEntry<float> entry = new LogEntry<float>(val, DateTime.Now);

            // Add the new LogEntry to the BindingSource list.
            this.bindingSource1.Add(entry);
        }

        private void numericUpDown1_ValueChanged(object sender, EventArgs e)
        {
            this.attributesDemoControl1.Threshold = 
                (float)this.numericUpDown1.Value;

            string msg = String.Format(
                "Threshold changed to {0}", 
                this.attributesDemoControl1.Threshold);

            this.ReportStatus(msg);
        }

        private void startButton_Click(object sender, EventArgs e)
        {
            this.ReportStatus(DateTime.Now + ": Starting");

            this.timer1.Start();
        }

        private void stopButton_Click(object sender, EventArgs e)
        {
            this.ReportStatus(DateTime.Now + ": Stopping");

            this.timer1.Stop();
        }

        private void ReportStatus(string msg)
        {
            if (msg != null)
            {
                this.statusStripPanel1.Text = msg;
            }
        }

        [STAThread]
        static void Main()
        {
            Application.EnableVisualStyles();
            Application.Run(new Form1());
        }

        protected override void Dispose(bool disposing)
        {
            if (disposing && (components != null))
            {
                components.Dispose();
            }
            base.Dispose(disposing);
        }

        private void InitializeComponent()
        {
            this.components = new System.ComponentModel.Container();
            this.bindingSource1 = new System.Windows.Forms.BindingSource(this.components);
            this.performanceCounter1 = new System.Diagnostics.PerformanceCounter();
            this.startButton = new System.Windows.Forms.Button();
            this.stopButton = new System.Windows.Forms.Button();
            this.timer1 = new System.Timers.Timer();
            this.statusStripPanel1 = new System.Windows.Forms.ToolStripStatusLabel();
            this.numericUpDown1 = new System.Windows.Forms.NumericUpDown();
            this.groupBox1 = new System.Windows.Forms.GroupBox();
            this.groupBox2 = new System.Windows.Forms.GroupBox();
            this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel();
            this.attributesDemoControl1 = new AttributesDemoControlLibrary.AttributesDemoControl();
            ((System.ComponentModel.ISupportInitialize)(this.bindingSource1)).BeginInit();
            ((System.ComponentModel.ISupportInitialize)(this.performanceCounter1)).BeginInit();
            ((System.ComponentModel.ISupportInitialize)(this.timer1)).BeginInit();
            
            
            ((System.ComponentModel.ISupportInitialize)(this.numericUpDown1)).BeginInit();
            this.groupBox1.SuspendLayout();
            this.groupBox2.SuspendLayout();
            this.tableLayoutPanel1.SuspendLayout();
            this.SuspendLayout();
            // 
            // performanceCounter1
            // 
            this.performanceCounter1.CategoryName = ".NET CLR Memory";
            this.performanceCounter1.CounterName = "Gen 0 heap size";
            this.performanceCounter1.InstanceName = "_Global_";
            // 
            // startButton
            // 
            this.startButton.Location = new System.Drawing.Point(31, 25);
            this.startButton.Name = "startButton";
            this.startButton.TabIndex = 1;
            this.startButton.Text = "Start";
            this.startButton.Click += new System.EventHandler(this.startButton_Click);
            // 
            // stopButton
            // 
            this.stopButton.Location = new System.Drawing.Point(112, 25);
            this.stopButton.Name = "stopButton";
            this.stopButton.TabIndex = 2;
            this.stopButton.Text = "Stop";
            this.stopButton.Click += new System.EventHandler(this.stopButton_Click);
            // 
            // timer1
            // 
            this.timer1.Interval = 1000;
            this.timer1.SynchronizingObject = this;
            this.timer1.Elapsed += new System.Timers.ElapsedEventHandler(this.timer1_Elapsed);
            // 
            // statusStripPanel1
            // 
            this.statusStripPanel1.BorderStyle = System.Windows.Forms.Border3DStyle.SunkenOuter;
            this.statusStripPanel1.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Text;
            this.statusStripPanel1.Name = "statusStripPanel1";
            this.statusStripPanel1.Text = "Ready";
            // 
            // numericUpDown1
            // 
            this.numericUpDown1.Location = new System.Drawing.Point(37, 29);
            this.numericUpDown1.Maximum = new decimal(new int[] {
            1410065408,
            2,
            0,
            0});
            this.numericUpDown1.Name = "numericUpDown1";
            this.numericUpDown1.TabIndex = 7;
            this.numericUpDown1.ValueChanged += new System.EventHandler(this.numericUpDown1_ValueChanged);
            // 
            // groupBox1
            // 
            this.groupBox1.Anchor = System.Windows.Forms.AnchorStyles.None;
            this.groupBox1.Controls.Add(this.numericUpDown1);
            this.groupBox1.Location = new System.Drawing.Point(280, 326);
            this.groupBox1.Name = "groupBox1";
            this.groupBox1.Size = new System.Drawing.Size(200, 70);
            this.groupBox1.TabIndex = 13;
            this.groupBox1.TabStop = false;
            this.groupBox1.Text = "Threshold Value";
            // 
            // groupBox2
            // 
            this.groupBox2.Anchor = System.Windows.Forms.AnchorStyles.None;
            this.groupBox2.Controls.Add(this.startButton);
            this.groupBox2.Controls.Add(this.stopButton);
            this.groupBox2.Location = new System.Drawing.Point(26, 327);
            this.groupBox2.Name = "groupBox2";
            this.groupBox2.Size = new System.Drawing.Size(214, 68);
            this.groupBox2.TabIndex = 14;
            this.groupBox2.TabStop = false;
            this.groupBox2.Text = "Logging";
            // 
            // tableLayoutPanel1
            // 
            this.tableLayoutPanel1.ColumnCount = 2;
            this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50F));
            this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50F));
            this.tableLayoutPanel1.Controls.Add(this.groupBox2, 0, 1);
            this.tableLayoutPanel1.Controls.Add(this.groupBox1, 1, 1);
            this.tableLayoutPanel1.Controls.Add(this.attributesDemoControl1, 0, 0);
            this.tableLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill;
            this.tableLayoutPanel1.Location = new System.Drawing.Point(0, 0);
            this.tableLayoutPanel1.Name = "tableLayoutPanel1";
            this.tableLayoutPanel1.Padding = new System.Windows.Forms.Padding(10);
            this.tableLayoutPanel1.RowCount = 2;
            this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 80F));
            this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 20F));
            this.tableLayoutPanel1.Size = new System.Drawing.Size(514, 411);
            this.tableLayoutPanel1.TabIndex = 15;
            // 
            // attributesDemoControl1
            // 
            this.tableLayoutPanel1.SetColumnSpan(this.attributesDemoControl1, 2);
            this.attributesDemoControl1.DataMember = "";
            this.attributesDemoControl1.DataSource = this.bindingSource1;
            this.attributesDemoControl1.Dock = System.Windows.Forms.DockStyle.Fill;
            this.attributesDemoControl1.Location = new System.Drawing.Point(13, 13);
            this.attributesDemoControl1.Name = "attributesDemoControl1";
            this.attributesDemoControl1.Padding = new System.Windows.Forms.Padding(10);
            this.attributesDemoControl1.Size = new System.Drawing.Size(488, 306);
            this.attributesDemoControl1.TabIndex = 0;
            this.attributesDemoControl1.Threshold = 200000F;
            this.attributesDemoControl1.TitleText = "TITLE";
            this.attributesDemoControl1.ThresholdExceeded += new AttributesDemoControlLibrary.ThresholdExceededEventHandler(this.attributesDemoControl1_ThresholdExceeded);
            // 
            // Form1
            // 
            this.BackColor = System.Drawing.SystemColors.Control;
            this.ClientSize = new System.Drawing.Size(514, 430);
            this.Controls.Add(this.tableLayoutPanel1);
            this.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
            this.Name = "Form1";
            this.Text = "Form1";
            ((System.ComponentModel.ISupportInitialize)(this.bindingSource1)).EndInit();
            ((System.ComponentModel.ISupportInitialize)(this.performanceCounter1)).EndInit();
            ((System.ComponentModel.ISupportInitialize)(this.timer1)).EndInit();
            ((System.ComponentModel.ISupportInitialize)(this.numericUpDown1)).EndInit();
            this.groupBox1.ResumeLayout(false);
            this.groupBox2.ResumeLayout(false);
            this.tableLayoutPanel1.ResumeLayout(false);
            this.ResumeLayout(false);
            this.PerformLayout();

        }
    }
}

첫 번째 코드 예제에서는 AttributesDemoControl을 구현합니다. 두 번째 코드 예제에서는 AttributesDemoControl을 사용하는 폼을 보여 줍니다.

클래스 수준 특성

일부 특성은 클래스 수준에서 적용됩니다. 다음 코드 예제에서는 Windows Forms 컨트롤에 일반적으로 적용되는 특성을 보여 줍니다.

' This control demonstrates a simple logging capability. 
<ComplexBindingProperties("DataSource", "DataMember"), _
DefaultBindingProperty("TitleText"), _
DefaultEvent("ThresholdExceeded"), _
DefaultProperty("Threshold"), _
HelpKeywordAttribute(GetType(UserControl)), _
ToolboxItem("System.Windows.Forms.Design.AutoSizeToolboxItem,System.Design")> _
Public Class AttributesDemoControl
    Inherits UserControl
// This control demonstrates a simple logging capability. 
[ComplexBindingProperties("DataSource", "DataMember")]
[DefaultBindingProperty("TitleText")]
[DefaultEvent("ThresholdExceeded")]
[DefaultProperty("Threshold")]
[HelpKeywordAttribute(typeof(UserControl))]
[ToolboxItem("System.Windows.Forms.Design.AutoSizeToolboxItem,System.Design")]
public class AttributesDemoControl : UserControl
{

TypeConverter 특성

TypeConverterAttribute는 일반적으로 사용되는 클래스 수준 특성입니다. 다음 코드 예제에서는 LogEntry 클래스에 대해 이 특성을 사용하는 방법을 보여 줍니다. 또한 이 예제에서는 LogEntry 형식에 대해 LogEntryTypeConverter라는 TypeConverter를 구현하는 방법도 보여 줍니다.

' This class encapsulates a log entry. It is a parameterized 
' type (also known as a template class). The parameter type T
' defines the type of data being logged. For threshold detection
' to work, this type must implement the IComparable interface.
<TypeConverter("LogEntryTypeConverter")> _
Public Class LogEntry(Of T As IComparable)

    Private entryValue As T

    Private entryTimeValue As DateTime


    Public Sub New(ByVal value As T, ByVal time As DateTime)
        Me.entryValue = value
        Me.entryTimeValue = time
    End Sub


    Public ReadOnly Property Entry() As T
        Get
            Return Me.entryValue
        End Get
    End Property


    Public ReadOnly Property EntryTime() As DateTime
        Get
            Return Me.entryTimeValue
        End Get
    End Property

    ' This is the TypeConverter for the LogEntry class.  
    Public Class LogEntryTypeConverter
        Inherits TypeConverter

        Public Overrides Function CanConvertFrom( _
        ByVal context As ITypeDescriptorContext, _
        ByVal sourceType As Type) As Boolean
            If sourceType Is GetType(String) Then
                Return True
            End If

            Return MyBase.CanConvertFrom(context, sourceType)
        End Function

        Public Overrides Function ConvertFrom( _
        ByVal context As ITypeDescriptorContext, _
        ByVal culture As System.Globalization.CultureInfo, _
        ByVal value As Object) As Object
            If TypeOf value Is String Then
                Dim v As String() = CStr(value).Split(New Char() {"|"c})

                Dim paramType As Type = GetType(T)
                Dim entryValue As T = CType(paramType.InvokeMember("Parse", BindingFlags.Static Or BindingFlags.Public Or BindingFlags.InvokeMethod, Nothing, Nothing, New String() {v(0)}, culture), T)

                Return New LogEntry(Of T)(entryValue, DateTime.Parse(v(2))) '

            End If

            Return MyBase.ConvertFrom(context, culture, value)
        End Function

        Public Overrides Function ConvertTo(ByVal context As ITypeDescriptorContext, ByVal culture As System.Globalization.CultureInfo, ByVal value As Object, ByVal destinationType As Type) As Object
            If destinationType Is GetType(String) Then

                Dim le As LogEntry(Of T) = CType(value, LogEntry(Of T))

                Dim stringRepresentation As String = String.Format("{0} | {1}", le.Entry, le.EntryTime)

                Return stringRepresentation
            End If

            Return MyBase.ConvertTo(context, culture, value, destinationType)
        End Function
    End Class
End Class
// This class encapsulates a log entry. It is a parameterized 
// type (also known as a template class). The parameter type T
// defines the type of data being logged. For threshold detection
// to work, this type must implement the IComparable interface.
[TypeConverter("LogEntryTypeConverter")]
public class LogEntry<T> where T : IComparable
{
    private T entryValue;
    private DateTime entryTimeValue;

    public LogEntry(
        T value,
        DateTime time)
    {
        this.entryValue = value;
        this.entryTimeValue = time;
    }

    public T Entry
    {
        get
        {
            return this.entryValue;
        }
    }

    public DateTime EntryTime
    {
        get
        {
            return this.entryTimeValue;
        }
    }

    // This is the TypeConverter for the LogEntry class.
    public class LogEntryTypeConverter : TypeConverter
    {
        public override bool CanConvertFrom(
            ITypeDescriptorContext context,
            Type sourceType)
        {
            if (sourceType == typeof(string))
            {
                return true;
            }

            return base.CanConvertFrom(context, sourceType);
        }

        public override object ConvertFrom(
            ITypeDescriptorContext context,
            System.Globalization.CultureInfo culture,
            object value)
        {
            if (value is string)
            {
                string[] v = ((string)value).Split(new char[] { '|' });

                Type paramType = typeof(T);
                T entryValue = (T)paramType.InvokeMember(
                    "Parse",
                    BindingFlags.Static | BindingFlags.Public | BindingFlags.InvokeMethod,
                    null,
                    null,
                    new string[] { v[0] },
                    culture);

                return new LogEntry<T>(
                    entryValue,
                    DateTime.Parse(v[2]));
            }

            return base.ConvertFrom(context, culture, value);
        }

        public override object ConvertTo(
            ITypeDescriptorContext context,
            System.Globalization.CultureInfo culture,
            object value,
            Type destinationType)
        {
            if (destinationType == typeof(string))
            {
                LogEntry<T> le = value as LogEntry<T>;

                string stringRepresentation =
                    String.Format("{0} | {1}",
                    le.Entry,
                    le.EntryTime);

                return stringRepresentation;
            }

            return base.ConvertTo(context, culture, value, destinationType);
        }
    }
}

멤버 수준 특성

일부 특성은 멤버 수준에서 적용됩니다. 다음 코드 예제에서는 Windows Forms 컨트롤의 속성에 일반적으로 적용되는 일부 특성을 보여 줍니다.


<Category("Appearance"), _
Description("The title of the log data."), _
DesignerSerializationVisibility(DesignerSerializationVisibility.Visible), Localizable(True), _
HelpKeywordAttribute("AttributesDemoControlLibrary.AttributesDemoControl.TitleText")> _
Public Property TitleText() As String
    Get
        Return Me.label1.Text
    End Get

    Set(ByVal value As String)
        Me.label1.Text = value
    End Set
End Property
[Category("Appearance")]
[Description("The title of the log data.")]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
[Localizable(true)]
[HelpKeywordAttribute("AttributesDemoControlLibrary.AttributesDemoControl.TitleText")]
public string TitleText
{
    get
    {
        return this.label1.Text;
    }

    set
    {
        this.label1.Text = value;
    }
}

AmbientValue 특성

다음 예제에서는 AmbientValueAttribute를 보여 주고 디자인 환경과의 상호 작용을 지원하는 코드를 보여 줍니다. 이러한 상호 작용을 앰비언스라고 합니다.

<AmbientValue(GetType(Color), "Empty"), _
Category("Appearance"), _
DefaultValue(GetType(Color), "White"), _
Description("The color used for painting alert text.")> _
Public Property AlertForeColor() As Color
    Get
        If Me.alertForeColorValue = Color.Empty AndAlso (Me.Parent IsNot Nothing) Then
            Return Parent.ForeColor
        End If

        Return Me.alertForeColorValue
    End Get

    Set(ByVal value As Color)
        Me.alertForeColorValue = value
    End Set
End Property

' This method is used by designers to enable resetting the
' property to its default value.
Public Sub ResetAlertForeColor()
    Me.AlertForeColor = AttributesDemoControl.defaultAlertForeColorValue
End Sub

' This method indicates to designers whether the property
' value is different from the ambient value, in which case
' the designer should persist the value.
Private Function ShouldSerializeAlertForeColor() As Boolean
    Return Me.alertForeColorValue <> AttributesDemoControl.ambientColorValue
End Function
[AmbientValue(typeof(Color), "Empty")]
[Category("Appearance")]
[DefaultValue(typeof(Color), "White")]
[Description("The color used for painting alert text.")]
public Color AlertForeColor
{
    get
    {
        if (this.alertForeColorValue == Color.Empty &&
            this.Parent != null)
        {
            return Parent.ForeColor;
        }

        return this.alertForeColorValue;
    }

    set
    {
        this.alertForeColorValue = value;
    }
}

// This method is used by designers to enable resetting the
// property to its default value.
public void ResetAlertForeColor()
{
    this.AlertForeColor = AttributesDemoControl.defaultAlertForeColorValue;
}

// This method indicates to designers whether the property
// value is different from the ambient value, in which case
// the designer should persist the value.
private bool ShouldSerializeAlertForeColor()
{
    return (this.alertForeColorValue != AttributesDemoControl.ambientColorValue);
}

데이터 바인딩 특성

다음 예제에서는 복합 데이터 바인딩의 구현을 보여 줍니다. 위에서 볼 수 있는 클래스 수준의 ComplexBindingPropertiesAttribute는 데이터 바인딩에 사용할 DataSource 및 DataMember 속성을 지정합니다. AttributeProviderAttribute는 DataSource 속성이 바인딩될 형식을 지정합니다.

<Category("Data"), _
Description("Indicates the source of data for the control."), _
RefreshProperties(RefreshProperties.Repaint), _
AttributeProvider(GetType(IListSource))> _
Public Property DataSource() As Object
    Get
        Return Me.dataGridView1.DataSource
    End Get

    Set(ByVal value As Object)
        Me.dataGridView1.DataSource = value
    End Set
End Property
[Category("Data")]
[Description("Indicates the source of data for the control.")]
[RefreshProperties(RefreshProperties.Repaint)]
[AttributeProvider(typeof(IListSource))]
public object DataSource
{
    get
    {
        return this.dataGridView1.DataSource;
    }

    set
    {
        this.dataGridView1.DataSource = value;
    }
}
<Category("Data"), _
Description("Indicates a sub-list of the data source to show in the control.")> _
Public Property DataMember() As String
    Get
        Return Me.dataGridView1.DataMember
    End Get

    Set(ByVal value As String)
        Me.dataGridView1.DataMember = value
    End Set
End Property
[Category("Data")]
[Description("Indicates a sub-list of the data source to show in the control.")]
public string DataMember
{
    get
    {
        return this.dataGridView1.DataMember;
    }

    set
    {
        this.dataGridView1.DataMember = value;
    }
}

코드 컴파일

  • AttributesDemoControl을 호스팅하는 폼에는 빌드를 위해 AttributesDemoControl 어셈블리에 대한 참조가 필요합니다.

참고 항목

작업

방법: DesignerSerializationVisibilityAttribute가 있는 표준 형식 컬렉션 serialize

참조

IComparable

DataGridView

개념

Windows Forms 컨트롤의 특성

기타 리소스

.NET Framework에서 사용자 지정 Windows Forms 컨트롤 개발