User Control - Custom Properties
Categories:
Mastering Custom Properties in WinForms User Controls

Learn how to expose and manage custom properties for your WinForms User Controls, enhancing reusability and design-time configurability.
WinForms User Controls are powerful tools for encapsulating UI logic and design. To make them truly reusable and configurable, you often need to expose custom properties that can be set at design-time in the Properties window or programmatically at run-time. This article will guide you through the process of creating, configuring, and enhancing custom properties for your WinForms User Controls, making them more flexible and developer-friendly.
Defining Basic Custom Properties
The simplest way to add a custom property to your User Control is by defining a public property with a getter and a setter. For the property to appear in the Visual Studio Properties window, it must be public and have both get
and set
accessors. You can then use attributes from the System.ComponentModel
namespace to control its behavior and appearance in the designer.
using System.ComponentModel;
using System.Windows.Forms;
public partial class MyCustomControl : UserControl
{
private string _myTextProperty = "Default Text";
[Category("Custom Properties")]
[Description("Gets or sets the custom text displayed by the control.")]
[DefaultValue("Default Text")]
public string MyTextProperty
{
get { return _myTextProperty; }
set
{
if (_myTextProperty != value)
{
_myTextProperty = value;
// Invalidate the control to force a repaint if the property affects its appearance
this.Invalidate();
}
}
}
public MyCustomControl()
{
InitializeComponent();
}
}
Example of a basic custom string property with common attributes.
[Category]
, [Description]
, and [DefaultValue]
attributes for your custom properties. This significantly improves the design-time experience for developers using your control.Enhancing Properties with Designer Attributes
Beyond the basic attributes, WinForms provides a rich set of attributes to customize how your properties behave in the designer. These include attributes for serialization, editor types, and more. Understanding these attributes allows you to create highly sophisticated and user-friendly custom controls.
flowchart TD A[Define Public Property] --> B{Add `[Category]`} B --> C{Add `[Description]`} C --> D{Add `[DefaultValue]`} D --> E{Implement `get` and `set`} E --> F{Handle Property Change (e.g., `Invalidate()`)} F --> G{Consider `[Browsable(false)]` for hidden properties} G --> H{Consider `[Editor]` for custom UI editors} H --> I{Consider `[DesignerSerializationVisibility]`} I --> J[Property Ready for Designer]
Flowchart illustrating the steps to define and enhance custom properties.
Here are some other useful attributes:
[Browsable(false)]
: Hides the property from the Properties window.[ReadOnly(true)]
: Makes the property read-only in the Properties window, but still settable programmatically.[Editor(typeof(UITypeEditor), typeof(UITypeEditor))]
: Specifies a custom UI editor for the property, allowing for more complex design-time interaction (e.g., color pickers, file dialogs).[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
: Controls how the property is serialized by the designer.Content
is useful for complex types that need their internal state serialized.[Localizable(true)]
: Indicates that the property's value should be localized.
using System.ComponentModel;
using System.Drawing;
using System.Windows.Forms;
using System.Windows.Forms.Design;
public partial class MyAdvancedControl : UserControl
{
private Color _fillColor = Color.Blue;
private int _itemCount = 0;
[Category("Appearance")]
[Description("The background color used for drawing.")]
[DefaultValue(typeof(Color), "Blue")]
[Editor(typeof(ColorEditor), typeof(System.Drawing.Design.UITypeEditor))]
public Color FillColor
{
get { return _fillColor; }
set
{
if (_fillColor != value)
{
_fillColor = value;
this.Invalidate();
}
}
}
[Browsable(false)] // This property won't appear in the Properties window
public int ItemCount
{
get { return _itemCount; }
set { _itemCount = value; }
}
public MyAdvancedControl()
{
InitializeComponent();
}
}
Example demonstrating [Editor]
for a Color property and [Browsable(false)]
.
Handling Complex Property Types and Events
When your custom property is a complex object (e.g., a custom class or a collection), you need to consider how changes to its internal state are detected and how they affect the control. For simple value types, the setter handles change detection. For reference types, you might need to implement INotifyPropertyChanged
within the complex type or provide methods to explicitly notify the control of changes.
INotifyPropertyChanged
) to trigger an update.using System.ComponentModel;
using System.Windows.Forms;
// Define a simple custom class for a property
public class CustomSettings : INotifyPropertyChanged
{
private string _settingName = "Default";
public string SettingName
{
get { return _settingName; }
set
{
if (_settingName != value)
{
_settingName = value;
OnPropertyChanged(nameof(SettingName));
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
public partial class MyControlWithComplexProperty : UserControl
{
private CustomSettings _settings = new CustomSettings();
[Category("Configuration")]
[Description("Custom settings for the control.")]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
public CustomSettings ControlSettings
{
get { return _settings; }
set
{
if (_settings != value)
{
// Unsubscribe from old settings, subscribe to new
if (_settings != null) _settings.PropertyChanged -= Settings_PropertyChanged;
_settings = value;
if (_settings != null) _settings.PropertyChanged += Settings_PropertyChanged;
this.Invalidate(); // Update control if the entire object reference changes
}
}
}
public MyControlWithComplexProperty()
{
InitializeComponent();
_settings.PropertyChanged += Settings_PropertyChanged;
}
private void Settings_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
// Handle changes to internal properties of CustomSettings
if (e.PropertyName == nameof(CustomSettings.SettingName))
{
this.Invalidate(); // Force repaint if SettingName affects appearance
}
}
}
Implementing a complex property with INotifyPropertyChanged
for internal state changes.