Autodesk官方最新的.NET教程(六)(C#版)
來源:互聯網
上載者:User
教程 第6章 更多的使用者介面:添加自訂資料 在本章中,我們將介紹.NET API的使用者介面部分能做些什麼。我們首先將介紹一個自訂操作功能表(捷徑功能表)。接下來我們將實現一個無模式可停靠的面板(一個真正的AutoCAD增強次要視窗)來支援拖放操作。接著我們將介紹通過模式表單選取實體。最後,我們將介紹使用AutoCAD的選項對話方塊來設定僱員的預設值。本章還會介紹和上面內容有關的API。 第一部分 自訂操作功能表 到目前為止,我們所寫的代碼只與CommandMethod屬性定義的命令列進行相互操作。一個AutoCAD .NET程式能通過一個特殊的類來實現裝載時的初始化工作。這個類只要實現IExtensionApplication .NET介面並暴露一個組件層級的屬性(此屬性把類定義為ExtensionApplication),就可以響應一次性的裝載和卸載事件。例子:[assembly: ExtensionApplication(typeof(Lab6_CS.AsdkClass1))] public class AsdkClass1 : IExtensionApplication { 1) 現在修改AsdkClass1類來實現上面的介面。要實現這個介面,你必須實現Initialize() 和Terminate()函數。因為我們要實現的是一個介面,而介面中的函數總是定義為純虛擬。 public void Initialize() { AddContextMenu(); EmployeeOptions.AddTabDialog(); } public void Terminate() { } 為了加入自訂操作功能表,我們必須定義一個‘ContextMenuExtension’類的成員。這個類位於Autodesk.AutoCAD.Windows命名空間中。要使用ContextMenuExtension,我們必須使用new關鍵字來進行初始化,給必要的屬性賦值,並調用Application.AddDefaultContextMenuExtension()。操作功能表的工作方式是:對於每個菜單條目,我們定義一個成員函數來處理菜單單擊事件。我們可能通過.NET的代理來實現。我們使用C#關鍵字+=和-=確定讓哪個函數來處理事件。請儘快熟悉這種設計模式,因為在C#中會使用很多。2) 添加一個‘ContextMenuExtension’成員變數和下面兩個用來添加和移除自訂菜單的函數。請好好研究一下代碼來看看究竟發生了什麼。 void AddContextMenu() { try { m_ContextMenu = new ContextMenuExtension(); m_ContextMenu.Title = "Acme Employee Menu"; Autodesk.AutoCAD.Windows.MenuItem mi; mi = new Autodesk.AutoCAD.Windows.MenuItem("Create Employee"); mi.Click += new EventHandler(CallbackOnClick); m_ContextMenu.MenuItems.Add(mi); Autodesk.AutoCAD.ApplicationServices.Application.AddDefaultContextMenuExtension(m_ContextMenu); } catch { } } void RemoveContextMenu() { try { if( m_ContextMenu != null ) { Autodesk.AutoCAD.ApplicationServices.Application.RemoveDefaultContextMenuExtension(m_ContextMenu); m_ContextMenu = null; } } catch { } } 注意我們在代碼中使用了‘CallbackOnClick’函數。我們希望這個函數(我們現在還沒有定義)響應功能表項目選擇事件。在我們的例子中,我們想要做的是調用我們的成員函數‘Create()’。請加入下面的代碼。 void CallbackOnClick(object Sender, EventArgs e) { Create(); } 現在,從Initialize()中調用AddContextMenu()函數,同樣地,請在Terminate()中調用RemoveContextMenu()。請運行代碼。使用NETLOAD來裝載編譯好的組件,然後在AutoCAD的空白處右擊……你應該可以看到’Acme‘捷徑功能表了。如果失敗了,明明你做的都是正確的……為什麼呢?通常,AutoCAD的資料是儲存在文檔中的,而訪問實體的命令有權修改文檔。當我們運行代碼來響應操作功能表單擊事件,我們是從命令結構的外部來訪問文檔。當我們調用的代碼嘗試通過添加一個僱員來修改文檔時,我們就碰到了錯誤。正確的做法是必須鎖住文檔,這可以通過使用Document.LockDocument()命令來實現。3) 修改CallbackOnClick來鎖住文檔: void CallbackOnClick(object Sender, EventArgs e) { DocumentLock docLock = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument.LockDocument(); Create(); docLock.Dispose(); } 注意,我們保留了一個‘DocumentLock’對象的拷貝。要把文檔解鎖,我們只要銷毀這個DocumentLock對象。再次運行代碼。現在應該可以看到捷徑功能表了。 第2部分 無強制回應對話方塊、可進行拖放的可固定面板為了使我們的使用者介面和AutoCAD實現無縫連結,我們要儘可能在所有的地方使用同樣的使用者介面結構。這會使應用程式看起來與AutoCAD結合的很好,並有效地減少代碼的重複。一個很好的例子是AutoCAD中的可固定面板。使用.NET API,我們可以建立一個簡單的表單並把它放到面板中。我們可以執行個體化一個自訂的‘PaletteSet’對象來包含表單,並可以把這個PaletteSet定義成我們喜歡的樣式。4) 在解決方案瀏覽器中通過右擊工程來添加一個使用者控制項。給它命名為ModelessForm。使用控制項工具箱,加入如下所示的編輯框和標籤控制項。<!--[if !vml]--><!--[endif]--> 使用屬性視窗設定三個編輯框的屬性。設定如下:<首先是最上面的編輯框>(Name) = tb_NameText = <請輸入一個名字> <第二個編輯框>(Name) = tb_DivisionText = Sales <第三個編輯框>(Name) = tb_SalaryText = <請輸入薪水> 要使用.NET API執行個體化一個面板對象,你必須要執行個體化使用者控制項對象(無模式表單)和‘PaletteSet’對象。調用PaletteSet的成員函數Add來傳遞使用者控制項對象。5) 接下來,我們要加入一個命令來建立面板。在類中加入一個名為CreatePalette的函數和CommandMethod屬性來定義名為“PALETTE”的命令。請看一下下面的代碼塊。這是執行個體化面板的代碼。ps = new Autodesk.AutoCAD.Windows.PaletteSet("Test Palette Set");ps.MinimumSize = new System.Drawing.Size(300, 300);System.Windows.Forms.UserControl myCtrl = new ModelessForm();ps.Add("test", myCtrl);ps.Visible = true; 6) 把上面的代碼加入到CreatePalette()函數。‘ps’需要在函數的外部聲明: private Autodesk.AutoCAD.Windows.PaletteSet ps; 在函數的執行個體化面板代碼之前加入檢查ps是否為null的代碼。編譯並運行工程。在AutoCAD中裝載組件,運行‘PALETTE’命令來檢查面板是否被裝載。使用PaletteSet.Style來看看PaletteSetStyles對象。例如:ps.Style = PaletteSetStyles.ShowTabForSingle;我們也可以試試諸如透明性的屬性,例如: ps.Opacity = 90; 注意:要使用PaletteSet 和PaletteSetStyles對象,你必須加入兩個命名空間Autodesk.AutoCAD.Windows和Autodesk.AutoCAD.Windows.Palette 在我們繼續之前,讓我們執行一個快速的維護更新:請在AsdkClass1類中加入下列成員: public static string sDivisionDefault = "Sales"; public static string sDivisionManager = "Fiona Q. Farnsby"; 這些值將被用作為部門和部門經理的預設值。由於它們被聲明為’static’,它們在每個程式中只執行個體化一次,並在組件裝載的時候執行個體化。 第2a部分 在無模式表單中加入拖放支援在這部分,我們將加入允許我們使用面板表單中編輯框的值來建立一個僱員。當使用者從面板中拖動到AutoCAD中,將會提示輸入職位,一個新的僱員實體將使用這些值來進行建立。7) 為了支援拖放,我們首先需要一個對象來進行拖動。在編輯框的下面,另外加入一個名為Label4的標籤控制項,設定標籤的文本為一些提示性的東西(‘Drag to Create Employee’)。通過這個標籤,我們可以在AutoCAD中處理拖放。要捕捉到什麼時候拖動事件發生,我們必須要知道什麼時候滑鼠開始操作。首先,我們要在類的建構函式中註冊事件,代碼如下: Label4.MouseMove += new System.Windows.Forms.MouseEventHandler(Label4_MouseMove); 8) 在ModelessForm類中加入下面的函式宣告: private void Label4_MouseMove( object sender, System.Windows.Forms.MouseEventArgs e) { if (System.Windows.Forms.Control.MouseButtons == System.Windows.Forms.MouseButtons.Left) { // start dragDrop operation, MyDropTarget will be called when the cursor enters the AutoCAD view area. Autodesk.AutoCAD.ApplicationServices.Application.DoDragDrop(this, this, System.Windows.Forms.DragDropEffects.All, new MyDropTarget()); } } 通常事件處理器有2個輸入參數,一個object類的sender和與事件有關的參數。對於MouseMove,我們也要做同樣的事情。運行這個工程,檢查一下當滑鼠經過文本的時候,函數是否被調用的。我們還可以進一步知道是不是按了滑鼠左鍵: if (System.Windows.Forms.Control.MouseButtons == System.Windows.Forms.MouseButtons.Left) { } 我們需要一個方法來檢測什麼時候對象被拖入到AutoCAD。我們可以使用.NET的基類DropTarget來實現。要使用它,你只要建立從這個基類派生的類並實現你想要的函數。在我們這個例子中,我們需要的是OnDrop()。9) 在工程中加入一個從Autodesk.AutoCAD.Windows.DropTarget派生的類‘MyDropTarget’。如果你把這個類加入到ModelessForm.cs檔案中,請把這個類加入到ModelessForm類之後。 override public void OnDrop(System.Windows.Forms.DragEventArgs e) {} 在這個函數中,我們最後會調用AsdkClass1的成員CreateDivision() 和CreateEmployee,傳入ModelessForm類中的編輯框的值。要實現這個功能,我們需要一個方法來串連ModelessForm執行個體。最佳的方法是通過DragEventArgs。但首先我們要把滑鼠事件串連到MyDropTarget類。10) 加入下面的代碼到滑鼠左鍵(MouseButtons.Left)處理函數中: Autodesk.AutoCAD.ApplicationServices.Application.DoDragDrop(this, this, System.Windows.Forms.DragDropEffects.All, new MyDropTarget()); 注意我們傳入’this’兩次。第一次是用於Control參數,第二次是用於傳入使用者自訂資料。因為我們傳入的是ModelessForm 類的執行個體,所以我們可以在放下的時候使用它來擷取編輯框的值。11) 回到OnDrop處理函數,讓我們使用參數來調用建立僱員的函數。首先,添加職位提示的代碼。在AsdkClass1.Create()中已經有相關的代碼了,位於‘Get Employees Coordinates…’.注釋下面。添加此代碼來提示輸入職位。12) 接下來,擷取傳入到DragEventArgs 參數的ModelessForm對象: ModelessForm ctrl = (ModelessForm)e.Data.GetData(typeof(ModelessForm)); 請注意一下怎樣通過typeof關鍵字把參數強制轉化為ModelessForm的執行個體。13) 使用上面的執行個體來調用AsdkClass1成員: AsdkClass1.CreateDivision(ctrl.tb_Division.Text, AsdkClass1.sDivisionManager); AsdkClass1.CreateEmployee(ctrl.tb_Name.Text, ctrl.tb_Division.Text, Convert.ToDouble(ctrl.tb_Salary.Text), prPosRes.Value); 注意:AsdkClass1的方法要不通過AsdkClass1的執行個體來調用,那麼方法必須被聲明為’ public static’。因為public static 方法只能調用其它的public static 方法,你需要修改幾個AsdkClass1類中的方法為’ public static’。請你進行相關的修改(應該至少有4項要修改)。14) 最後,因為我們處理的事件位於AutoCAD命令之外,我們必須再次在會修改資料庫的代碼處鎖住文檔。請加入鎖住文檔的代碼,加入的方法與前面的操作功能表是一樣的。編譯、裝載並運行組件,使用PALETTE命令。你應該可以使用拖放操作來建立一個僱員了。 第三部分 從有模式表單中選擇實體本章的以下部分將示範擷取一個使用者在螢幕上選擇的僱員執行個體的詳細資料,並把資訊顯示在一個有模式表單的編輯框中。這部分的重點是建立一個有模式表單,並在執行選擇操作而表單要失去焦點時隱藏它。為了擷取僱員的詳細資料,我們將使用第4章結束時給出的ListEmployee協助函數。首先,我們需要建立一個表單類。這個類是一個真實的表單而不是我們在ModelessForm中建立的使用者控制項。15) 在工程中建立一個Windows表單類。調用‘ModalForm’類。在表單中加入以下所示的三個編輯框控制項和標籤控制項以及兩個按鈕。<!--[if !vml]--><!--[endif]--> 使用屬性視窗來設定三個編輯框的屬性。設定如下:<首先是最上面的編輯框>(Name) = tb_NameText = <空白> <第二個編輯框>(Name) = tb_DivisionText = <空白> <第三個編輯框>(Name) = tb_SalaryText = <空白> <上部的按鈕>(Name) = SelectEmployeeButtonText = Select Employee <下部的按鈕>(Name) = CloseText = Close 接下來建立按鈕的事件處理函數。‘Close’按鈕可以只簡單地調用: this.Close(); 要顯示對話方塊,讓我們在類中建立一個把表單執行個體化為有強制回應對話方塊的命令函數。下面的實現的代碼: [CommandMethod("MODALFORM")] public void ShowModalForm() { ModalForm modalForm = new ModalForm(); Autodesk.AutoCAD.ApplicationServices.Application.ShowModalDialog(modalForm); } 編譯、裝載並在AutoCAD中運行MODALFORM命令來看看對話方塊是否可以工作。試試在對話方塊的右下角調整對話方塊的大小,然後關閉它。注意,重新使用MODALFORM命令時,對話方塊會出現在你上次離開的地方!這是ShowModalDialog方法的一個特徵。大小和位置值被AutoCAD儲存了。 ‘Select Employee’按鈕首先將執行一個簡單的實體選擇。這我們可以通過使用Editor.GetEntity()方法來實現,選擇單一的實體比使用選擇集來得方便的多。下面是怎樣使用這個方法的代碼: PromptEntityOptions prEnt = new PromptEntityOptions("Select an Employee"); PromptEntityResult prEntRes = ed.GetEntity(prEnt); 16) 把上面的代碼加入到SelectEmployeeButton_Click處理函數中,還要加入必需的資料庫、命令列、交易處理設定變數和一個try catch塊。不要忘了在finally塊中銷毀它們。使用PromptStatus.OK來測試GetEntity的傳回值,如果返回不等於,就調用this.Show並退出處理函數。一旦我們獲得的傳回值是OK,那麼我們就可以使用PromptEntityResult.ObjectId()方法來擷取所選實體的object Id。這個id可以和一個固定的字串數組被傳入到AsdkClass1.ListEmployee函數中來擷取僱員的詳細資料。可以通過以下的代碼說明: ArrayList saEmployeeList = new ArrayList(); AsdkClass1.ListEmployee(prEntRes.ObjectId, saEmployeeList); if (saEmployeeList.Count == 4) { tb_Name.Text = saEmployeeList[0].ToString(); tb_Salary.Text = saEmployeeList[1].ToString(); tb_Division.Text = saEmployeeList[2].ToString(); } 17) 加入上面的代碼,它會在表單的編輯框中顯示僱員的詳細資料。 在開始測試代碼之前,我們還要記住的是代碼是在有強制回應對話方塊中啟動並執行,也就意味著當對話方塊可見的時候使用者與AutoCAD的互操作是被禁止的。在使用者能夠進行選擇僱員對象之前,我們必須隱藏表單。當選擇結束後,我們可以再次站表單顯示(例如,可以在finally塊的函數中)18) 在選擇之前加入隱藏表單的代碼(例如在try塊之前) ‘this.Hide’ 和選擇結束後顯示表單的代碼(例如,可以在finally塊中)‘this.Show’。編譯、裝載並在AutoCAD中運行MODALFORM命令來看看對話方塊是否工作。試試選擇一個實體並填充表單中編輯框的值。 第四部分 在AutoCAD選項對話方塊中加入頁面 本章的最後部分將向你介紹如何定義一個使用者控制項,這個控制項可以被作為一個頁面顯示在AutoCAD的選項對話方塊中。我們可以使用這個頁面來設定程式運行期間的預設值。在Employee例子中,我們只是在AsdkClass1類中簡單地設定了sDivisionDefault 和sDivisionManager字串。19) 在工程中加入另外一個名為‘EmployeeOptions’的使用者控制項。在控制項中加入兩個編輯框和標籤控制項,如下圖所示:<!--[if !vml]--><!--[endif]--> 使用屬性視窗來設定編輯框的屬性,設定如下:<上面的編輯框>(Name) = tb_EmployeeDivisionText = <空白> <下面的編輯框>(Name) = tb_DivisionManagerText = <空白> 使用.NET API來顯示自訂多頁對話方塊,需要兩個步驟。首先,通過傳入要調用的成員函數的地址,來知道什麼時候選項對話方塊出現。其次是實現回呼函數。傳入到回呼函數中的第二個參數是一個‘TabbedDialogEventArgs’對象,我們必須使用它來調用‘AddTab’函數。AddTab使用一個標題字串和一個‘TabbedDialogExtension’對象的執行個體,此執行個體封裝了我們的表單(其實是使用者控制項)。在TabbedDialogExtension的建構函式中,我們輸入表單的執行個體和回呼函數(OnOK, OnCancel 或OnHelp)的地址。 20) 在EmployeeOptions類中,加入一個名為AddTabDialog的public static函數,它會添加一個可供系統調用的事件處理: public static void AddTabDialog() { Autodesk.AutoCAD.ApplicationServices.Application.DisplayingOptionDialog += new TabbedDialogEventHandler(TabHandler); } 在AsdkClass1的Initialize函數中加入調用此函數的代碼。因為這個函數是在程式啟動的時候調用的(因為類已經實現了IExtensionApplication介面),所以多頁對話方塊就被自動的載入。20a) 實現一個相同的函數來移除事件處理,使用C#的-=關鍵字。在這裡,你可以看到我們為AutoCAD中的Application 對象的DisplayingOptionDialog事件加入了一個處理函數,此函數會調用‘TabHandler’函數。所以接下來我們要實現這個函數。21) 加入下面的代碼來實現處理函數: private static void TabHandler(object sender, Autodesk.AutoCAD.ApplicationServices.TabbedDialogEventArgs e) { EmployeeOptions EmployeeOptionsPage = new EmployeeOptions(); e.AddTab("Acme Employee Options", new TabbedDialogExtension( EmployeeOptionsPage, new TabbedDialogAction(EmployeeOptionsPage.OnOk))); } 我們首先執行個體化了一個EmployeeOptions對象。然後調用e.AddTab(),在這個函數中傳入了一個TabbedDialogExtension的執行個體。TabbedDialogExtension的建構函式使用了EmployeeOptionsPage執行個體和一個TabbedDialogAction對象。TabbedDialogAction對象的參數可以是Ok, Cancel 或Help回呼函數。在這個函數中,我們使用的是OK。22) 現在剩下的就是確定回呼函數的內容,也就是ONOK的內容。前面已經說過了,我們只要設定AsdkClass1的static成員,也就是設定tb_DivisionManager 和tb_EmployeeDivision編輯框中的值。下面是代碼: public void OnOk() { AsdkClass1.sDivisionDefault = tb_EmployeeDivision.Text; AsdkClass1.sDivisionManager = tb_DivisionManager.Text; } 編譯、裝載並選擇AutoCAD的選項功能表項目來看一下我們的自訂對話方塊。試試設定對話方塊中的值並執行個體化一個僱員。你可以使用PRINTOUTEMPLOYEE命令來查看詳細資料。 附加的問題:怎樣讓對話方塊的編輯框能自動顯示為AsdkClass1中的Manager和Division字串的內容?