[C #] Use IExtenderProvider to add extended properties for the control, like ToolTip,
Statement:
-This Article applies to WinForm development.
-The term "Control" in this article refers to a single interface element, including ToolStripItem and MenuItem. It does not refer to a narrow Control inherited from the Control class.
All the shoes that have used the ToolTip component know this phenomenon: drag a ToolTip in VS, and click various controls in the form, in the attribute pane, an attribute named ToolTip is displayed,
This article is about how to extend an attribute for the control like ToolTip (the quotation marks are used because it does not actually add an attribute for the control, but in VS, it looks like that ). The following is an actual case.
The requirement is as follows: when the user pointsMenu item(ToolStripMenuItem) orToolbar item(ToolStripButton, ToolStripLabel, and so on ),Status Bar label(ToolStripStatusLabel) display the function description of this item-many software do this, such as the famous Beyond Compare,
For this effect, it is easy to register the MouseEnter and MouseLeave events for each menu item and toolbar item (hereinafter referred to as item, set the Text = "item Function Description" of the status bar label (viewer) in the enter event, and viewer in the leave event. text = string. empty, that is, to clear Text; or bind the events of all items to the two total enter and leave event processing methods, and then use the switch in the method to differentiate the processing; or, enter the item function description in the Tag attribute, and then use the viewer in the enter event. text = (sender as ToolStripItem ). tag as string ~ In short, there are many methods.Besides, for ToolStripItem such as menu items and toolbar items, they naturally have the ToolTipText attribute to set the bubble prompt. However, this article does not discuss whether the bubble mode is better than the status bar mode.
Is there a way to write a component like ToolTip, such as ToolDescribe? After you drag it into VS, you can add a Describe attribute in the item attribute pane, you can simply enter the item function description text in it. How nice is this:
This is not nonsense. The pictures are all on. Can you. ToolDescribe code on:
Using System; using System. collections. generic; using System. componentModel; using System. windows. forms; namespace AhDung. winForm. controls {// <summary> /// provide [Description] For the menu item. This extended property /// </summary> [Description ("provides Description for the menu item or control extended attributes ")] [ProvideProperty ("Describe", typeof (ToolStripItem)] public class ToolDescribe: Component, IExtenderProvider {// <summary> // stores the ToolStripItem and its description. /// </summary> readonly Dict Ionary <ToolStripItem, string> dic; // <summary> // obtain or set the control used to display the menu item description /// </summary> [DefaultValue (null ), description ("get or set controls used to display menu item descriptions")] public Component Viewer {get; set ;} /// <summary> /// create a ToolDescribe class // </summary> public ToolDescribe () {dic = new Dictionary <ToolStripItem, string> ();} /// <summary> /// obtain the menu item Description /// </summary> [Description ("set menu item Description")] // although the method is Get, but it is displayed as "set" in VS to conform to the understanding [D EfaultValue (null)] public string GetDescribe (ToolStripItem item) {// retrieves the description string value of this item from the set; dic. tryGetValue (item, out value); return value ;}/// <summary> // you can specify the description of a menu item. /// </summary> public void SetDescribe (ToolStripItem, string value) {if (item = null) {return;} // if the value is null or string. empty, The ToolStripItem if (string. isNullOrEmpty (value) | value. trim (). length = 0) {// remove the ite from the set M, and cancel the related event binding dic. Remove (item); item. MouseEnter-= item_MouseEnter; item. MouseLeave-= item_MouseLeave;} else {if (! Dic. containsKey (item) // if it is a newly added item, register its related event {item. mouseEnter + = item_MouseEnter; item. mouseLeave + = item_MouseLeave;} // Add or change the description of this item dic [item] = value; // This method is used for keys that do not exist in dic, will automatically add} // point to event private void item_MouseEnter (object sender, EventArgs e) {// Let Viewer. text displays the item description if (Viewer is ToolStripItem) {(Viewer as ToolStripItem ). text = dic [sender as ToolStripItem];} else if (Viewer is StatusBarPanel) {(Viewer as StatusBarPanel ). text = dic [sender as ToolStripItem];} else if (Viewer is Control) {(Viewer as Control ). text = dic [sender as ToolStripItem] ;}// move the mouse away from the event private void item_MouseLeave (object sender, EventArgs e) {// clear the Viewer. text if (Viewer is ToolStripItem) {(Viewer as ToolStripItem ). text = string. empty;} else if (Viewer is StatusBarPanel) {(Viewer as StatusBarPanel ). text = string. empty;} else if (Viewer is Control) {(Viewer as Control ). text = string. empty ;}/// <summary> // whether attributes can be extended for an object /// </summary> public bool CanExtend (object extendee) {return true ;}}}
Implementation Instructions:
1. Let the ToolDescribe class implement System. ComponentModel.IExtenderProviderAnd Inherit System. ComponentModel.ComponentClass.ProvidePropertyAttributeFeature description. The implementation of the IExtenderProvider interface indicates that this class is a [extended program provider], MSDN has a related example: http://msdn.microsoft.com/zh-cn/library/ms229066 (v = vs.80). aspx. So what attributes should be extended to? This is defined by the ProvideProperty feature. The purpose of this class is to extend an attribute called Describe for the ToolStripItem class, so Describe [ProvideProperty ("Describe", typeof (ToolStripItem)]. Inherit Component so that ToolDescribe can be dragged into the VS Component bar like ToolTip, so that the attribute pane of the item will have an additional Describe attribute;
2. Define a collection class container in the ToolDescribe class to store the item and its description text that has set the function description. In this example, Dictionary <ToolStripItem, string> is used. Obviously, Key represents item, and Value represents the description text of item;
3. Define an attribute whose type is Component to present the item Function Description control. In this example, It is Viewer. The reason why the type is Component rather than Control is that the Viewer must allow the label to be set as the status bar label (ToolStripStatusLabel), while the ToolStripStatusLabel is not a Control, so the type must be set to "base class, to increase the flexibility of Viewer settings;
4. Implement a public string GetDescribe (ToolStripItem item) method to obtain the description text of the specified item, which is also the reason for defining the container in step 1, this method is hard to implement without the container recording of each item and its description text. Note that the name of this method must be the extended property name defined in Get + ProvideProperty, that is, Describe, which together is GetDescribe. In addition, it is necessary to add DefaultValue to this method. Otherwise, when you drag ToolDescribe, VS will expand all items, whether or not the Describe of an item is set, this can be seen from the InitializeComponent method;
5. implement a public void SetDescribe (ToolStripItem item, string value) method. The naming requirements are the same as above. This method is used to set the description text of an item. In terms of the specific implementation logic, it mainly needs to do two things: ① store the item and its value into the set; ② register the item-related events. That is to say, when something happens to the item, this example is to do something when the item has MouseEnter and MouseLeave, so you have to register the two events of the item. Here, we can understand that the core implementation of the item function description is still based on the registration of related events, that is, in essence, the original and stupid way of registering events for each item is the same as the previous one. ToolDescribe is not a big place, but it is not so troublesome. Of course, here we are talking about the application layer. What does the underlying VS do to the IExtenderProvider program? Naturally, it is high;
6. Implement the preceding event processing methods. In this example, item_MouseEnter and item_MouseLeave are implemented. The preceding Code focuses on the Implementation routine, so no extra performance optimization is performed. if the code is to be applied to the production environment, the if (Viewer is ToolStripItem) such a statement can be processed. For example, you can record the type of Viewer in the setter of the Viewer attribute, and you do not have to judge the Viewer type when each event is triggered;
7. Finally, the only member implementing the IExtenderProvider interface: public bool CanExtend (object extendee) method. This method is purely for VS. The logic of the method is that when you click a control in VS, extendee is the control, if the return value is true, the extended property is added to the property pane of the control. Otherwise, no extension property is added. In this example, if true is returned directly, will it cause more Describe attributes to be clicked on any control? The answer is no, because the ProvideProperty feature has already limited the extension of only the ToolStripItem class. Under what circumstances does this method need to add logic? For example, to extend attributes for Button and TextBox, it is more appropriate to limit ProvideProperty to Control, other controls that do not belong to buttons and TextBox are also extended. To do not extend them, you need to add the logic return extendee is Button in the CanExtend method | extendee is TextBox, whether a control is extended depends on two aspects: ProvideProperty and CanExtend. The former is the first level, and the latter is the second level;
OK, the routine is like this. Let's take a look at the results:
1. Drag a ToolDescribe component and set its Viewer to the status bar label:
2. Set the Describe attribute of item, as shown in figure 3;
3. Run it and see:
In other words, if there is a better solution than adding extended attributes for this effect, I hope you will not give it to me.
The following is a formal ToolDescribe, which is better than the preceding Demo. You can add extended attributes for ToolStripItem, Control, and MenuItem, and process performance optimization for the production environment. At the same time, we can see that the ProvideProperty feature can be used in combination to add different extended attributes for different controls. The reason why it is not written as the Component extension Describe attribute is that MenuItem only has the mouse to move into the event (Select ), if there is no removal event, to reach the MenuItem that points to a dimension that does not set Describe, Viewer. text is cleared and only extended for all menuitems. This is why the DefaultValue feature is not added to GetDescribeOfMenuItem ~ I don't know if the viewer can understand what I'm talking about. Code:
Using System; using System. collections. generic; using System. componentModel; using System. windows. forms; namespace AhDung. winForm. controls {// <summary> /// provide function description extended attributes for menu items or Controls /// </summary> /// <remarks> // Author: ahDung // Update: 201412161356, first version /// </remarks> [Description ("provide function Description extended attributes for menu items or controls")] [ProvideProperty ("DescribeOfToolStripItem ", typeof (ToolStripItem)] [ProvideProperty ("DescribeOfControl ", Typeof (Control)] [ProvideProperty ("DescribeOfMenuItem", typeof (MenuItem)] public class ToolDescribe: Component, IExtenderProvider {// <summary> // Component used to store the service and its description /// </summary> readonly Dictionary <Component, string> dic; Component viewer; bool viewerIsToolStripItem, viewerIsStatusBarPanel, viewerIsControl; ToolStripItem viewerAsToolStripItem; StatusBarPanel viewerAsStatusBarPanel; Control vie WerAsControl; /// <summary> /// obtain or set the control used to display the function description /// </summary> [DefaultValue (null ), description ("controls used to display function descriptions")] public Component Viewer {get {return viewer;} set {if (viewer = value) {return;} viewer = value; viewerIsToolStripItem = false; viewerIsStatusBarPanel = false; viewerIsControl = false; if (viewer is ToolStripItem) {viewerIsToolStripItem = true; viewerAsToolStripItem = viewer S ToolStripItem;} else if (viewer is StatusBarPanel) {viewerIsStatusBarPanel = true; viewerAsStatusBarPanel = viewer as StatusBarPanel;} else if (viewer is Control) {viewerIsControl = true; viewerAsControl = viewer as Control; }}/// <summary> // create a ToolDescribe class // </summary> public ToolDescribe () {dic = new Dictionary <Component, string> () ;}/// <summary> /// obtain the ToolStripItem description /// </s Ummary> [DefaultValue (null), Description ("set menu item Function Description")] public string GetDescribeOfToolStripItem (ToolStripItem item) {return GetDescribe (item );} /// <summary> /// get Control Description /// </summary> [DefaultValue (null), Description ("set Control function Description")] public string GetDescribeOfControl (Control item) {return GetDescribe (item );} /// <summary> /// obtain the MenuItem Description /// </summary> [Description ("set menu item Function Description")] public s Tring GetDescribeOfMenuItem (MenuItem item) {return GetDescribe (item);} // gets the Component description (private) private string GetDescribe (Component item) {string value; dic. tryGetValue (item, out value); return value ;}/// <summary> // set the ToolStripItem description /// </summary> public void SetDescribeOfToolStripItem (ToolStripItem item, string value) {if (item = null) {return;} if (string. isNullOrEmpty (value) | value. trim (). L Ength = 0) {dic. Remove (item); item. MouseEnter-= item_MouseEnter; item. MouseLeave-= item_MouseLeave;} else {if (! Dic. containsKey (item) {item. mouseEnter + = item_MouseEnter; item. mouseLeave + = item_MouseLeave;} dic [item] = value ;}} /// <summary> /// set the Control description /// </summary> public void SetDescribeOfControl (Control item, string value) {if (item = null) {return;} if (string. isNullOrEmpty (value) | value. trim (). length = 0) {dic. remove (item); item. mouseEnter-= item_MouseEnter; item. mouseLeave-= item _ MouseLeave;} else {if (! Dic. containsKey (item) {item. mouseEnter + = item_MouseEnter; item. mouseLeave + = item_MouseLeave;} dic [item] = value ;}} /// <summary> /// set the MenuItem description /// </summary> public void SetDescribeOfMenuItem (MenuItem item, string value) {if (item = null) {return;} if (! Dic. containsKey (item) {item. select + = item_MouseEnter;} dic [item] = value;} // point the cursor over the event private void item_MouseEnter (object sender, EventArgs e) {this. setViewerText (dic [sender as Component]);} // move the mouse away from the private void item_MouseLeave (object sender, EventArgs e) {this. setViewerText (string. empty);} // <summary> // set the Text value of the Viewer /// </summary> private void SetViewerText (string s) {if (viewer IsToolStripItem) {viewerAsToolStripItem. text = s;} else if (viewerIsStatusBarPanel) {viewerAsStatusBarPanel. text = s;} else if (viewerIsControl) {viewerAsControl. text = s ;}/// IExtenderProvider member [EditorBrowsable (EditorBrowsableState. never)] public bool CanExtend (object extendee) {return! (Extendee is Form );}}}
-Wen Bi-