There is a very cool feature built into .NET (has been since the beginning) that I find not too many people know about called an Extender. Well, an Extender isn't so much of a feature as it is an implementation of an interface formally called IExtenderProvider, but for brevity, I'll simply call implementors of the IExtenderProvider "Extenders".
An Extender class is one that provides additional functionality to a control or controls by augmenting its property set and providing the corresponding behavior. An example of an Extender with which many people are familiar is the ToolTip. Have you ever noticed that controls don't have a ToolTip property? This is easily remedied by dragging a ToolTip control onto your form. The ToolTip takes residence in the tray at the bottom of the designer. All of the sudden all of the controls on the form magically have a new 'ToolTip' property. In fact, the property is displayed more like "ToolTip on ToolTip1" to indicate the name of the control providing the property to the control. This is the Extender in action. The ToolTip control essentially provides one or more properties to a given type or types of controls.
Creating Extenders is remarkably easy to do, but there are some nuances to it that I'd like to explore here for a bit. Basically the steps are as follows:
A common demo use case for an Extender is to provide visual feedback to the user based on some event or occurrence in the form. Examples might include an Extender that provides a "ContextDescription" property such that on mouse over on a control, the description is displayed elsewhere on the form (or on the Extender itself). Perhaps you want to highlight the control over which the mouse is positioned to give nice visual feedback to the user, to enhance accessibility of your applications, or for training purposes. The list goes on and on.
One very cool aspect of Extenders is that in many ways, the Extender provides functionality that would otherwise be difficult or tedious to obtain. For instance, if you want to implement the "ContextDescription" example above, you could do one of the following:
I've created a very simple yet fun Extender that provides feedback as the user moves the mouse over a DataGridView control, highlighting the cell that the user is over with a color of their choice. This simple example illustrates the techniques mentioned above:
using System;using System.Collections;using System.ComponentModel;using System.Drawing;using System.Windows.Forms; namespace DataGridViewExtenderExample.Components { [ProvideProperty("CellHighlightColor", typeof(DataGridView))] public partial class DataGridViewCellHighlighterExtender : Component, IExtenderProvider { public DataGridViewCellHighlighterExtender() { InitializeComponent(); } public DataGridViewCellHighlighterExtender(IContainer container) { container.Add(this); InitializeComponent(); } private const int InvalidCell = -1; private Hashtable _properties = new Hashtable(); private DataGridViewCell _lastCell; private int _lastCol = InvalidCell, _lastRow = InvalidCell; [Category("Appearance")] [Description("Sets or returns the color to highlight the cell as the mouse moves over the cell.")] public Color GetCellHighlightColor(Control control) { object color = _properties[control]; return ( null == color ? SystemColors.Window : (Color)color ); } public void SetCellHighlightColor(Control control, Color value) { DataGridView grid = control as DataGridView; MouseEventHandler ehMove = new MouseEventHandler(mouseMove); EventHandler ehLeave = new EventHandler(mouseLeave); if ( SystemColors.Window == value ) { _properties.Remove(grid); grid.MouseMove -= ehMove; grid.MouseLeave -= ehLeave; } else { _properties[grid] = value; grid.MouseMove += ehMove; grid.MouseLeave += ehLeave; } } bool IExtenderProvider.CanExtend(object extendee) { return ( extendee is DataGridView ); } private void mouseMove(object sender, MouseEventArgs e) { DataGridView grid = sender as DataGridView; DataGridView.HitTestInfo hti = grid.HitTest(e.X, e.Y); int col = hti.ColumnIndex; int row = hti.RowIndex; if ( col != _lastCol || row != _lastRow ) restoreCell(); if ( col >= 0 && row >= 0 ) { DataGridViewCell cell = grid[col, row]; cell.Style.BackColor = GetCellHighlightColor(grid); _lastCell = cell; _lastCol = col; _lastRow = row; } } private void mouseLeave(object sender, EventArgs e) { restoreCell(); } private void restoreCell() { if ( null != _lastCell ) { _lastCell.Style.BackColor = SystemColors.Window; _lastCell = null; } } } }
using System;using System.Collections;using System.ComponentModel;using System.Drawing;using System.Windows.Forms;
namespace DataGridViewExtenderExample.Components {
[ProvideProperty("CellHighlightColor", typeof(DataGridView))] public partial class DataGridViewCellHighlighterExtender : Component, IExtenderProvider { public DataGridViewCellHighlighterExtender() { InitializeComponent(); }
public DataGridViewCellHighlighterExtender(IContainer container) { container.Add(this); InitializeComponent(); }
private const int InvalidCell = -1;
private Hashtable _properties = new Hashtable(); private DataGridViewCell _lastCell; private int _lastCol = InvalidCell, _lastRow = InvalidCell;
[Category("Appearance")] [Description("Sets or returns the color to highlight the cell as the mouse moves over the cell.")] public Color GetCellHighlightColor(Control control) { object color = _properties[control]; return ( null == color ? SystemColors.Window : (Color)color ); }
public void SetCellHighlightColor(Control control, Color value) { DataGridView grid = control as DataGridView; MouseEventHandler ehMove = new MouseEventHandler(mouseMove); EventHandler ehLeave = new EventHandler(mouseLeave);
if ( SystemColors.Window == value ) { _properties.Remove(grid); grid.MouseMove -= ehMove; grid.MouseLeave -= ehLeave; } else { _properties[grid] = value; grid.MouseMove += ehMove; grid.MouseLeave += ehLeave; } }
bool IExtenderProvider.CanExtend(object extendee) { return ( extendee is DataGridView ); }
private void mouseMove(object sender, MouseEventArgs e) { DataGridView grid = sender as DataGridView; DataGridView.HitTestInfo hti = grid.HitTest(e.X, e.Y); int col = hti.ColumnIndex; int row = hti.RowIndex;
if ( col != _lastCol || row != _lastRow ) restoreCell();
if ( col >= 0 && row >= 0 ) { DataGridViewCell cell = grid[col, row]; cell.Style.BackColor = GetCellHighlightColor(grid);
_lastCell = cell; _lastCol = col; _lastRow = row; } }
private void mouseLeave(object sender, EventArgs e) { restoreCell(); }
private void restoreCell() { if ( null != _lastCell ) { _lastCell.Style.BackColor = SystemColors.Window; _lastCell = null; } } }
}
Enjoy! As you can see, there's not much to it. You can download an entire sample application that puts the Extender to work. :)
Remember Me
a@href@title, b, i, strike
Powered by: newtelligence dasBlog 2.0.7226.0
Disclaimer The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.
© Copyright 2008R. Aaron Zupancic
E-mail