Edit

Share via


How to: Apply Attributes in Windows Forms Controls

To develop components and controls that interact correctly with the design environment and execute correctly at run time, you need to apply attributes correctly to classes and members.

Caution

This content was written for .NET Framework. If you're using .NET 6 or a later version, use this content with caution. The designer system has changed for Windows Forms and it's important that you review the Designer changes since .NET Framework article.

Example

The following code example demonstrates how to use several attributes on a custom control. The control demonstrates a simple logging capability. When the control is bound to a data source, it displays the values sent by the data source in a DataGridView control. If a value exceeds the value specified by the Threshold property, a ThresholdExceeded event is raised.

The AttributesDemoControl logs values with a LogEntry class. The LogEntry class is a template class, which means it is parameterized on the type that it logs. For example, if the AttributesDemoControl is logging values of type float, each LogEntry instance is declared and used as follows.

C#
// 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);
}

Note

Because LogEntry is parameterized by an arbitrary type, it must use reflection to operate on the parameter type. For the threshold feature to work, the parameter type T must implement the IComparable interface.

The form that hosts the AttributesDemoControl queries a performance counter periodically. Each value is packaged in a LogEntry of the appropriate type and added to the form's BindingSource. The AttributesDemoControl receives the value through its data binding and displays the value in a DataGridView control.

C#
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);
            }
        }
    }
}
C#
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();
        }
    }
}

The first code example is the AttributesDemoControl implementation. The second code example demonstrates a form that uses the AttributesDemoControl.

Class-level Attributes

Some attributes are applied at the class level. The following code example shows the attributes that are commonly applied to a Windows Forms control.

C#
// 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 Attribute

TypeConverterAttribute is another commonly used class-level attribute. The following code example shows its use for the LogEntry class. This example also shows an implementation of a TypeConverter for the LogEntry type, called LogEntryTypeConverter.

C#
// 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);
        }
    }
}

Member-level Attributes

Some attributes are applied at the member level. The following code examples show some attributes that are commonly applied to properties of Windows Forms controls.

C#
[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 Attribute

The following example demonstrates the AmbientValueAttribute and shows code that supports its interaction with the design environment. This interaction is called ambience.

C#
[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);
}

Databinding Attributes

The following examples demonstrate an implementation of complex data binding. The class-level ComplexBindingPropertiesAttribute, shown previously, specifies the DataSource and DataMember properties to use for data binding. The AttributeProviderAttribute specifies the type to which the DataSource property will bind.

C#
[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;
    }
}
C#
[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;
    }
}

Compiling the Code

  • The form that hosts the AttributesDemoControl requires a reference to the AttributesDemoControl assembly in order to build.

See also