Note

Access to this page requires authorization. You can try signing in or .

Access to this page requires authorization. You can try .

IExtenderProvider Interface

Definition

Namespace:
System.ComponentModel
Assemblies:
netstandard.dll, System.ComponentModel.TypeConverter.dll
Assembly:
System.ComponentModel.TypeConverter.dll
Assembly:
System.dll
Assembly:
netstandard.dll
Source:
IExtenderProvider.cs
Source:
IExtenderProvider.cs
Source:
EventDescriptorCollection.cs

Important

Some information relates to prerelease product that may be substantially modified before it’s released. Microsoft makes no warranties, express or implied, with respect to the information provided here.

Defines the interface for extending properties to other components in a container.

public interface class IExtenderProvider
public interface IExtenderProvider
type IExtenderProvider = interface
Public Interface IExtenderProvider
Derived

Examples

The following code example demonstrates how to implement the IExtenderProvider interface. This example is part of a larger example discussed in How to: Implement a HelpLabel Extender Provider.


using System;
using System.Collections;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.Drawing;
using System.Windows.Forms;
using System.Windows.Forms.Design;

namespace Microsoft.Samples.WinForms.Cs.HelpLabel;
//
// Help Label offers an extender property called
// "HelpText". It monitors the active control
// and displays the help text for the active control.
//
[
ProvideProperty("HelpText", typeof(Control)),
Designer(typeof(HelpLabelDesigner))
]
public class HelpLabel : Control, IExtenderProvider
{
 /// <summary>
 /// Required designer variable.
 /// </summary>
 Container components;
 readonly Hashtable helpTexts;
 Control activeControl;

 //
 // Creates a new help label object.
 //
 public HelpLabel()
 {
 //
 // Required for Windows Form Designer support
 //
 InitializeComponent();

 helpTexts = [];
 }

 /// <summary>
 /// Clean up any resources being used.
 /// </summary>
 protected override void Dispose(bool disposing)
 {
 if (disposing)
 {
 components.Dispose();
 }
 base.Dispose(disposing);
 }

 /// <summary>
 /// Required method for Designer support - do not modify
 /// the contents of this method with the code editor.
 /// </summary>
 void InitializeComponent()
 {
 BackColor = SystemColors.Info;
 components = new Container();
 ForeColor = SystemColors.InfoText;
 TabStop = false;
 }

 //
 // Overrides the text property of Control. This label ignores
 // the text property, so we add additional attributes here so the
 // property does not show up in the properties window and is not
 // persisted.
 //
 [
 Browsable(false),
 EditorBrowsable(EditorBrowsableState.Never),
 DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)
 ]
 public override string Text
 {
 get => base.Text; set => base.Text = value;
 }

 //
 // This implements the IExtenderProvider.CanExtend method. The
 // help label provides an extender property, and the design time
 // framework will call this method once for each component to determine
 // if we are interested in providing our extended properties for the
 // component. We return true here if the object is a control and is
 // not a HelpLabel (since it would be silly to add this property to
 // ourselves).
 //
 bool IExtenderProvider.CanExtend(object target) => target is Control and
 not HelpLabel;

 //
 // This is the extended property for the HelpText property. Extended
 // properties are actual methods because they take an additional parameter
 // that is the object or control to provide the property for.
 //
 [
 DefaultValue(""),
 ]
 public string GetHelpText(Control control)
 {
 string text = (string)helpTexts[control];
 text ??= string.Empty;
 return text;
 }

 //
 // This is an event handler that responds to the OnControlEnter
 // event. We attach this to each control we are providing help
 // text for.
 //
 void OnControlEnter(object sender, EventArgs e)
 {
 activeControl = (Control)sender;
 Invalidate();
 }

 //
 // This is an event handler that responds to the OnControlLeave
 // event. We attach this to each control we are providing help
 // text for.
 //
 void OnControlLeave(object sender, EventArgs e)
 {
 if (sender == activeControl)
 {
 activeControl = null;
 Invalidate();
 }
 }

 //
 // This is the extended property for the HelpText property.
 //
 public void SetHelpText(Control control, string value)
 {
 value ??= string.Empty;

 if (value.Length == 0)
 {
 helpTexts.Remove(control);

 control.Enter -= OnControlEnter;
 control.Leave -= OnControlLeave;
 }
 else
 {
 helpTexts[control] = value;

 control.Enter += OnControlEnter;
 control.Leave += OnControlLeave;
 }

 if (control == activeControl)
 {
 Invalidate();
 }
 }

 //
 // Overrides Control.OnPaint. Here we draw our
 // label.
 //
 protected override void OnPaint(PaintEventArgs pe)
 {
 // Let the base draw. This will cover our back
 // color and set any image that the user may have
 // provided.
 //
 base.OnPaint(pe);

 // Draw a rectangle around our control.
 //
 Rectangle rect = ClientRectangle;

 Pen borderPen = new(ForeColor);
 pe.Graphics.DrawRectangle(borderPen, rect);
 borderPen.Dispose();

 // Finally, draw the text over the top of the
 // rectangle.
 //
 if (activeControl != null)
 {
 string text = (string)helpTexts[activeControl];
 if (!string.IsNullOrEmpty(text))
 {
 rect.Inflate(-2, -2);
 Brush brush = new SolidBrush(ForeColor);
 pe.Graphics.DrawString(text, Font, brush, rect);
 brush.Dispose();
 }
 }
 }

 // Returns true if the backColor should be persisted in code gen. We
 // override this because we change the default back color.
 // true if the backColor should be persisted.
 //
 public bool ShouldSerializeBackColor() => !BackColor.Equals(SystemColors.Info);

 // Returns true if the foreColor should be persisted in code gen. We
 // override this because we change the default foreground color.
 // true if the foreColor should be persisted.
 //
 public bool ShouldSerializeForeColor() => !ForeColor.Equals(SystemColors.InfoText);

 //
 // This is a designer for the HelpLabel. This designer provides
 // design time feedback for the label. The help label responds
 // to changes in the active control, but these events do not
 // occur at design time. In order to provide some usable feedback
 // that the control is working the right way, this designer listens
 // to selection change events and uses those events to trigger active
 // control changes.
 //
 public class HelpLabelDesigner : ControlDesigner
 {
 bool trackSelection = true;

 /// <summary>
 /// This property is added to the control's set of properties in the method
 /// PreFilterProperties below. Note that on designers, properties that are
 /// explicitly declared by TypeDescriptor.CreateProperty can be declared as
 /// private on the designer. This helps to keep the designer's public
 /// object model clean.
 /// </summary>
 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
 bool TrackSelection
 {
 get => trackSelection;
 set
 {
 trackSelection = value;
 if (trackSelection)
 {
 ISelectionService ss = (ISelectionService)GetService(typeof(ISelectionService));
 if (ss != null)
 {
 UpdateHelpLabelSelection(ss);
 }
 }
 else
 {
 HelpLabel helpLabel = (HelpLabel)Control;
 if (helpLabel.activeControl != null)
 {
 helpLabel.activeControl = null;
 helpLabel.Invalidate();
 }
 }
 }
 }

 public override DesignerVerbCollection Verbs
 {
 get
 {
 DesignerVerb[] verbs = [
 new("Sample Verb", new EventHandler(OnSampleVerb))
 ];
 return [.. verbs];
 }
 }

 //
 // Overrides Dispose. Here we remove our handler for the selection changed
 // event. With designers, it is critical that they clean up any events they
 // have attached. Otherwise, during the course of an editing session many
 // designers may get created and never destroyed.
 //
 protected override void Dispose(bool disposing)
 {
 if (disposing)
 {
 ISelectionService ss = (ISelectionService)GetService(typeof(ISelectionService));
 if (ss != null)
 {
 ss.SelectionChanged -= OnSelectionChanged;
 }
 }

 base.Dispose(disposing);
 }

 //
 // Overrides initialize. Here we add an event handler to the selection service.
 // Notice that we are very careful not to assume that the selection service is
 // available. It is entirely optional that a service is available and you should
 // always degrade gracefully if a service could not be found.
 //
 public override void Initialize(IComponent component)
 {
 base.Initialize(component);

 ISelectionService ss = (ISelectionService)GetService(typeof(ISelectionService));
 if (ss != null)
 {
 ss.SelectionChanged += OnSelectionChanged;
 }
 }

 void OnSampleVerb(object sender, EventArgs e) => MessageBox.Show("You have just invoked a sample verb. Normally, this would do something interesting.");

 //
 // Our handler for the selection change event. Here we update the active control within
 // the help label.
 //
 void OnSelectionChanged(object sender, EventArgs e)
 {
 if (trackSelection)
 {
 ISelectionService ss = (ISelectionService)sender;
 UpdateHelpLabelSelection(ss);
 }
 }

 protected override void PreFilterProperties(IDictionary properties)
 {
 // Always call base first in PreFilter* methods, and last in PostFilter*
 // methods.
 base.PreFilterProperties(properties);

 // We add a design-time property called "TrackSelection" that is used to track
 // the active selection. If the user sets this to true (the default), then
 // we will listen to selection change events and update the control's active
 // control to point to the current primary selection.
 properties["TrackSelection"] = TypeDescriptor.CreateProperty(
 GetType(), // the type this property is defined on
 "TrackSelection", // the name of the property
 typeof(bool), // the type of the property
 [CategoryAttribute.Design]); // attributes
 }

 /// <summary>
 /// This is a helper method that, given a selection service, will update the active control
 /// of our help label with the currently active selection.
 /// </summary>
 /// <param name="ss"></param>
 void UpdateHelpLabelSelection(ISelectionService ss)
 {
 HelpLabel helpLabel = (HelpLabel)Control;
 if (ss.PrimarySelection is Control c)
 {
 helpLabel.activeControl = c;
 helpLabel.Invalidate();
 }
 else
 {
 if (helpLabel.activeControl != null)
 {
 helpLabel.activeControl = null;
 helpLabel.Invalidate();
 }
 }
 }
 }
}
Option Strict On
Option Explicit On 
Imports System.Collections
Imports System.ComponentModel
Imports System.ComponentModel.Design
Imports System.Drawing
Imports System.Windows.Forms
Imports System.Windows.Forms.Design

Namespace Microsoft.Samples.WinForms.Vb.HelpLabel
 '
 ' Help Label offers an extender property called
 ' HelpText. It monitors the active control
 ' and displays the help text for the active control.
 '

 <ProvideProperty("HelpText", GetType(Control)), Designer(GetType(HelpLabel.HelpLabelDesigner))> _
 Public Class HelpLabel
 Inherits Control
 Implements System.ComponentModel.IExtenderProvider
 ' Required designer variable.
 Private components As System.ComponentModel.Container
 Private helpTexts As Hashtable
 Private activeControl As System.Windows.Forms.Control

 '
 ' Creates a new help label object.
 '
 Public Sub New()
 '
 ' Required for Windows Form designer support.
 '
 InitializeComponent()
 helpTexts = New Hashtable
 End Sub

 ' Clean up any resources being used.
 Protected Overloads Overrides Sub Dispose(ByVal disposing As Boolean)
 If disposing Then
 If components IsNot Nothing Then
 components.Dispose()
 End If
 End If
 MyBase.Dispose(disposing)
 End Sub

 ' Required method for designer support. Do not modify
 ' the contents of this method with the code editor.
 Private Sub InitializeComponent()
 Me.components = New System.ComponentModel.Container
 Me.BackColor = System.Drawing.SystemColors.Info
 Me.ForeColor = System.Drawing.SystemColors.InfoText
 Me.TabStop = False
 End Sub
 '
 ' Overrides the text property of Control. This label ignores
 ' the text property, so we add additional attributes here so the
 ' property does not show up in the Properties window and is not
 ' persisted.
 '
 <Browsable(False), _
 EditorBrowsable(EditorBrowsableState.Never), _
 DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)> _
 Public Overrides Property [Text]() As String
 Get
 Return MyBase.Text
 End Get
 Set(ByVal Value As String)
 MyBase.Text = Value
 End Set
 End Property


 '
 ' This implements the IExtenderProvider.CanExtend method. The
 ' help label provides an extender property, and the design-time
 ' framework will call this method once for each component to determine
 ' if we are interested in providing our extended properties for the
 ' component. We return true here if the object is a control and is
 ' not a HelpLabel (because it would not be meaningful to add this property to
 ' ourselves).
 '
 Function CanExtend(ByVal target As Object) As Boolean Implements IExtenderProvider.CanExtend
 If TypeOf target Is Control And Not TypeOf target Is HelpLabel Then

 Return True
 End If
 Return False
 End Function

 '
 ' This is the extended property for the HelpText property. Extended
 ' properties are actual methods because they take an additional parameter
 ' that is the object or control to provide the property for.
 '
 <DefaultValue("")> _
 Public Function GetHelpText(ByVal ctrl As Control) As String
 Dim myText As String = CStr(helpTexts(ctrl))
 If myText Is Nothing Then
 myText = String.Empty
 End If
 Return myText
 End Function

 '
 ' This is the extended property for the HelpText property.
 '
 Public Sub SetHelpText(ByVal ctrl As Control, ByVal value As String)
 If value Is Nothing Then
 value = String.Empty
 End If

 If value.Length = 0 Then
 helpTexts.Remove(ctrl)

 RemoveHandler ctrl.Enter, AddressOf OnControlEnter
 RemoveHandler ctrl.Leave, AddressOf OnControlLeave
 Else
 helpTexts(ctrl) = value
 AddHandler ctrl.Enter, AddressOf OnControlEnter
 AddHandler ctrl.Leave, AddressOf OnControlLeave
 End If

 If ctrl Is activeControl Then
 Invalidate()
 End If
 End Sub

 '
 ' This is an event handler that responds to the OnControlEnter
 ' event. We attach this to each control we are providing help
 ' text for.
 '
 Private Sub OnControlEnter(ByVal sender As Object, ByVal e As EventArgs)
 activeControl = CType(sender, Control)
 Invalidate()
 End Sub

 '
 ' This is an event handler that responds to the OnControlLeave
 ' event. We attach this to each control we are providing help
 ' text for.
 '
 Private Sub OnControlLeave(ByVal sender As Object, ByVal e As EventArgs)
 If sender Is activeControl Then
 activeControl = Nothing
 Invalidate()
 End If
 End Sub

 '
 ' Overrides Control.OnPaint. Here we draw our
 ' label.
 '
 Protected Overrides Sub OnPaint(ByVal pe As PaintEventArgs)
 ' Let the base draw. This will cover our back
 ' color and set any image that the user has
 ' provided.
 '
 MyBase.OnPaint(pe)

 ' Draw a rectangle around the control.
 '
 Dim rect As Rectangle = ClientRectangle

 Dim borderPen As New Pen(ForeColor)
 pe.Graphics.DrawRectangle(borderPen, rect)
 borderPen.Dispose()

 ' Finally, draw the text over the top of the
 ' rectangle.
 '
 If (activeControl IsNot Nothing) Then
 Dim myText As String = CStr(helpTexts(activeControl))
 If (myText IsNot Nothing) And myText.Length > 0 Then
 rect.Inflate(-2, -2)
 Dim brush As New SolidBrush(ForeColor)
 pe.Graphics.DrawString(myText, Font, brush, RectangleF.op_Implicit(rect))
 brush.Dispose()
 End If
 End If
 End Sub


 ' Returns true if backColor should be persisted in code gen. We
 ' override this because we change the default back color.
 ' true if the backColor should be persisted.
 '
 Public Function ShouldSerializeBackColor() As Boolean
 Return Not BackColor.Equals(SystemColors.Info)
 End Function


 ' Returns true if foreColor should be persisted in code gen. We
 ' override this because we change the default foreground color.
 ' true if foreColor should be persisted.
 '
 Public Function ShouldSerializeForeColor() As Boolean
 Return Not ForeColor.Equals(SystemColors.InfoText)
 End Function

 '
 ' This is a designer for the HelpLabel. This designer provides
 ' design time feedback for the label. The help label responds
 ' to changes in the active control, but these events do not
 ' occur at design time. In order to provide some usable feedback
 ' that the control is working the right way, this designer listens
 ' to selection change events and uses those events to trigger active
 ' control changes.
 '
 <System.Security.Permissions.PermissionSetAttribute(System.Security.Permissions.SecurityAction.Demand, Name:="FullTrust")> _
 Public Class HelpLabelDesigner
 Inherits System.Windows.Forms.Design.ControlDesigner

 Private _trackSelection As Boolean = True

 ' This property is added to the control's set of properties in the method
 ' PreFilterProperties below. Note that on designers, properties that are
 ' explictly declared by TypeDescriptor.CreateProperty can be declared as
 ' private on the designer. This helps to keep the designer's public
 ' object model clean.
 <DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)> _
 Private Property TrackSelection() As Boolean
 Get
 Return _trackSelection
 End Get
 Set(ByVal Value As Boolean)
 _trackSelection = Value
 If _trackSelection Then
 Dim ss As ISelectionService = CType(GetService(GetType(ISelectionService)), ISelectionService)
 If (ss IsNot Nothing) Then
 UpdateHelpLabelSelection(ss)
 End If
 Else
 Dim helpLabel As HelpLabel = CType(Control, HelpLabel)
 If (helpLabel.activeControl IsNot Nothing) Then
 helpLabel.activeControl = Nothing
 helpLabel.Invalidate()
 End If
 End If
 End Set
 End Property

 Public Overrides ReadOnly Property Verbs() As DesignerVerbCollection
 Get
 Dim myVerbs() As DesignerVerb = {New DesignerVerb("Sample Verb", AddressOf OnSampleVerb)}
 Return New DesignerVerbCollection(myVerbs)
 End Get
 End Property

 '
 ' Overrides Dispose. Here we remove our handler for the selection changed
 ' event. With designers, it is critical that they clean up any events they
 ' have attached. Otherwise, during the course of an editing session many
 ' designers might get created and never destroyed.
 '
 Protected Overloads Overrides Sub Dispose(ByVal disposing As Boolean)
 If disposing Then
 Dim ss As ISelectionService = CType(GetService(GetType(ISelectionService)), ISelectionService)
 If (ss IsNot Nothing) Then
 RemoveHandler ss.SelectionChanged, AddressOf OnSelectionChanged
 End If
 End If
 MyBase.Dispose(disposing)
 End Sub

 '
 ' Overrides initialize. Here we add an event handler to the selection service.
 ' Notice that we are very careful not to assume that the selection service is
 ' available. It is entirely optional that a service is available and you should
 ' always degrade gracefully if a service cannot be found.
 '
 Public Overrides Sub Initialize(ByVal component As IComponent)
 MyBase.Initialize(component)

 Dim ss As ISelectionService = CType(GetService(GetType(ISelectionService)), ISelectionService)
 If (ss IsNot Nothing) Then
 AddHandler ss.SelectionChanged, AddressOf OnSelectionChanged
 End If
 End Sub

 Private Sub OnSampleVerb(ByVal sender As Object, ByVal e As EventArgs)
 MessageBox.Show("You have just invoked a sample verb. Normally, this would do something interesting.")
 End Sub

 '
 ' The handler for the selection change event. Here we update the active control within
 ' the help label.
 '
 Private Sub OnSelectionChanged(ByVal sender As Object, ByVal e As EventArgs)
 If _trackSelection Then
 Dim ss As ISelectionService = CType(sender, ISelectionService)
 UpdateHelpLabelSelection(ss)
 End If
 End Sub

 Protected Overrides Sub PreFilterProperties(ByVal properties As IDictionary)
 ' Always call base first in PreFilter* methods, and last in PostFilter*
 ' methods.
 MyBase.PreFilterProperties(properties)

 ' We add a design-time property called TrackSelection that is used to track
 ' the active selection. If the user sets this to true (the default), then
 ' we will listen to selection change events and update the control's active
 ' control to point to the current primary selection.
 properties("TrackSelection") = TypeDescriptor.CreateProperty( _
 Me.GetType(), _
 "TrackSelection", _
 GetType(Boolean), _
 New Attribute() {CategoryAttribute.Design})
 End Sub

 ' This is a helper method that, given a selection service, will update the active control
 ' of the help label with the currently active selection.
 ' <param name="ss"></param>
 Private Sub UpdateHelpLabelSelection(ByVal ss As ISelectionService)
 Dim c As Control = CType(ss.PrimarySelection, Control)
 Dim helpLabel As HelpLabel = CType(Control, HelpLabel)
 If (c IsNot Nothing) Then
 helpLabel.activeControl = c
 helpLabel.Invalidate()
 Else
 If (helpLabel.activeControl IsNot Nothing) Then
 helpLabel.activeControl = Nothing
 helpLabel.Invalidate()
 End If
 End If
 End Sub

 Public Sub New()

 End Sub
 End Class

 End Class
End Namespace

Remarks

An extender provider is a component that provides properties to other components. For example, the ToolTip control is an extender provider. When you add a ToolTip control to a Form, all other controls on the form have a ToolTip property added to their list of properties.

Any component that provides extender properties must implement IExtenderProvider. A visual designer can then call CanExtend to determine which objects in a container should receive the extender properties.

For more information about extender providers, see How to: Implement an Extender Provider.

Methods

Name Description
CanExtend(Object)

Specifies whether this object can provide its extender properties to the specified object.

Applies to


Feedback

Was this page helpful?