基于Attribute(属性)的插件开发
2012-04-23 09:51 yangtam 阅读(380) 评论(0) 收藏 举报在Windows Form中我们常常遇到这样的一个问题,系统升级后菜单就变了,或者将一个dll放入到对应运行的文件夹下面,然后重新运行系统,系统菜单就自然改变了。类似这样的解决方法会有很多,下面只是个人的想法,有什么不对的地方请各位指出。
在.NET平台下主要通过Attribute+反射来实现的,具体的步骤如下:
- 先定义一个Attribute,比如说(MenuItemAttribute);
- 根据不同的需求继承MenuItemAttribute;
- 定义并实现一个操作反射的类(AssemblyHelper),主要用于查找MenuItemAttribute;
- 在集成的MainForm中通过调用AssemblyHelper来完成菜单的生成;
MenuItemAttribute主要是一个抽象类,其定义如下:
View Code
public abstract class MenuItemAttribute : Attribute { protected MenuItemAttribute(string text) { Text = text; } public string Text { get; set; } public int Id { get; set; } public int ParentId { get; set; } public virtual void Excute(Type targetType) { if(targetType.IsSubclassOf(typeof(Form))) { ((Form) Activator.CreateInstance(targetType)).Show(); } if (targetType.IsSubclassOf(typeof(Window))) { ((Window)Activator.CreateInstance(targetType)).Show(); } } }
Text表示菜单显示的文字,而Id主要是用来判断该MenuItem在菜单中出现的顺序,而ParentId表示该菜单属于哪一级菜单的子菜单。方法Excute(Type targetType)主要是用来保存当点击了MenuItem的时候触发的事件做的事情。
定义一个ToolStripMenuItemAttribute继承MenuItemAttribute,如下:
View Code
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)] public class ToolStripMenuItemAttribute : MenuItemAttribute { public ToolStripMenuItemAttribute(string text) : base(text) { } }
定义一个操作反射的类AssemblyHelper,该类主要是通过使用反射查找MenuItemAttribute来实现对菜单的动态生成,定义如下:
View Code
public class AssemblyHelper { public static ToolStripItem[] GetMenuItems() { var result = new List<ToolStripItem>(); string[] files = Directory.GetFiles(AppDomain.CurrentDomain.BaseDirectory, "*.dll", SearchOption.TopDirectoryOnly); foreach (string t in files) { var assembly = Assembly.LoadFrom(t); var types = assembly.GetTypes(); var nodes = new List<Node>(); foreach (var type in types) { var attrs = (MenuItemAttribute[]) type.GetCustomAttributes(typeof (MenuItemAttribute), true); if (attrs.Length > 0) { nodes.AddRange(attrs.Select(attr => new Node() {MenuItem = attr, Target = type})); } } if (nodes.Count > 0) { var gNodes = nodes.GroupBy(p => p.MenuItem.ParentId).OrderBy(p => p.Key).ToList(); var root = gNodes[0].First(); var rootMenu = Order(root, gNodes); result.Add(rootMenu); } } return result.ToArray(); } public static ToolStripMenuItem Order(Node root, List<IGrouping<int, Node>> nodes) { var menuItem = new ToolStripMenuItem(root.MenuItem.Text); var nodes2 = nodes.Find(p => p.Key == root.MenuItem.Id); if(nodes2 == null) { menuItem.Click += (sender, e) => root.MenuItem.Excute(root.Target); } else { var temNode2 = nodes2.OrderBy(p => p.MenuItem.Id).ToList(); foreach (var node in temNode2) { menuItem.DropDownItems.Add(Order(node, nodes)); } } return menuItem; } } public class Node { public MenuItemAttribute MenuItem { get; set; } public Type Target { get; set; } }
在实现该功能中主要采用了遍历书的思想来实现的,首先定义一个节点类型Node。该类主要是用来保存MenuItemAttribute和对应的Form类型的。在AssemblyHelper中主要是先通过分组的方式生成一个树,然后在通过Order遍历数(先序遍历),来生成对应的菜单。
运行效果如下图:


浙公网安备 33010602011771号