这两天看到同事的一个小工具,用的是模块式开发,也就是俗称的插件开发,用的是反射+接口的方式实现的。感觉挺好的,也就学习了一下,写个小Demo,在此记录下。
一、写接口类
接口类是所有模块的基础,因为让主程序去寻找模块,就是通过反射来找到继承此接口的相关项目,也就是后期包含继承此接口类的DLL文件。
此接口类包含以下几个属性
工具的名称(必需)、是否要弹出、前景色、背景色、工具启动方法(必需)
public interface IToolsInterface
{ /// <summary>
/// 获取工具名称 /// </summary>
string ToolName
{ get;
} /// <summary>
/// 是否弹出 /// </summary>
bool IsPopUp
{ get;
} /// <summary>
/// 前景色 /// </summary> Brush ForgroundBrush
{ get;
} /// <summary>
/// 背景色 /// </summary> Brush BackgroundBrush
{ get;
} /// <summary>
/// 工具启动方法 /// </summary>
/// <returns></returns> FrameworkElement RunToolApplication();
}
二、写主窗体
所谓的主窗体,也就是各个模块的承载器而已,因为各个模块都是UserControl,需要窗体来承载。
需要的方法大概有两个,寻找目录的层级、创建相应的模块。
1、寻找目录层级
因为模块最终要生成到一个位置,然后让主程序去搜索,所以,需要一个搜索方法,去寻找
/// <summary>
/// 查找指定目录下的所有末级子目录 /// </summary>
/// <param name="dir">要查找的目录</param>
/// <param name="dirList">查找结果列表</param>
/// <param name="system">是否包含系统目录</param>
/// <param name="hidden">是否包含隐藏目录</param>
public static void GetEndDirectories(DirectoryInfo dir,List<DirectoryInfo> dirList,bool system=false,bool hidden=false)
{ try
{ //返回当前目录的子目录集合
DirectoryInfo[] dirSub = dir.GetDirectories(); if(dirSub.Length==0)
{ //如果没有子目录了则添加进列表 dirList.Add(dir); return;
} foreach (DirectoryInfo subItem in dirSub)
{ //跳过系统目录
if (!system && (subItem.Attributes & FileAttributes.System)==FileAttributes.System)
{ continue;
} //跳过隐藏目录
if (!hidden && (subItem.Attributes & FileAttributes.Hidden)==FileAttributes.Hidden)
{ continue;
} //递归 GetEndDirectories(subItem, dirList);
}
} catch (Exception ex)
{
MessageBox.Show("获取目录层级失败。" + ex.Message);
}
}
2、创建相应模块
当存在一个DLL时,就生成一个模块,两个DLL时就要有两个模块,以此类推……
/// <summary>
/// 创建功能按钮 /// </summary>
/// <param name="toolsInterface"></param>
/// <returns></returns>
private UIElement CreateFunction(IToolsInterface toolsInterface)
{
Button btn = new Button();
btn.Click += Btn_Click;
btn.Content = toolsInterface.ToolName;
btn.Width = 100;
btn.Height = 50;
btn.Margin = new Thickness(5, 0, 0, 0);
btn.Background = toolsInterface.BackgroundBrush;
btn.Foreground = toolsInterface.ForgroundBrush;
btn.Tag = toolsInterface; return btn;
} private void Btn_Click(object sender, RoutedEventArgs e)
{
Button btn = sender as Button; if(btn!=null)
{
IToolsInterface toolsInterface = btn.Tag as IToolsInterface; if(toolsInterface !=null)
{
FrameworkElement control = toolsInterface.RunToolApplication();
gUc.Children.Clear();
gUc.Children.Add(control);
} else
{
MessageBox.Show("实例化接口失败");
}
} else
{
MessageBox.Show("实例化按钮失败");
}
}
3、加载模块
private void LoadWidgets()
{ string applicationPath = AppDomain.CurrentDomain.SetupInformation.ApplicationBase; string dirPath = applicationPath + "Widgets/"; if(!Directory.Exists(dirPath))
{
MessageBox.Show("未找到相关的Widgets文件夹信息,请确认相关的文件夹是否存在。"); return;
}
DirectoryInfo dir = new DirectoryInfo(dirPath);
List<DirectoryInfo> lastDirNameList = new List<DirectoryInfo>();
Tools.FileToolsHelper.GetEndDirectories(dir, lastDirNameList); foreach (DirectoryInfo item in lastDirNameList)
{
WrapPanel wrapPanel = null;
TabItem tabItem = Tools.UCToolsHelper.CreateTabByDirName(item.Name,tcToolkClass);
wrapPanel = Tools.UCToolsHelper.CreateWrapPanel(tabItem); string[] dllFilesPath = Directory.GetFiles(item.FullName, "*.dll"); foreach (string dllPath in dllFilesPath)
{
Assembly assembly = Assembly.LoadFile(dllPath);
Type[] types = assembly.GetExportedTypes(); foreach (Type type in types)
{ if(typeof(IToolsInterface).IsAssignableFrom(type)&&!type.IsAbstract)
{
IToolsInterface toolInterface = Activator.CreateInstance(type) as IToolsInterface; if (toolInterface !=null)
{
wrapPanel.Children.Add(CreateFunction(toolInterface));
}
}
}
}
}
}
三、写相应的模块部分
最近也没写什么小东西,就把原来做的两个Winform东西,直接搬过来,弄成了WPF的,把窗体改成了UserControl,额外加了一个类,用来实现第一部分提到的接口。
其中一个小工具在这:http://www.cnblogs.com/ZXdeveloper/p/5682230.html
基本东西不动,只是加了一个FunctionHelper用来实现接口,此处需要注意,一定是Public,否则查询不到
public class FunctionHelper : IToolsInterface
{ public Brush BackgroundBrush
{ get
{ return new SolidColorBrush(Colors.LightBlue);
}
} public Brush ForgroundBrush
{ get
{ return new SolidColorBrush(Colors.YellowGreen);
}
} public bool IsPopUp
{ get
{ return false;
}
} public string ToolName
{ get
{ return "方法查询工具";
}
} public FrameworkElement RunToolApplication()
{ return new SearchUC();
}
}
四、看一下效果图
大概也就这么一个流程,不是很难,方便了后期的开发。
DEMO还有很多不完善的地方,我会慢慢弄,后期会不断的完善
DEMO