設為首頁收藏本站Access中國

Office中國論壇/Access中國論壇

 找回密碼
 注冊

QQ登錄

只需一步,快速開始

返回列表 發(fā)新帖
查看: 10987|回復: 8
打印 上一主題 下一主題

【作業(yè)】03課-紫電

[復制鏈接]
跳轉(zhuǎn)到指定樓層
1#
發(fā)表于 2014-3-16 04:53:36 | 只看該作者 回帖獎勵 |倒序瀏覽 |閱讀模式
本帖最后由 紫電 于 2014-3-17 14:36 編輯

一、效果演示
       為了減小圖片大小,圖中,我只演示了一個單元格被修改后,程序插入批注的效果。實際上功能開啟狀態(tài)下,可以在多個單元格、多個工作表、多個工作簿上都起作用,且刪除、插入等各種危險操作,均不會影響其效果。


二、技術剖析
1、基本原理
     使用字典獲取工作表中的初始值或批注值,作為單元格的初始值。當工作表Change事件被觸發(fā)時,遍歷改變的單元格,與字典中的初值進行比較,如果不相符插入批注;相符,刪除批注。
2、多工作表、多工作簿

為了實現(xiàn)在多個工作表、工作簿上都能實現(xiàn)增加批注記錄原始數(shù)據(jù)的功能,需要訂閱Excel.Application中的WorkbookOpen、WorkbookActivate、 SheetActivate事件,也就是進行事件委托。為避免代碼集中在ThisAddin或者Ribbon中,這里我新建了了一個cs文件另外構(gòu)造類,這部分初始化工作在構(gòu)造函數(shù)中進行。一般情況下,通過以上三個事件之一 調(diào)用GetActiveWorksheet()實現(xiàn)字段初始化、工作表事件的委托。由于調(diào)試時不會觸發(fā)這三個事件,所以構(gòu)造函數(shù)中需要額外增加一個GetActiveWorksheet()進行初始化。


  1.         #region 構(gòu)造函數(shù)
  2.         public MarkChangedCells(Excel.Application app)
  3.         {
  4.             xlsApp = app;
  5.             GetActiveWorksheet();//設置工作表對象
  6.             xlsApp.WorkbookOpen += xlsApp_WorkbookOpen;//打開工作簿
  7.             xlsApp.WorkbookActivate += xlsApp_WorkbookActivate;//激活工作簿
  8.             xlsApp.SheetActivate += xlsApp_SheetActivate;//切換工作表
  9.         }
  10.         #endregion
復制代碼


3、避免工作表事件change多次被運行

問題:調(diào)試過程中,監(jiān)視到工作表的Change事件,會在切換工作表之后,被多次調(diào)用,禁用事件觸發(fā)仍然不起作用。
分析:堆中存在多個訂閱事件的方法,沒有被回收。
方案:取消事件委托之后再重新設置工作表對象,避免產(chǎn)生垃圾。

  1.         /// <summary>
  2.         /// 獲取活動工作表,并關聯(lián)事件,初始化字典
  3.         /// </summary>
  4.         /// <returns>是否成功獲取工作表</returns>
  5.         bool GetActiveWorksheet()
  6.         {
  7.             try
  8.             {
  9.                 if (null != MySh)
  10.                 {
  11.                     MySh.Change -= MySh_Change;//解除事件,否則會多次執(zhí)行Change事件
  12.                 }               
  13.                 MySh = xlsApp.ActiveSheet;//啟動時,初始化FirstSh
  14.                 MySh.Change += MySh_Change;//監(jiān)視事件
  15.                 m_NickNameConvert = new RangeNickName(MySh);//重置名稱定義
  16.                 InitiateMyShData(MySh);// 初始化字典
  17.                 return true;
  18.             }
  19.             catch (Exception)
  20.             {
  21.                 MySh = null;//如果獲取到的是Chart,則放棄操作
  22.                 return false;
  23.             }            
  24.         }
復制代碼


4、復制、剪切、插入、刪除單元格,初始值錯位。

      起初使用的字典結(jié)構(gòu)是Range.Address作為Key,Range.Value作為Value。分析可知,復制、剪切、插入、刪除單元格之后,單元格實際情況與字典中的情況會不一致。比如插入了一行,字典中的Range.Address不會向下偏移一行,因此新插入行下面添加的批注都是錯誤的。為了修正這個漏洞,我重新調(diào)整了字典結(jié)構(gòu)。具體措施如下:
(1)、使用GUID+Range.Value構(gòu)造初始值字典,GUID為單元格別名(需要做簡單字符串處理)。
(2)、再次構(gòu)建一個字典,加載Excel名稱定義(即單元格別名),體現(xiàn)GUID和Range.Address的對應關系。
(3)、初始值字典在加載單元格初始值、校驗單元格是否改變時,通過調(diào)用類RangeNickName來實現(xiàn)。使用Range.Address獲取GUID(單元格別名),判斷單元格別名對應的單元格與Range是否屬于同一單元格,以此來確定是否已經(jīng)進行了剪切、插入、刪除單元格操作。
(4)、提示復制操作,可以選擇覆蓋、修改粘貼區(qū)域,以此來避免產(chǎn)生大量的錯誤批注!
由于代碼較多,以下只貼出部分核心代碼。
  1.         /// <summary>
  2.         /// 檢測別名是否存在,即是否修改過工作表
  3.         /// </summary>
  4.         /// <param name="Rng">要檢測的單元格</param>
  5.         /// <returns>存在返回名稱定義檢測結(jié)果,否則返回空字符串</returns>
  6.         public string NickName(Excel.Range Rng)
  7.         {
  8.             InitiateNames(m_TargetSh);// 強制刷新字典,防止插入刪除操作導致的錯位
  9.             string sRefersTo = "="+ m_TargetSh.Name+"!" + Rng.Address;
  10.             string sGuid ;
  11.             if (m_Names_Sheet.ContainsKey(sRefersTo))
  12.             {
  13.                 sGuid = m_Names_Sheet[sRefersTo];
  14.             }
  15.             else
  16.             {
  17.                 sGuid = "";
  18.             }
  19.             return sGuid;
  20.         }


  21.         /// <summary>
  22.         /// 向工作表中添加單元格別名,返回別名
  23.         /// </summary>
  24.         /// <param name="Rng">需要添加別名的單個單元格</param>
  25.         /// <returns>返回別名</returns>
  26.         public string AddRangeNickName(Excel.Range Rng, bool Visible = false)
  27.         {
  28.             string sGuid;//獲取GUID
  29.             InitiateNames(m_TargetSh);// 強制刷新字典,防止插入刪除操作導致的錯位
  30.             string sRefersTo = "=" + m_TargetSh.Name + "!" + Rng.Address;
  31.             if (!m_Names_Sheet.ContainsKey(sRefersTo))
  32.             {
  33.                 sGuid = AddNickName(sRefersTo, Visible);//添加名稱
  34.             }
  35.             else
  36.             {
  37.                 sGuid = m_Names_Sheet[sRefersTo];//返回原有的名稱定義
  38.             }      
  39.             return sGuid;//返回別名
  40.         }
復制代碼

5、用戶自定義開關此功能

     創(chuàng)建一個Ribbon,使用Checkbox,開關此功能。開就是new,關需要使用到手動析,區(qū)別于VB的是,設為null是不行的。注冊表功能不再贅述,詳見代碼。
  1.         public void Dispose()
  2.         {            
  3.             if (null != MySh)
  4.             {
  5.                 MySh.Change -= MySh_Change;//解除事件,否則會多次執(zhí)行Change事件
  6.             }   
  7.             MySh = null;
  8.             xlsApp = null;
  9.             MyShData = null;
  10.             GC.SuppressFinalize(this);//不需要再調(diào)用本對象的Finalize方法
  11.         }
復制代碼




本帖子中包含更多資源

您需要 登錄 才可以下載或查看,沒有帳號?注冊

x

評分

參與人數(shù) 1V幣 +2 收起 理由
faunus + 2 (V幣)課程報名、錄像學習、代碼練習跟貼.

查看全部評分

分享到:  QQ好友和群QQ好友和群 QQ空間QQ空間 騰訊微博騰訊微博 騰訊朋友騰訊朋友
收藏收藏2 分享分享 分享淘帖 訂閱訂閱
2#
發(fā)表于 2014-3-17 12:08:12 | 只看該作者
優(yōu)秀作業(yè),把代碼、原時,關鍵部分的說明描述下就更完關。
3#
 樓主| 發(fā)表于 2014-3-17 12:27:30 | 只看該作者
faunus 發(fā)表于 2014-3-17 12:08
優(yōu)秀作業(yè),把代碼、原時,關鍵部分的說明描述下就更完關。

好的
4#
發(fā)表于 2014-3-17 22:36:43 | 只看該作者
呵呵,不錯,還沒有弄清楚什么原理,委托弄不明白,太笨了。可是發(fā)現(xiàn)一個小BUG,就是工作表的名稱中有空格時會出錯
5#
發(fā)表于 2014-3-17 22:37:28 | 只看該作者
雖說是用的GUID生成的不會有重復的,可是沒有考慮好工作表本身的問題
6#
發(fā)表于 2014-3-17 22:38:54 | 只看該作者
要是每修改一次就加一次并且有時間就更好啦
7#
 樓主| 發(fā)表于 2014-3-18 14:14:04 | 只看該作者
/kuk心如止水 發(fā)表于 2014-3-17 22:38
要是每修改一次就加一次并且有時間就更好啦

這個簡單,賦值的時候一加就好了,幾行代碼的事情
8#
 樓主| 發(fā)表于 2014-3-18 14:14:54 | 只看該作者
/kuk心如止水 發(fā)表于 2014-3-17 22:36
呵呵,不錯,還沒有弄清楚什么原理,委托弄不明白,太笨了?墒前l(fā)現(xiàn)一個小BUG,就是工作表的名稱中有空格 ...

不應該啊。。。。
總體來說這個程序還是不行,初始化載入太慢了只能是娛樂
9#
發(fā)表于 2016-4-26 16:29:44 | 只看該作者
優(yōu)秀作業(yè),把代碼、原時,關鍵部分的說明描述下就更完關。
您需要登錄后才可以回帖 登錄 | 注冊

本版積分規(guī)則

QQ|站長郵箱|小黑屋|手機版|Office中國/Access中國 ( 粵ICP備10043721號-1 )  

GMT+8, 2025-7-13 08:19 , Processed in 0.108475 second(s), 37 queries .

Powered by Discuz! X3.3

© 2001-2017 Comsenz Inc.

快速回復 返回頂部 返回列表