Autodesk官方最新的.NET教程(六)(vb.net版)

第6章  更多的用戶界面:添加自定義數據

在本章中,我們將介紹.NET API的用戶界面部分能做些什麼。我們首先將介紹一個自定義上下文菜單(快捷菜單)。接下來我們將實現一個無模式可停靠的面板(一個真正的AutoCAD增強輔助窗口)來支持拖放操作。接着我們將介紹通過模式窗體選取實體。最後,我們將介紹使用AutoCAD的選項對話框來設置僱員的缺省值。

本章還會介紹和上面內容有關的API

 

第一部分 自定義上下文菜單

 

到目前爲止,我們所寫的代碼只與CommandMethod屬性定義的命令行進行相互操作。一個AutoCAD .NET程序能通過一個特殊的類來實現裝載時的初始化工作。這個類只要實現IExtensionApplication .NET接口並暴露一個組件級別的屬性(此屬性把類定義爲ExtensionApplication),就可以響應一次性的裝載和卸載事件。例子:

 

<Assembly: ExtensionApplication(GetType(AsdkClass1))>

 

Public Class AsdkClass1

    Implements IExtensionApplication

 

 

1)   現在修改AsdkClass1類來實現上面的接口。要實現這個接口,你必須實現Initialize() Terminate()函數。因爲我們要實現的是一個接口,而接口中的函數總是定義爲純虛擬的。

 

    Overridable Sub Initialize() Implements IExtensionApplication.Initialize

    End Sub

 

    Overridable Sub Terminate() Implements IExtensionApplication.Terminate

    End Sub

 

爲了加入自定義上下文菜單,我們必須定義一個‘ContextMenuExtension’類的成員。這個類位於Autodesk.AutoCAD.Windows命名空間中。

要使用ContextMenuExtension,我們必須使用new關鍵字來進行初始化,給必要的屬性賦值,並調用Application.AddDefaultContextMenuExtension()。上下文菜單的工作方式是:對於每個菜單條目,我們定義一個成員函數來處理菜單單擊事件。我們可能通過.NET的代理來實現。我們使用VB關鍵字‘AddHandler’‘AddressOf’確定讓哪個函數來處理事件。請儘快熟悉這種設計模式,因爲在.NET中會使用很多。

 

2)   添加一個‘ContextMenuExtension’成員變量和下面兩個用來添加和移除自定義菜單的函數。請好好研究一下代碼來看看究竟發生了什麼。

Sub AddContextMenu()

        Try

            m_ContextMenu = New ContextMenuExtension()

            m_ContextMenu.Title = "Acme Employee Menu"

 

            Dim mi As MenuItem

            mi = New MenuItem("Create Employee")

            AddHandler mi.Click, AddressOf CallbackOnClick

            m_ContextMenu.MenuItems.Add(mi)

 

            Application.AddDefaultContextMenuExtension(m_ContextMenu)

        Finally

        End Try

    End Sub

 

    Sub RemoveContextMenu()

        Try

            If Not m_ContextMenu Is Nothing Then

                Application.RemoveDefaultContextMenuExtension(m_ContextMenu)

                m_ContextMenu = Nothing

            End If

        Finally

        End Try

    End Sub

 

注意我們在代碼中使用了‘CallbackOnClick’函數。我們希望這個函數(我們現在還沒有定義)響應菜單項選擇事件。在我們的例子中,我們想要做的是調用我們的成員函數‘Create()’。請加入下面的代碼。

 

    Sub CallbackOnClick(ByVal Sender As Object, ByVal e As System.EventArgs)

        Create()

    End Sub

 

現在,從Initialize()中調用AddContextMenu()函數,同樣地,請在Terminate()中調用RemoveContextMenu()

請運行代碼。使用NETLOAD來裝載編譯好的組件,然後在AutoCAD的空白處右擊……你應該可以看到’Acme‘快捷菜單了。如果失敗了,明明你做的都是正確的……爲什麼呢?

通常,AutoCAD的數據是存儲在文檔中的,而訪問實體的命令有權修改文檔。當我們運行代碼來響應上下文菜單單擊事件,我們是從命令結構的外部來訪問文檔。當我們調用的代碼嘗試通過添加一個僱員來修改文檔時,我們就碰到了錯誤。正確的做法是必須鎖住文檔,這可以通過使用Document.LockDocument()命令來實現。

 

 

3)  修改CallbackOnClick來鎖住文檔:

    Sub CallbackOnClick(ByVal Sender As Object, ByVal e As System.EventArgs)

        Dim docLock As DocumentLock = Application.DocumentManager.MdiActiveDocument.LockDocument()

        Create()

        docLock.Dispose()

    End Sub

 

注意,我們保留了一個‘DocumentLock’對象的拷貝。要把文檔解鎖,我們只要銷燬這個DocumentLock對象。

再次運行代碼。現在應該可以看到快捷菜單了。

 

 

第2部分 無模式對話框、可進行拖放的可停靠面板

爲了使我們的用戶界面和AutoCAD實現無縫鏈接,我們要儘可能在所有的地方使用同樣的用戶界面結構。這會使應用程序看起來與AutoCAD結合的很好,並有效地減少代碼的重複。一個很好的例子是AutoCAD中的可停靠面板。

使用.NET API,我們可以創建一個簡單的窗體並把它放到面板中。我們可以實例化一個自定義的‘PaletteSet’對象來包含窗體,並可以把這個PaletteSet定義成我們喜歡的樣式。

 

 

4)   在解決方案瀏覽器中通過右擊工程來添加一個用戶控件。給它命名爲ModelessForm。使用控件工具箱,加入如下所示的編輯框和標籤控件。 

 

 

使用屬性窗口設置三個編輯框的屬性。設置如下:

<首先是最上面的編輯框>

(Name)    = tb_Name

Text = <請輸入一個名字>

 

<第二個編輯框>

(Name) = tb_Division

Text = Sales

 

<第三個編輯框>

(Name) = tb_Salary

Text = <請輸入薪水>

 

 

要使用.NET API實例化一個面板對象,你必須要實例化用戶控件對象(無模式窗體)和‘PaletteSet’對象。調用PaletteSet的成員函數Add來傳遞用戶控件對象。

 

5)   接下來,我們要加入一個命令來創建面板。在類中加入一個名爲CreatePalette的函數和CommandMethod屬性來定義名爲“PALETTE”的命令。

 

請看一下下面的代碼塊。這是實例化面板的代碼。

 

  ps = New Autodesk.AutoCAD.Windows.PaletteSet("Employee Palette”)

Dim myForm As ModelessForm = New ModelessForm()

ps.Add("Employee Palette", myForm)

ps.MinimumSize = New System.Drawing.Size(300, 300)

ps.Visible = True

 

6)  把上面的代碼加入到CreatePalette()函數。‘ps’需要在函數的外部聲明:

 

Dim ps As Autodesk.AutoCAD.Windows.PaletteSet = Nothing

在函數的實例化面板代碼之前加入檢查ps是否爲null的代碼。

編譯並運行工程。在AutoCAD中裝載組件,運行‘PALETTE’命令來檢查面板是否被裝載。

使用PaletteSet.Style來看看PaletteSetStyles對象。例如:

ps.Style = PaletteSetStyles.ShowTabForSingle

我們也可以試試諸如透明性的屬性,例如:

 

ps.Opacity = 65

 

注意:要使用PaletteSet PaletteSetStyles對象,你必須加入兩個命名空間Autodesk.AutoCAD.WindowsAutodesk.AutoCAD.Windows.Palette

 

在我們繼續之前,讓我們執行一個快速的維護更新:請在AsdkClass1類中加入下列成員:

 

    Public Shared sDivisionDefault As String = "Sales"

    Public Shared sDivisionManager As String = "Fiona Q. Farnsby" ‘ for this, you can chose any name you like

 

這些值將被用作爲部門和部門經理的缺省值。由於它們被聲明爲’static’,它們在每個程序中只實例化一次,並在組件裝載的時候實例化。

 

2a部分 在無模式窗體中加入拖放支持

在這部分,我們將加入允許我們使用面板窗體中編輯框的值來創建一個僱員。當用戶從面板中拖動到AutoCAD中,將會提示輸入職位,一個新的僱員實體將使用這些值來進行創建。

 

7)  爲了支持拖放,我們首先需要一個對象來進行拖動。在編輯框的下面,另外加入一個名爲DragLabel的標籤控件,設置標籤的文本爲一些提示性的東西(‘Drag to Create Employee’)。通過這個標籤,我們可以在AutoCAD中處理拖放。

要捕捉到什麼時候拖動事件發生,我們必須要知道什麼時候鼠標開始操作。

首先,你要注意的是缺省地DragLabel被聲明爲‘WithEvents’,這允許DragLabel對象可以接收影響它的事件通知,包括我們感興趣的‘MouseMove’在內。

 

8)    ModelessForm類中加入下面的函數聲明:

 

Private Sub DragLabel_MouseMove()

 

現在在‘Handles DragLabel.’ 後面加入點來看智能提示:

 

Private Sub _ DragLabel_MouseMove()Handles DragLabel.

 

可以看到我們可以選擇的所有事件。找到‘MouseMove’並把它加入。在MouseMove 事件下面有一行藍色的東西(智能提示),因爲它們的形式不一樣。通常,事件處理使用兩個參數,一個object類的sender和與事件有關的參數。對於MouseMove,我們也要做同樣的事情。改變函數的聲明來接收 ‘sender’ ‘e’

 

    Private Sub DragLabel_MouseMove(ByVal sender As System.Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles DragLabel.MouseMove

 

    End Sub

 

運行這個工程,檢查一下當鼠標經過文本的時候,函數是否被調用的。

我們還可以進一步知道是不是按了鼠標左鍵:

 

If (System.Windows.Forms.Control.MouseButtons = System.Windows.Forms.MouseButtons.Left) Then

 

End If

 

我們需要一個方法來檢測什麼時候對象被拖入到AutoCAD。我們可以使用.NET的基類DropTarget來實現。要使用它,你只要創建從這個基類派生的類並實現你想要的函數。在我們這個例子中,我們需要的是OnDrop()

 

9)    在工程中加入一個從Autodesk.AutoCAD.Windows.DropTarget派生的類‘MyDropTarget’。如果你把這個類加入到ModelessForm.cs文件中,請把這個類加入到ModelessForm類之後。

 

Public Overrides Sub OnDrop(ByVal e As System.Windows.Forms.DragEventArgs)

End Sub

 

在這個函數中,我們最後會調用AsdkClass1的成員CreateDivision() CreateEmployee,傳入ModelessForm類中的編輯框的值。要實現這個功能,我們需要一個方法來連接ModelessForm實例。最佳的方法是通過DragEventArgs。但首先我們要把鼠標事件連接到MyDropTarget類。

 

10)   加入下面的代碼到鼠標左鍵(MouseButtons.Left)處理函數中:

 

Application.DoDragDrop(Me, Me, System.Windows.Forms.DragDropEffects.All, New MyDropTarget())

 

注意我們傳入’me’兩次。第一次是用於Control參數,第二次是用於傳入用戶自定義數據。因爲我們傳入的是ModelessForm 類的實例,所以我們可以在放下的時候使用它來獲取編輯框的值。

 

11)   回到OnDrop處理函數,讓我們使用參數來調用創建僱員的函數。首先,添加職位提示的代碼。在AsdkClass1.Create()中已經有相關的代碼了,位於‘Get Employees Coordinates…’.註釋下面。添加此代碼來提示輸入職位。

 

12)   接下來,獲取傳入到DragEventArgs 參數的ModelessForm對象:

 

Dim ctrl As ModelessForm = e.Data.GetData(GetType(ModelessForm))

 

請注意一下怎樣通過GetType關鍵字把參數強制轉化爲ModelessForm的實例。

13)   使用上面的實例來調用AsdkClass1成員:

 

AsdkClass1.CreateDivision(ctrl.tb_Division.Text, AsdkClass1.sDivisionManager)

AsdkClass1.CreateEmployee(ctrl.tb_Name.Text, ctrl.tb_Division.Text, ctrl.tb_Salary.Text, prPosRes.Value())

 

注意:AsdkClass1的方法要不通過AsdkClass1的實例來調用,那麼方法必須被聲明爲 Shared。因爲public static 方法只能調用其它的Shared方法,你需要修改幾個AsdkClass1類中的方法爲 Shared。請你進行相關的修改(應該至少有4項要修改)。

 

14)   最後,因爲我們處理的事件位於AutoCAD命令之外,我們必須再次在會修改數據庫的代碼處鎖住文檔。請加入鎖住文檔的代碼,加入的方法與前面的上下文菜單是一樣的。

編譯、裝載並運行組件,使用PALETTE命令。你應該可以使用拖放操作來創建一個僱員了。

 

 

第三部分  從有模式窗體中選擇實體

本章的以下部分將演示獲取一個用戶在屏幕上選擇的僱員實例的詳細信息,並把信息顯示在一個有模式窗體的編輯框中。這部分的重點是創建一個有模式窗體,並在執行選擇操作而窗體要失去焦點時隱藏它。爲了獲取僱員的詳細信息,我們將使用第4章結束時給出的ListEmployee幫助函數。

首先,我們需要創建一個窗體類。這個類是一個真實的窗體而不是我們在ModelessForm中創建的用戶控件。

 

15)   在工程中創建一個Windows窗體類。調用‘ModalForm’類。在窗體中加入以下所示的三個編輯框控件和標籤控件以及兩個按鈕。

 

 

 

使用屬性窗口來設置三個編輯框的屬性。設置如下:

<首先是最上面的編輯框>

(Name)    = tb_Name

Text = <空白>

 

<第二個編輯框>

(Name) = tb_Division

Text = <空白>

 

<第三個編輯框>

(Name) = tb_Salary

Text = <空白>

 

<上部的按鈕>

(Name)  = SelectEmployeeButton

Text = Select Employee

 

<下部的按鈕>

(Name)  = Close

Text = Close

 

接下來創建按鈕的事件處理函數。‘Close’按鈕可以只簡單地調用:

       Me.Close()

 

要顯示對話框,讓我們在類中創建一個把窗體實例化爲有模式對話框的命令函數。下面的實現的代碼:

 

      <CommandMethod("MODALFORM")> _

      Public Sub ShowModalForm()

Dim modalForm As ModalForm = New ModalForm()

Application.ShowModalDialog(modalForm)

End Sub

 

編譯、裝載並在AutoCAD中運行MODALFORM命令來看看對話框是否可以工作。試試在對話框的右下角調整對話框的大小,然後關閉它。注意,重新使用MODALFORM命令時,對話框會出現在你上次離開的地方!這是ShowModalDialog方法的一個特徵。大小和位置值被AutoCAD保存了。

 

 ‘Select Employee’按鈕首先將執行一個簡單的實體選擇。這我們可以通過使用Editor.GetEntity()方法來實現,選擇單一的實體比使用選擇集來得方便的多。下面是怎樣使用這個方法的代碼:

 

Dim prEnt As PromptEntityOptions = New PromptEntityOptions("Select an Employee")

Dim prEntRes As PromptEntityResult = ed.GetEntity(prEnt)

 

16)   把上面的代碼加入到SelectEmployeeButton_Click處理函數中,還要加入必需的數據庫、命令行、事務處理設置變量和一個try catch塊。不要忘了在finally塊中銷燬它們。

使用PromptStatus.OK來測試GetEntity的返回值,如果返回不等於,就調用this.Show並退出處理函數。

一旦我們獲得的返回值是OK,那麼我們就可以使用PromptEntityResult.ObjectId()方法來獲取所選實體的object Id。這個id可以和一個固定的字符串數組被傳入到AsdkClass1.ListEmployee函數中來獲取僱員的詳細信息。可以通過以下的代碼說明:

 

            Dim saEmployeeList(-1) As String 'This is right...it is redimed in the ListEmployee function.

 

            AsdkClass1.ListEmployee(prEntRes.ObjectId, saEmployeeList)

            If (saEmployeeList.Length = 4) Then

                tb_Name.Text = saEmployeeList(0)

                tb_Salary.Text = saEmployeeList(1)

                tb_Division.Text = saEmployeeList(2)

            End If

 

17)   加入上面的代碼,它會在窗體的編輯框中顯示僱員的詳細信息。

 

在開始測試代碼之前,我們還要記住的是代碼是在有模式對話框中運行的,也就意味着當對話框可見的時候用戶與AutoCAD的互操作是被禁止的。在用戶能夠進行選擇僱員對象之前,我們必須隱藏窗體。當選擇結束後,我們可以再次站窗體顯示(例如,可以在finally塊的函數中)

18)   在選擇之前加入隱藏窗體的代碼(例如在try塊之前) ‘this.Hide’ 和選擇結束後顯示窗體的代碼(例如,可以在finally塊中)‘this.Show’

編譯、裝載並在AutoCAD中運行MODALFORM命令來看看對話框是否工作。試試選擇一個實體並填充窗體中編輯框的值。

 

 

第四部分 在AutoCAD選項對話框中加入頁面

 

本章的最後部分將向你介紹如何定義一個用戶控件,這個控件可以被作爲一個頁面顯示在AutoCAD的選項對話框中。我們可以使用這個頁面來設置程序運行期間的缺省值。在Employee例子中,我們只是在AsdkClass1類中簡單地設置了sDivisionDefault sDivisionManager字符串。

 

19)   在工程中加入另外一個名爲‘EmployeeOptions’的用戶控件。在控件中加入兩個編輯框和標籤控件,如下圖所示:

 

 

使用屬性窗口來設置編輯框的屬性,設置如下:

<上面的編輯框>

(Name)    = tb_EmployeeDivision

Text = <空白>

 

<下面的編輯框>

(Name) = tb_DivisionManager

Text = <空白>

 

使用.NET API來顯示自定義多頁對話框,需要兩個步驟。首先,通過傳入要調用的成員函數的地址,來知道什麼時候選項對話框出現。其次是實現回調函數。傳入到回調函數中的第二個參數是一個‘TabbedDialogEventArgs’對象,我們必須使用它來調用‘AddTab’函數。AddTab使用一個標題字符串和一個‘TabbedDialogExtension’對象的實例,此實例封裝了我們的窗體(其實是用戶控件)。在TabbedDialogExtension的構造函數中,我們輸入窗體的實例和回調函數(OnOK, OnCancel OnHelp)的地址。

 

20)   EmployeeOptions類中,加入一個名爲AddTabDialogpublic static函數,它會添加一個可供系統調用的事件處理:

 

    Public Shared Sub AddTabDialog()

        AddHandler Application.DisplayingOptionDialog, AddressOf TabHandler

    End Sub

 

AsdkClass1Initialize函數中加入調用此函數的代碼。因爲這個函數是在程序啓動的時候調用的(因爲類已經實現了IExtensionApplication接口),所以多頁對話框就被自動的加載。

 

20a) 實現一個相同的函數來移除事件處理,使用VBRemoveHandler關鍵字。

在這裏,你可以看到我們爲AutoCAD中的Application 對象的DisplayingOptionDialog事件加入了一個處理函數,此函數會調用‘TabHandler’函數。所以接下來我們要實現這個函數。

21)   加入下面的代碼來實現處理函數:

 

 

Private Shared Sub TabHandler(ByVal sender As Object, ByVal e As Autodesk.AutoCAD.ApplicationServices.TabbedDialogEventArgs)

Dim EmployeeOptionsPage As EmployeeOptions = New EmployeeOptions()

 

e.AddTab("Acme Employee Options", _

New TabbedDialogExtension( _

EmployeeOptionsPage, _

New TabbedDialogAction(AddressOf EmployeeOptionsPage.OnOk)))

    End Sub

 

我們首先實例化了一個EmployeeOptions對象。然後調用e.AddTab(),在這個函數中傳入了一個TabbedDialogExtension的實例。TabbedDialogExtension的構造函數使用了EmployeeOptionsPage實例和一個TabbedDialogAction對象。TabbedDialogAction對象的參數可以是Ok, Cancel Help回調函數。在這個函數中,我們使用的是OK

 

22)   現在剩下的就是確定回調函數的內容,也就是ONOK的內容。前面已經說過了,我們只要設置AsdkClass1static成員,也就是設置tb_DivisionManager tb_EmployeeDivision編輯框中的值。下面是代碼:

 

Public Sub OnOk()

AsdkClass1.sDivisionDefault = tb_EmployeeDivision.Text

AsdkClass1.sDivisionManager = tb_DivisionManager.Text

End Sub

 

編譯、裝載並選擇AutoCAD的選項菜單項來看一下我們的自定義對話框。試試設置對話框中的值並實例化一個僱員。你可以使用PRINTOUTEMPLOYEE命令來查看詳細信息。

 

附加的問題:怎樣讓對話框的編輯框能自動顯示爲AsdkClass1中的ManagerDivision字符串的內容?

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章