Excel插件开发

1.本例开发了一个Excel 文档管理的插件,基于VS2012 和Excel2010,先上效果图

2.技术原理:使用Excel提供的自定义任务面板即CTP技术以及Ribbon菜单实现

1.先新增一个Ribbon 菜单,本人给的名字是UserRibbon,就是开始菜单的第一个文档管理的按钮,控制是否打开文档管理的那个自定义任务面板

2.增加一个UserControl,里面加上TreeView控件,来显示三种EXcel文档(当前打开文档,经常使用文档,最近使用文档)

3.加载任务面板代码段

  1. <pre name="code" class="csharp">using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using System.Xml.Linq;
  6. using Excel = Microsoft.Office.Interop.Excel;
  7. using Office = Microsoft.Office.Core;
  8. using Microsoft.Office.Tools.Excel;
  9. using Microsoft.Office.Core;
  10. using System.Diagnostics;
  11. using System.Runtime.InteropServices;
  12. namespace OfficeDocM
  13. {
  14. public partial class ThisAddIn
  15. {
  16. // 定义一个任务窗体
  17. internal Microsoft.Office.Tools.CustomTaskPane DocManageTaskPane;
  18. internal DocExplorer docExplorer;
  19. internal Excel.Application ExcelApp;
  20. private void ThisAddIn_Startup(object sender, System.EventArgs e)
  21. {
  22. //SingleInstanceCheck(); 有点问题先不控制
  23. Init();
  24. }
  25. private void Init()
  26. {
  27. //初始化
  28. ExcelApp = Globals.ThisAddIn.Application;
  29. //实例化文档浏览器窗口
  30. docExplorer = new DocExplorer();
  31. //注册Excel打开事件
  32. Globals.ThisAddIn.Application.WorkbookOpen += Application_WorkbookOpen;
  33. //加载自定义面板
  34. DocManageTaskPane = Globals.ThisAddIn.CustomTaskPanes.Add(docExplorer, "文档浏览器");
  35. ShowDocManageTaskPane(MsoCTPDockPosition.msoCTPDockPositionLeft);
  36. }
  37. //处理Excel打开事件
  38. private void Application_WorkbookOpen(Excel.Workbook Wb)
  39. {
  40. docExplorer.SetFileList(Wb.Path, ExcelApp.ActiveWorkbook.Name);
  41. }
  42. private void ThisAddIn_Shutdown(object sender, System.EventArgs e)
  43. {
  44. }
  45. public void ShowDocManageTaskPane(MsoCTPDockPosition postion = MsoCTPDockPosition.msoCTPDockPositionLeft)
  46. {
  47. // 使任务窗体可见
  48. DocManageTaskPane.Visible = true;
  49. DocManageTaskPane.DockPosition = postion;
  50. }
  51. public void HideDocManageTaskPane()
  52. {
  53. // 使任务窗体不可见
  54. DocManageTaskPane.Visible = false;
  55. }
  56. #region VSTO 生成的代码
  57. /// <summary>
  58. /// 设计器支持所需的方法 - 不要
  59. /// 使用代码编辑器修改此方法的内容。
  60. /// </summary>
  61. private void InternalStartup()
  62. {
  63. this.Startup += new System.EventHandler(ThisAddIn_Startup);
  64. this.Shutdown += new System.EventHandler(ThisAddIn_Shutdown);
  65. }
  66. #endregion
  67. }
  68. //非托管代码部分(引用Win32API)
  69. public partial class ThisAddIn
  70. {
  71. [DllImport("User32.dll ")]
  72. private static extern bool ShowWindowAsync(IntPtr hWnd, int cmdShow);
  73. //单实例检查
  74. private void SingleInstanceCheck()
  75. {
  76. int Process_min_id;
  77. Process[] processes = Process.GetProcessesByName("EXCEL");
  78. Process_min_id = processes[0].Id;
  79. //杀死其他进程
  80. if (processes.Length > 1)
  81. {
  82. for (int i = 1; i < processes.Length; i++)
  83. {
  84. if (Process_min_id < processes[i].Id)
  85. processes[i].Kill();
  86. else
  87. {
  88. Process.GetProcessById(Process_min_id).Kill();
  89. Process_min_id = processes[i].Id;
  90. }
  91. }
  92. }
  93. //显示最开始的进程
  94. ShowWindowAsync(Process.GetProcessById(Process_min_id).MainWindowHandle, 1);
  95. }
  96. }
  97. }

4 操作文档管理代码段

  1. using System;
  2. using System.Collections.Generic;
  3. using System.ComponentModel;
  4. using System.Drawing;
  5. using System.Data;
  6. using System.Linq;
  7. using System.Text;
  8. using System.Windows.Forms;
  9. using Excel=Microsoft.Office.Interop.Excel;
  10. using System.IO;
  11. using System.Xml;
  12. using System.Collections;
  13. using System.Reflection;
  14. using Microsoft.Win32;
  15. namespace OfficeDocM
  16. {
  17. public partial class DocExplorer : UserControl
  18. {
  19. Excel.Application ExcelApp;
  20. Excel.Workbooks ExcelWorkBooks;
  21. Hashtable ht_common = new Hashtable(); //保存常用的excel
  22. Hashtable ht_cuureet = new Hashtable();//保存当时的excel
  23. TreeNode root = new TreeNode();//根节点
  24. TreeNode common_root = new TreeNode();//常用根节点
  25. TreeNode current_root = new TreeNode();//当前根节点
  26. TreeNode recent_root = new TreeNode();//最近使用根节点
  27. public DocExplorer()
  28. {
  29. InitializeComponent();
  30. Init();
  31. }
  32. public void Init()
  33. {
  34. ExcelApp = Globals.ThisAddIn.Application;
  35. ExcelWorkBooks = ExcelApp.Workbooks;
  36. //树根初始化
  37. TreeViewInit();
  38. //加载XML常用文件配置
  39. LoadCommonFiles();
  40. //加载最近使用文件
  41. LoadRecentFiles();
  42. }
  43. public void SetFileList(string filePath, string fileName)
  44. {
  45. string fileFullPath;
  46. fileFullPath = filePath + @"\" + fileName;
  47. if (File.Exists(fileFullPath))
  48. {
  49. TreeNode chldNode = new TreeNode();
  50. chldNode.Name = fileFullPath;
  51. chldNode.Text = fileName;
  52. current_root.Nodes.Add(chldNode);
  53. chldNode.ForeColor = Color.Red;
  54. if (!current_root.IsExpanded)
  55. current_root.ExpandAll();
  56. }
  57. }
  58. //单击打开文件
  59. private void treeView_FileList_NodeMouseClick(object sender, TreeNodeMouseClickEventArgs e)
  60. {
  61. //创建Excel控件对象
  62. string Excelpath;
  63. Boolean docExists = false;
  64. if (e.Node.Level == 2 && e.Button==MouseButtons.Left)//左键单击叶子节点打开文件
  65. {
  66. Excelpath = treeView_FileList.SelectedNode.Name;
  67. foreach (Excel._Workbook wb in ExcelWorkBooks)
  68. {
  69. if (Excelpath == wb.FullName)
  70. {
  71. wb.Activate();
  72. docExists = true;
  73. break;
  74. }
  75. }
  76. if (!docExists)
  77. {
  78. if (File.Exists(Excelpath))
  79. {
  80. //监听了excel 打开事件,当前使用文档统一在打开事件里面处理
  81. ExcelWorkBooks.Open(Excelpath);
  82. if (!current_root.IsExpanded)
  83. current_root.ExpandAll();
  84. }
  85. else
  86. MessageBox.Show("文件不存在!");
  87. }
  88. }
  89. }
  90. //双击关闭文件
  91. private void treeView_FileList_NodeMouseDoubleClick(object sender, TreeNodeMouseClickEventArgs e)
  92. {
  93. if (e.Node.Parent == current_root && e.Node.Level==2)//双击打开文件则关闭
  94. {
  95. foreach (Excel._Workbook wb in ExcelWorkBooks)
  96. {
  97. if (e.Node.Name == wb.FullName)
  98. {
  99. wb.Close();
  100. current_root.Nodes.Remove(e.Node);
  101. break;
  102. }
  103. }
  104. }
  105. }
  106. private void TreeViewInit()
  107. {
  108. root.Text = "文档列表";
  109. current_root.Text = "当前打开文档";
  110. common_root.Text = "经常使用文档";
  111. recent_root.Text = "最近使用文档";
  112. treeView_FileList.Nodes.Add(root);
  113. root.Nodes.Add(current_root);
  114. root.Nodes.Add(common_root);
  115. root.Nodes.Add(recent_root);
  116. if (!root.IsExpanded)
  117. {
  118. root.Expand();
  119. }
  120. }
  121. private void LoadCommonFiles()
  122. {
  123. string xmlPath;
  124. int firstIndex, lastIndex;
  125. RegistryKey Key;
  126. Key = Registry.CurrentUser.OpenSubKey(@"Software\Microsoft\Office\Excel\Addins\OfficeDocM");
  127. xmlPath = Key.GetValue("Manifest").ToString();
  128. Key.Close();
  129. firstIndex=xmlPath.IndexOf(":")+4;
  130. lastIndex=xmlPath.LastIndexOf("/");
  131. xmlPath=xmlPath.Substring(firstIndex, lastIndex - firstIndex);
  132. xmlPath = xmlPath + @"\CustExcels.xml";
  133. //加载XML
  134. XmlDocument xmlDoc = new XmlDocument();
  135. if (File.Exists(xmlPath))
  136. {
  137. xmlDoc.Load(xmlPath);
  138. //忽略注释内容
  139. XmlReaderSettings settings = new XmlReaderSettings();
  140. settings.IgnoreComments = true;
  141. //得到根节点
  142. XmlNode ExcelsNode = xmlDoc.SelectSingleNode("Excels");
  143. // 得到根节点的所有子节点
  144. XmlNodeList ExcelsNodeList = ExcelsNode.ChildNodes;
  145. foreach (XmlNode xnitem in ExcelsNodeList)
  146. {
  147. TreeNode chldNode = new TreeNode();
  148. string docPath, docName;
  149. // 将节点转换为元素,便于得到节点的属性值
  150. XmlElement excelList = (XmlElement)xnitem;
  151. // 得到excel节点的所有子节点
  152. XmlNodeList xnl0 = excelList.ChildNodes;
  153. docName = xnl0.Item(0).InnerText;
  154. docPath = xnl0.Item(1).InnerText;
  155. chldNode.Name = docPath + @"\" + docName;
  156. chldNode.Text = docName;
  157. chldNode.ForeColor = Color.Purple;
  158. common_root.Nodes.Add(chldNode);
  159. }
  160. if (!common_root.IsExpanded)
  161. common_root.ExpandAll();
  162. }
  163. else
  164. {
  165. Log my = new Log(@"D:\log.txt", FileMode.Create);
  166. my.Writeln(DateTime.Now.ToLocalTime().ToString()+"["+xmlPath+"]");
  167. }
  168. }
  169. private void LoadRecentFiles()
  170. {
  171. string fileFullPath,fileName;
  172. for (int i = 1; i <= ExcelApp.RecentFiles.Count; i++)
  173. {
  174. fileFullPath=ExcelApp.RecentFiles.get_Item(i).Name;
  175. if (File.Exists(fileFullPath))
  176. {
  177. fileName = System.IO.Path.GetFileName(fileFullPath);
  178. TreeNode chldNode = new TreeNode();
  179. chldNode.Name = fileFullPath;
  180. chldNode.Text = fileName;
  181. recent_root.Nodes.Add(chldNode);
  182. chldNode.ForeColor = Color.SteelBlue;
  183. if (!recent_root.IsExpanded)
  184. recent_root.ExpandAll();
  185. }
  186. }
  187. }
  188. /*因为单击选中的还是上次的节点,那么在第一次加载上次的节点还是null的
  189. *所以使用单击打开这个事件是必不可少的*/
  190. private void treeView_FileList_MouseDown(object sender, MouseEventArgs e)
  191. {
  192. if ((sender as TreeView) != null)
  193. {
  194. treeView_FileList.SelectedNode = treeView_FileList.GetNodeAt(e.X, e.Y);
  195. }
  196. }
  197. }
  198. }


5关键点说明

1.本例中使用到了一个XML文件作为配置文件,保存经常使用的文档信息,有一个问题就是,没办法确定这个文件的路径,因为不知道用户会把这个插件安装在哪里,各种方法试就是不行,突然灵机一动,Excel 怎么加载插件的,原来在注册表,果然如此 就有了下面一段代码(路径里包含一些其他东西 就特别处理了一下)

  1. string xmlPath;
  2. int firstIndex, lastIndex;
  3. RegistryKey Key;
  4. Key = Registry.CurrentUser.OpenSubKey(@"Software\Microsoft\Office\Excel\Addins\OfficeDocM");
  5. xmlPath = Key.GetValue("Manifest").ToString();
  6. Key.Close();
  7. firstIndex=xmlPath.IndexOf(":")+4;
  8. lastIndex=xmlPath.LastIndexOf("/");
  9. xmlPath=xmlPath.Substring(firstIndex, lastIndex - firstIndex);
  10. xmlPath = xmlPath + @"\CustExcels.xml";

2.Ribbon  菜单位置的确定

                                                图1                                                                                             图2

   


2.1如果自己的菜单想独立(与office本生的菜单如开始,插入等 平级 Ribbon的ControlIDType 就选择Custom,若想其作为一个字菜单就选Office,并通过OfficeId指定其属于哪个菜单,如 图1 )

2.2 子菜单位置确定,如 图2 关键属性 Position 

2.3 每个Office 菜单Id 见 office 菜单名称

(0)

相关推荐