對於Excel中的很多對象,比如單元格(Cell),圖形(shape),圖表(chart)等等,有時需要將它們保存成一張圖片。就像截圖一樣。
最近做一個Excel相關的項目,項目中遇到一個很變態的需求, 需要對Excel中的一些對象進行拍圖,比如,對一個單元格設置一些顏色之后拍圖,或者對一個圖表,報表拍成圖片。經過比較曲折的經歷,終於還是完成了。拿出來分享一下。
要做Excel,首先當然是查看Excel的com對象模型。地址在這里:
http://msdn.microsoft.com/en-us/library/bb149081(v=office.12).aspx
這里說明一下:
官方的Excel2010對象參考沒有找到,點進去就是死循環,點來點去就是找不到,哪位神人找到了麻煩回復告知一下。
Excel2003的對象模型,需要下下來安裝,比較麻煩,我也是安裝了之后才知道的,這里推薦大家就通過上面的網址查看Excel2007的模型就可以了。 實際上按照微軟的兼容慣例,Excel2010和Excel2003的差別應該不大(是嗎?)。
然后是查看Range對象的成員,期待它有沒有什么現成的方法就爽了:http://msdn.microsoft.com/en-us/library/bb225606(v=office.12).aspx
查看了一圈,也沒發現類似什么 ExportImage,SaveImage之類的方法。表示沮喪,不過也是情理之中。
絕望之中發現了一個亮點,一個方法名字叫做CopyPicture。 看了一下方法說明,是要把對象當作圖片拷到剪貼板里面。 呵呵,一個比較扭曲的想法誕生了,既然能拷到剪貼板里面,我再從剪貼板里面把圖片摳出來不就行了嗎。
好,就這么定了,說干就干。
……
此處省略200字(怎么創建excel的com對象, 怎么取到Range對象就不說了,不知道的自己查,也可以回復提問。)
……
拿到Range對象之后。調用CopyPicture方法,需要兩個參數。第一個參數是XlPictureAppearance枚舉,1表示按照屏幕的樣子拷貝,2表示按照打印時的樣子拷貝。 第二個參數是XlCopyPictureFormat枚舉,2表示拷貝成位圖,-4147表示拷貝成矢量圖片。
於是乎,我寫了大概類似如下的代碼。
Range cell = excel.Activesheet.Cells[1,1];
cell.CopyPicture(1,2);
Bitmap image = Clipboard.GetImage()
as
Bitmap;
if
(image !=
null
)
{
}
|
調試,運行,一切Ok,完全沒問題,提交代碼(期間還做了無數額外代碼,此處不提。)。(嘿嘿,這種小事能難得到樓主嗎,得意的笑。)
然后24小時過去了,話說第二天到了。
剛一上班,樓主就興沖沖的發郵件告訴大家:恭喜大家,excel的拍圖功能完成啦,大家趕快享用(是enjoy)吧。
大家也都很配合,紛紛發來賀電:“樓主Nb啊”之類的。
樓主的虛榮心得到了極大的滿足,表示很開心。沉浸在自我陶醉之中。。。
不多時,同組一MM驚呼樓主的名字,“樓主,樓主,你快來看看吧,你的拍圖功能真的太……”。 在最后還有兩個字沒說出來的時候,樓主已經飛奔到MM的身邊。話說此MM那可真是。。(嗯,先不說吧),“太給力嗎”,樓主很自信的補充了剩下的幾個字。 MM手指屏幕,“你的拍圖怎么崩掉了。”, 果然,屏幕上一個NullReferenceException囂張的躺在屏幕中央。 樓主果斷調試代碼,跟蹤發現,確實是拍圖出了問題,上面的Clipboard.GetImage() 返回了null。 怎么會呢, 樓主又反復運行了幾次,結果一樣,都是返回null。 樓主表示鴨梨很大。
不過,鴨梨歸鴨梨,樓主是個不輕易服輸的人,口頭禪是:我就不信這個邪了。 這次也是,樓主一邊說着口頭禪,一邊繼續跟蹤。
首先當然是要看看剪貼板的數據,看拷貝成功了沒,
Clipboard.GetDataObject().GetFormats() 方法返回:
[0]: "EnhancedMetafile"
[1]: "MetaFilePict"
[2]: "Link"
看樣子,應該是拷出來了。然后樓主通過這幾個format來get數據,發現還是不成功。 這就奇怪了。
樓主很受打擊,回到座位上繼續調試。
相同的代碼,在樓主的機器上就是好好的,為什么在MM的機器上就是null呢。
樓主在自己的機器上再次查看剪貼板數據。
用Clipboard.GetDataObject().GetFormats() 方法返回:
[0]: "System.Drawing.Bitmap"
[1]: "Bitmap"
[2]: "DeviceIndependentBitmap"
[3]: "Format17"
[4]: "Link"
咦,怎么和上面的不一樣呢。樓主很果斷的發現,樓主的機器上安裝的是Excel2010,而mm的機器上安裝的是Excel2007. 原來是它們考出來的數據不一樣。
果斷Google之。 於是,經過若干次失敗和跳轉,樓主來到這里:http://support.microsoft.com/kb/323530/en-us/
方才恍然大悟,這里的大概意思是說,由於.Net的一些限制,一些舊的程序(比如這里遇到的Excel2007)復制到剪貼板的數據可能不可用,需要通過本地Api來使用。 我去。。。 msdn上也沒見半個字的提醒。
樓主是個比較勤快的人,知道了這個,那還不趕快動手。
於是樓主寫下了大概類似如下的代碼:
IntPtr hwnd = excel.Hwnd;
try
|
IntPtr data = GetClipboardData(14);
|
using
(Metafile mf =
new
Metafile(data,
true
))
|
Bitmap b =
new
Bitmap(mf);
|
}
[DllImport(
"User32.dll"
)] [
return
: MarshalAs(UnmanagedType.Bool)]
public
static
extern
bool
OpenClipboard(IntPtr hWndNewOwner);
[DllImport(
"User32.dll"
)] [
return
: MarshalAs(UnmanagedType.Bool)]
public
static
extern
bool
CloseClipboard(); [DllImport(
"User32.dll"
)]
public
static
extern
IntPtr GetClipboardData(System.UInt32 uFormat);
|
調試,運行,成功。呵呵,又是得意的笑。提交代碼(期間省略若干額外代碼)。
樓主又一次興沖沖的發郵件給大家:excel的拍圖可以用啦,大家快來享用吧。
然后,大家很配合紛紛發來賀電,然后。。。。
然后。。。。
然后什么,然后沒了,很抱歉讓大家失望了,這次沒出問題。搞定。得意的笑。
總結一下吧:
1. 第一點要注意的是,Excel2007和Excel2010的拷貝數據格式不一樣,要特別注意。
2. 很顯然,excel里面凡是帶有CopyPIcture方法的對象,都可以這樣拍圖。 粗略的看了一下,很多對象 都有這個方法,Range,Shape,Chart等等。
3. 另外,對於Chart對象,它還有一個Export方法,可以直接導出成圖片。
4. 辛勤的樓主把上面的方法稍稍包裝了一個Win32ClipboardHelper, 使用其中的GetImage傳入excel的hwnd,就可以從剪貼板里面取出圖片了。 上傳到附件中,供大家享用。
5. 最后,友情提醒,由於某些原因,上文中出現的代碼都是示意代碼,與真實項目無關,也不保證上面的代碼能編譯通過,大家領會精神,不可較真。
在以上如何把Excel中的單元格等對象保存成圖片的學習中,我們又增加了對Excel使用的認識。如果在項目中需要集成Excel的功能,還可以利用一下開發工具。SpreadJS 是企業級JavaScript電子表格控件,能將電子表格、數據可視化及計算功能集成在JavaScript Web應用程序中,能創建計算器、動態交互式儀表盤和樣式豐富的報表。
源碼下載
Win32ClipboardHelper.zip