在使用系统控件的时候我们经常看见和使用控件的右上角的一个三角型的图标 ,点击之后弹出一个菜单,标题是“XXXX 任务”,里面提供了好多方便有用的设置。但是我们自己写的自定义控件中却没有,下面就让自己的UserControl也有这个功能。
想实现功能首先要知道功能的名称吧,不然想去Google一下都不知道要用什么关键字 :D 这个功能叫做“智能标记 smart tag panel“。
今天以最常见的”在父容器中停靠“功能来演示一下如何让自定义控件实现智能标记功能。
一、正所谓”工欲善其事,必先利其器“,首先要引入一个库文件”System.Design“。
二、在自定义控件的命名空间下定义一个类,集成ControlDesigner类。(我自定义的控件叫做AdSchedule,所以给类起名AdScheduleDesigner)
1 public class AdScheduleDesigner : System.Windows.Forms.Design.ControlDesigner
这个类的作用是”扩展 Control 的设计模式行为“,类中需要定义一个DesignerActionListCollection类型的属性。
1 private DesignerActionListCollection actionLists;
2 public override DesignerActionListCollection ActionLists
3 {
4 get
5 {
6 if (null == actionLists)
7 {
8 actionLists = new DesignerActionListCollection();
9 actionLists.Add(new AdScheduleActionList(this.Component));
10 }
11 return actionLists;
12 }
13 }
1 public class AdScheduleActionList : System.ComponentModel.Design.DesignerActionList
2 {
3 private AdSchedule adSchedule;
4 private DesignerActionUIService designerActionUISvc = null;
5
6 public AdScheduleActionList(IComponent component) : base(component)
7 {
8 this.adSchedule = component as AdSchedule;
9
10 this.designerActionUISvc =GetService(typeof(DesignerActionUIService)) as DesignerActionUIService;
11 }
12 }
四、在AdScheduleActionList类中重写GetSortedActionItems方法。
1 public override DesignerActionItemCollection GetSortedActionItems()
2 {
3 DesignerActionItemCollection items = new DesignerActionItemCollection();
4 items.Add(new DesignerActionMethodItem(this, "ParentComponentStop", "在父容器中停靠"));
5 return items;
6 }
1 public void ParentComponentStop()
2 {
3 componentLocation = adSchedule.Location;
4 componentSize = adSchedule.Size;
5
6 adSchedule.Dock = System.Windows.Forms.DockStyle.Fill;
7 adSchedule.Location = new System.Drawing.Point(0, 0);
8 }
五、就这么简单,智能标记已经可以用了。
点击以后窗口就可以在父容器中停靠,但是问题来了,如何取消停靠呢?
很简单就是修改GetSortedActionItems方法中的DesignerActionItemCollection。
这里我们还需要创建两个变量,在ParentComponentStop函数中赋值用来保存控件停靠前的位置和大小,以便取消的时候返回原来的样子。
1 public override DesignerActionItemCollection GetSortedActionItems()
2 {
3 DesignerActionItemCollection items = new DesignerActionItemCollection();
4 if (DockStyle.None == adSchedule.Dock)
5 items.Add(new DesignerActionMethodItem(this, "ParentComponentStop", "在父容器中停靠"));
6 else
7 items.Add(new DesignerActionMethodItem(this, "CancelParentComponentStop", "取消在父容器中停靠"));
8 return items;
9 }
1 public void CancelParentComponentStop()
2 {
3 if (null == componentLocation)
4 componentLocation = adSchedule.Location;
5 if (null == componentSize)
6 componentSize = adSchedule.Size;
7
8 adSchedule.Dock = DockStyle.None;
9 adSchedule.Location = componentLocation;
10 adSchedule.Size = componentSize;
11
12 designerActionUISvc.Refresh(this.Component);
13 }
还没有完,点击之后为什么还是“在父容器中停靠”?文字没有变化呢?
还记得designerActionUISvc这个变量吗?他用来缓存DesignerActionUIService,以实现DesigneractionList刷新功能。
在ParentComponentStop函数最后加上这句话
1 designerActionUISvc.Refresh(this.Component);
现在就大功告成了,喝杯咖啡自己欣赏一下吧。
这只是个简单的应用,简单的文字链接形式实现调用函数,其实在智能标记中可以实现很多功能。
大家可以参考一下MSDN,给DesignerActionItemCollection传入不同类型的值就会实现更多的操作。
参考MSDN文献: