小男孩‘自慰网亚洲一区二区,亚洲一级在线播放毛片,亚洲中文字幕av每天更新,黄aⅴ永久免费无码,91成人午夜在线精品,色网站免费在线观看,亚洲欧洲wwwww在线观看

分享

淺談Excel開發(fā):七 Excel 自定義任務(wù)窗體

 法效天地 2014-01-29

    前面花了三篇文章講解了Excel中的UDF函數(shù)RTD函數(shù)異步UDF函數(shù),這些都是Excel開發(fā)中的重中之重。本文現(xiàn)在開始接著第二篇文章的菜單系統(tǒng)開始講解Excel中可供開發(fā)的界面元素,本文要講解的是Excel中的自定義任務(wù)面板(Custome Task Panel,CTP) 。

    自定義任務(wù)面板在Office 2003中就引入了,相信大家都用過Word中的字典和插入剪貼畫功能,左側(cè)的邊欄就是自定義面板。如下圖:

CTPIn2003

    但是Office 2003的自定義面板并沒有給我們開發(fā)人員提供開發(fā)接口,也就是說,我們不能創(chuàng)建我們的自定義的任務(wù)面板。

    從Office 2007版本開始,Office的一個顯著變化是添加了更多的快速預(yù)覽以及自定義面板,這在Office的各個產(chǎn)品中均有體現(xiàn)。如下圖:

CTPIn2010

    更重要的是,從Office 2007開始,CTP接口開放使得開發(fā)人員可以將我們自己的業(yè)務(wù)邏輯集成到自定義面板中。

一 自定義任務(wù)面板的優(yōu)點(diǎn)

    自定義參數(shù)面板有一些優(yōu)點(diǎn):

    首先:和傳統(tǒng)的模態(tài)和非模態(tài)彈出框相比,CTP是嵌入到Office里面的,使得我們在CTP上的交互和工作區(qū)的內(nèi)容不相互打擾,不會分散人的注意力:

    其次:用戶的學(xué)習(xí)成本比較低,用戶自定義的任務(wù)面板和內(nèi)置的面板行為相似,這些面板也可以在程序內(nèi)部上下左右??炕蛘邞腋?,我們可以創(chuàng)建多個任務(wù)面板,并且對每個面板進(jìn)行單獨(dú)的控制。

    再次,在開發(fā)方面,CTP 在Visual Studio中其實(shí)實(shí)際與窗體控件的,我們可以直接創(chuàng)建一個用戶自定義控件,然后,在設(shè)計(jì)面板上像設(shè)計(jì)Windows Form程序一樣進(jìn)行設(shè)置。

二 CTP的工作原理

    通過Office的extensibility COM類庫暴露出來的接口能夠創(chuàng)建CTP窗體。CTP其實(shí)是一個窗體,窗體內(nèi)包含了一個ActiveX控件,Excel負(fù)責(zé)管理CTP窗體,包括如何創(chuàng)建,銷毀,以及處理窗體間的消息傳遞等,而ActiveX控件則負(fù)責(zé)提供我們的業(yè)務(wù)邏輯功能。CTP就是一個容器,他能夠很好的和Excel進(jìn)行結(jié)合。CTP窗體可以在Excel界面中??炕蛘邞腋?,用戶可以改變大小,移動,或者關(guān)閉,這些都是由Excel來處理的。

    一般地,我們可以在VSTO和SharedAddin中創(chuàng)建CTP應(yīng)用程序。使用VSTO創(chuàng)建CTP應(yīng)用程序比較簡單,只需要創(chuàng)建一個自定義窗體即可。在SharedAddin中創(chuàng)建CTP應(yīng)用程序稍微有點(diǎn)兒復(fù)雜,Excel在加載Addin時(shí)需要創(chuàng)建CTP窗體,整個大致流程如下:

CTP

    首先,Excel啟動的時(shí)候,通過注冊表項(xiàng)加載特定的Addin程序,在加載Addin的時(shí)候查詢Addin上是否實(shí)現(xiàn)由ICustomTaskPane接口,如果有,調(diào)用CTPFactoryAvailable接口,并將該方法的參數(shù)ICTPFactory對象保存下來,在后面調(diào)用該對象的Create方法創(chuàng)建對象,Create方法需要指定該CTP窗體內(nèi)的ActiveX控件的ProgID值,然后將生成的CustomTaskPane對象保存,在根據(jù)菜單項(xiàng)顯示或者隱藏該對象。

    接下來暫時(shí)如何在VSTO以及SharedAddin中實(shí)現(xiàn)CTP的功能。

三 CTP的實(shí)現(xiàn)

    我們可以在VSTO和SharedAddin中創(chuàng)建CTP應(yīng)用程序,CTP內(nèi)部的ActiveX控件其實(shí)是一個窗體,當(dāng)在VSTO中創(chuàng)建CTP的時(shí)候,我們只需要實(shí)例化這個自定義窗體就可以了;但是在SharedAddin中要創(chuàng)建CTP必須要把自定義控件注冊為Com組件才能使用。

    以天氣預(yù)報(bào)功能為例子,在前面講解RTD函數(shù)和異步UDF函數(shù)的時(shí)候,我們后面掩飾的時(shí)候,是直接在單元格里面敲函數(shù)表達(dá)式以及參數(shù)的,這樣對用戶不友好,所以我們將這部分功能可以放置在一個CTP中,然后用戶進(jìn)行選擇城市以及指標(biāo),點(diǎn)擊確定,我們再往Excel通過代碼寫入函數(shù)表達(dá)式。這樣就可以防止用戶的錯誤輸入了。

    我們將在第二篇文章中的菜單系統(tǒng)作為例子,將相應(yīng)項(xiàng)目加載到VS中來。首先為了達(dá)到代碼的重用性,我們將該部分要顯示的CTP里面的ActiveX控件放置到一個類庫YYWeatherCTP里面,然后在里面僅添加一個名為YYWeatherWizard的自定義窗體,該窗體設(shè)計(jì)如下:

YYWeatherWizard

    該窗體主要用來幫助用戶往Excel中插入天氣函數(shù)。首先選擇要看的城市,然后勾選該城市的各個氣象指標(biāo)。界面很簡單,每個CheckBox的tag都保存有該指標(biāo)的函數(shù)名稱。需要注意的是,我們在該用戶窗體中公開的一個可以供外部注冊的事件,該事件在點(diǎn)擊插入按鈕時(shí)觸發(fā)。具體代碼為:

public event EventHandler<WeatherFunctionEventArgs> InsertFunctionEventHandler;

private void btnInsert_Click(object sender, EventArgs e)
{
    if (InsertFunctionEventHandler != null)
    {
        String cityName = this.cmbCity.SelectedText;
        List<String> functionNames = new List<string>();
        foreach (Control control in this.Controls)
        {
            GroupBox gb=control as GroupBox;
            if (gb != null)
            {
                foreach (Control ccontrol in gb.Controls)
                {
                    CheckBox cb = ccontrol as CheckBox;
                    if (cb != null)
                    {
                        if (cb.Checked)
                        {
                            functionNames.Add(cb.Tag.ToString());
                        }
                    }
                }
            }
        }
        WeatherFunctionEventArgs args = new WeatherFunctionEventArgs(cityName, functionNames);
        EventHandler<WeatherFunctionEventArgs> temp = null ;
        if (Interlocked.Exchange(ref temp, InsertFunctionEventHandler)==null)
        {
            temp(sender, args);
        }
    }
}

    上面的代碼公開了InsertFunctionEventHandler事件,該事件的參數(shù)是一個WeatherFunctionEventArgs類型的 EventArgs類,具體定義如下:

public class WeatherFunctionEventArgs : EventArgs
{
    private readonly List<String> functionNames = new List<string>();
    private readonly String cityName;
    public List<String> FunctionNames { get { return functionNames; } }
    public String CtiyName { get { return cityName; } }

    public WeatherFunctionEventArgs(String cityName, List<String> weatherFunctions)
    {
        this.cityName = cityName;
        this.functionNames = weatherFunctions;
    }
}

    注冊了插入事件之后,在回調(diào)方法中,可以返回用戶選擇的城市,以及選中的指標(biāo),我們將來可以在該回調(diào)方法中,利用這些信息往Excel中插入函數(shù)。

    該自定義控件編寫完成之后,我們來演示如何在VSTO以及SharedAddin中將該用戶控件作為CTP加載進(jìn)來。先看看在VSTO中如何使用該控件。

3.1 在VSTO中創(chuàng)建CTP

    在第二篇文章中,我們介紹了Excel中的菜單系統(tǒng),包括如何在VSTO以及SharedAddin中創(chuàng)建Excel菜單,本文在此基礎(chǔ)上進(jìn)行。首先打開我們之前在VSTO中創(chuàng)建的Ribbon菜單的后臺代碼。首先我們定義了一個名為TaskPanels的Dictionary對象,其中key為CTP的英文名稱,Value表示一個CTP的CustomTaskPane對象。這里一般我們在創(chuàng)建完CTP之后,將其保存起來,后面如果不用的話,就將其隱藏,這樣避免了來回創(chuàng)建CustomTaskPane對象造成的性能開銷。

[ComVisible(true)]
public class Ribbon : Office.IRibbonExtensibility
{
    private Office.IRibbonUI ribbon;

    private Dictionary<String, CustomTaskPane> TaskPanels = new Dictionary<string, CustomTaskPane>();

    public static CustomTaskPaneCollection TaskPanelCollection;

    void yyWeatherCtp_InsertFunctionEventHandler(object sender, WeatherFunctionEventArgs e)
    {
        MessageBox.Show("Your insert a bunch of weather functions");
    }
}

    然后我們定義了一個公共的靜態(tài)的TaskPanelCollection對象,用來保存,系統(tǒng)調(diào)用時(shí)的Addin對象。這個就和RTD函數(shù)中我們要通過局部變量保存IRTDUpdateEvent對象一邊后來調(diào)研UpdateNotify一樣,我們通過TaskPanelCollection對象保存全局的表示TaskPanel集合的對象,如下,在StartUp中給該對象賦值:

private void ThisAddIn_Startup(object sender, System.EventArgs e)
{
    Ribbon.TaskPanelCollection = this.CustomTaskPanes;
}

    完成之后,我們在Ribbon菜單項(xiàng)中處理菜單點(diǎn)擊事件。當(dāng)用戶點(diǎn)擊天氣函數(shù)的時(shí)候, 我們首先在TaskPanel中根據(jù)key查找有沒有之前創(chuàng)建好了的CustomTaskPane,如果有,直接取出來,并將其Visible屬性改為true。

public void GeneralButton_Click(Office.IRibbonControl control)
   {
       try
       {
           if (control.Id.Equals("btnWeatherFunction"))
           {
               CustomTaskPane weatherFunction = null;
               if (TaskPanels.TryGetValue("btnWeatherFunction", out weatherFunction))
               {
                   weatherFunction.Visible = true;
               }
               else
               {
                   var yyWeatherCtp = new YYWeatherWizard();
                   yyWeatherCtp.InsertFunctionEventHandler += new EventHandler<WeatherFunctionEventArgs>(yyWeatherCtp_InsertFunctionEventHandler);
                   var myTaskPane = TaskPanelCollection.Add(yyWeatherCtp, "Weather Report Function");
                   myTaskPane.DockPosition = Office.MsoCTPDockPosition.msoCTPDockPositionLeft;
                   myTaskPane.Width = 300;
                   myTaskPane.Visible = true;

                   TaskPanels.Add("btnWeatherFunction", myTaskPane);
               }

           }
       }
       catch (COMException ex)
       {
           Console.WriteLine("Create CTP encounter errors: " + ex.ToString());
       }
   }

    如果不存在, 首先創(chuàng)建一個之前設(shè)計(jì)好的YYWeatherWizard自定義窗體,并注冊其插入按鈕的回調(diào)方法。緊接著調(diào)用TaskPanelCollection的Add方法,該方法第一個參數(shù)接受一個實(shí)例化的自定義窗體,也就是我們之前創(chuàng)建好的YYWeatherWizard對象,第二個參數(shù)是CTP中在窗體左上角顯示的名稱,這里我們起名為“Weather Report Function”。然后緊接著設(shè)置停靠的位置,寬度,以及是否可見。最后,將我們創(chuàng)建好的CustomTaskPane對象存儲到自定中,以用作緩存。

    編譯運(yùn)行,就可以看到效果了。這是在VSTO中創(chuàng)建CTP的方法,接下來將要介紹下如何在SharedAddin中創(chuàng)建CTP。

3.2 在SharedAddin中創(chuàng)建CTP

    在SharedAddin中創(chuàng)建CTP比在VSTO中要復(fù)雜一些,在SharedAddin中使用CTP我們需要將創(chuàng)建好的自定義窗體注冊為Com組件,這樣在SharedAddin中才能直接調(diào)用。因?yàn)槲覀冎耙呀?jīng)定義了YYWeatherFucntion自定義用戶窗體控件,我們需要做的就是讓其Com可見,改動也非常簡單。

[ComVisible(true)]
public partial class YYWeatherWizard : UserControl

然后類庫編譯的時(shí)候,設(shè)置下Com可見:

Compile CTP

    如果您使用的是Win7系統(tǒng),那么需要以管理員權(quán)限運(yùn)行Visual Studio編譯,VS會調(diào)研regasm程序?qū)ll進(jìn)行注冊,并往注冊表里面寫注冊表項(xiàng)。

    這一步完成之后,我們打開在第二篇文章中我們創(chuàng)建的SharedAddin應(yīng)用程序。

/// <summary>
///   The object for implementing an Add-in.
/// </summary>
/// <seealso class='IDTExtensibility2' />
[GuidAttribute("B61D06DE-C254-43B4-A417-BF2A45270D37"), ProgId("YYSharedAddin.Connect")]
public partial class Connect : Object, Extensibility.IDTExtensibility2, ICustomTaskPaneConsumer
{
    static ICTPFactory ctpFactory = null;

    public void CTPFactoryAvailable(ICTPFactory CTPFactoryInst)
    {
       ctpFactory=CTPFactoryInst;
    }

…………..
}

    要在SharedAddin中實(shí)現(xiàn)CTP必須讓Connect對象實(shí)現(xiàn)ICustomTaskPaneConsumer接口,該僅有一個方法,我們定義了一個ICTPFactory對象來講該方法傳進(jìn)來的CTPFactoryInst對象保存起來。為以后創(chuàng)建CTP對象做準(zhǔn)備。

    在Ribbon的事件處理中,我們處理菜單點(diǎn)擊事件:

public void GeneralButton_Click(Office.IRibbonControl control)
{
    if (control.Id.Equals("btnWeatherFunction"))
    {
        CustomTaskPane weatherFunction = null;
        if (TaskPanels.TryGetValue("btnWeatherFunction", out weatherFunction))
        {
            weatherFunction.Visible = true;
        }
        else
        {
            try
            {
                weatherFunction = ctpFactory.CreateCTP("YYWeatherCTP.YYWeatherWizard", "Weather Report Function", Type.Missing);
                weatherFunction.DockPosition = MsoCTPDockPosition.msoCTPDockPositionLeft;
                weatherFunction.Width = 270;
                weatherFunction.Visible = true;
                TaskPanels.Add("btnWeatherFunction", weatherFunction);

                Control contentControl = weatherFunction.ContentControl as Control;
                contentControl.Dock = System.Windows.Forms.DockStyle.Fill;
                YYWeatherWizard yyWeatherWizard = contentControl as YYWeatherWizard;
                yyWeatherWizard.InsertFunctionEventHandler += new EventHandler<WeatherFunctionEventArgs>(yyWeatherWizard_InsertFunctionEventHandler);
            }
            catch (COMException comEX)
            {
                Console.WriteLine("Create CTP encounter errors: " + comEX.ToString());
            }
        }
    }

}

    首先,我們在TaskPanels中查找該CTP是否已經(jīng)創(chuàng)建,如果沒有找到,則利用之前保存的ctpFactory對象來創(chuàng)建一個CustomTaskPane,注意ctpFactory的CreateCTP方法和VSTO中的Add方法不一樣,該方法第一個參數(shù)為待創(chuàng)建的CTP中的ActiveX控件,也就是我們之前創(chuàng)建的自定義窗體的類的全稱,包括程序集的名稱,第二個參數(shù)一樣??梢钥吹紼xcel使用反射技術(shù)來生成CTP中的ActiveX控件的。生成完了之后,我們得到了一個類型為CustomTaskPane的weatherFunction對象,現(xiàn)在,我們是無法注冊插入事件 。要想獲得到我們得自定義窗體控件,首先要將weatherFunction的ContentControl轉(zhuǎn)換為Control控件,然后再將Control控件轉(zhuǎn)換為我們得實(shí)際類型YYWeatherWizard,現(xiàn)在我們就可以注冊插入的回調(diào)方法了?;卣{(diào)方法里面很簡單,就是根據(jù)選中的城市以及指標(biāo),拼出RTD函數(shù),然后插入到Excel中。

void yyWeatherWizard_InsertFunctionEventHandler(object sender, WeatherFunctionEventArgs e)
{
    String city = e.CtiyName;
    String dt = DateTime.Today.ToString("yyyy-MM-dd");

    String[,] formulas = new String[1, e.FunctionNames.Count];
    for (int i = 0; i < e.FunctionNames.Count; i++)
    {
        //            =RTD("YYAsyncRTD.Func",,"YY_Weather_Condition",2, "Shanghai",TODAY())
        formulas[0, i] = String.Format("=RTD(\"YYAsyncRTD.Func\",,\"{0}\",2,\"{1}\",\"{2}\"", e.FunctionNames[i], city, dt);
    }
    Range targetRange = applicationObject.Application.ActiveCell as Range;
    if (targetRange == null)
        targetRange = applicationObject.Application.get_Range("A1", Type.Missing);
    FillRange(targetRange, formulas, true);
}

    現(xiàn)在在SharedAddin中創(chuàng)建CTP已經(jīng)實(shí)現(xiàn)啦。

3.3 運(yùn)行效果

    下面的動畫就是整個CTP的效果:當(dāng)點(diǎn)擊菜單上的天氣函數(shù)的時(shí)候,左側(cè)欄出現(xiàn)天氣函數(shù)的CTP窗體,當(dāng)點(diǎn)擊插入的時(shí)候,即可向Excel中插入天氣函數(shù),然后天氣函數(shù)去獲取當(dāng)前選中城市的選中的天氣指標(biāo)的情況,很酷吧。

CTP

四 總結(jié)

    Excel中的CTP面板具有良好的用戶體驗(yàn),是業(yè)務(wù)邏輯的一中比較好的載體,在插件開發(fā)中具有比較重要的位置。本文介紹了Excel 中的Custom Task Panel自定義任務(wù)面板功能的運(yùn)行原理,開發(fā)方法,以及如何在VSTO以及SharedAddin中創(chuàng)建和使用CTP,最后以一個天氣函數(shù)的實(shí)例協(xié)助用戶插入天氣函數(shù)的CTP面板做了演示。

    本文所有代碼點(diǎn)擊此處下載,希望本文對您了解Excel中的自定義任務(wù)面板有所幫助。

    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多