一 簡介
首先本文假定你已經熟悉VBNET和Visual StudioNET Windows表單設計器
在開發定制Windows表單控件時提供我們自己的下拉框類型編輯器來操作控件的屬性常常是非常方便的定制的類型編輯器不僅可以提供更為豐富的設計時刻體驗而且可能成為用戶是否喜歡你的控件的決定因素
如果你決定創建你自己的下拉式類型編輯器那麼它應該遵循與內置的下拉框類型編輯器相類似的模式讓我們以Anchor屬性為例一種典型的操作該屬性的設計時刻用戶交互描述如下
· 用戶選擇屬性格子中的Anchor屬性並且點擊屬性格右邊的下拉按鈕
· 一種良好的圖形控件是下拉框它能夠允許用戶使用鼠標點擊邊緣或者使用箭頭鍵來高亮某個邊緣並使用空格鍵選擇/取消選擇它
· 用戶可以通過按下ENTER鍵或點擊下拉控件的外部來接收變化為了取消這一變化用戶可以按下ESC鍵
下面讓我們來討論具體的實現技術
二 實現
首先讓我們構建一個ResourceImageEditor類型編輯器它允許從當前文件系統中選擇一個圖像文件(就象內置的ImageEditor類一樣)或者從一個程序集的manifest文件中選擇一個圖像資源而且在用戶體驗方面該ResourceImageEditor的行為應該類似於系統內置的類型編輯器下面是對我們要求的概述
當用戶從屬性格子中選擇一個屬性時該格子就會顯示出來—以一個下拉框UI形式顯示可以編輯的屬性
當點擊下拉按鈕時當前程序集中的所有圖像資源將顯示出來
當用戶選擇一個圖像資源項相應的圖像即可以從程序集中進行加載
允許選擇一個圖像文件並且在下拉列表框中的最後一項將標記為Browse當用戶點按Browse項將顯示經典的打開文件對話框用戶能夠從中選擇一個圖像文件
通過單擊鼠標或使用箭頭鍵高亮某項並按回車鍵實際選擇它從而允許用戶從該下拉列表框中選擇一項這個下拉選擇可以通過按下ESC鍵取消
ResourceImageEditor是一個類型編輯器因此它直接或間接地派生自SystemDrawingDesignUITypeEditor類我決定從內置的SystemDrawingDesignImageEditor類進行派生是因為它已經實現了圖像文件選擇功能也就是說ImageEditorEditValue實現將顯示一個文件打開對話框以允許用戶從文件系統中選擇一個圖像文件然後從我的派生類中調用這一功能只需要簡單地調用MyBaseEditValue即可
為了實現上面第一個要求(在屬性格子中顯示下拉箭頭按鈕)我必須重載GetEditStyle方法以從UITypeEditorEditStyle枚舉中返回適當的常數
Public Overloads Overrides Function GetEditStyle( _
ByVal context As ITypeDescriptorContext) As UITypeEditorEditStyle
Return UITypeEditorEditStyleDropDown
End Function
為了顯示圖像資源列表我必須列舉一個給定程序集中的所有資源並且僅在列表中顯示圖像資源為了簡化我決定使用一種簡單的約定當一個資源名以一個有效圖像文件擴展名(bmpjpggif)結束時我們就認為這是一種圖像資源並且把它包括到該下拉列表框中而且我使用圖像資源名的集合來填充這個下拉ListBox控件後面詳預以詳述
開始時被枚舉以查詢圖像資源的程序集就是包含ResourceImageEditor類的程序集然而我們可以通過把ResourceImageEditorResourceAssembly屬性設置為任何有效的SystemReflectionAssembly參考來改變它
當用戶從列表框中選擇一個圖像資源名時該圖像應該即可從給定的程序集中的manifest文件中加載這是在LoadResourceImage方法內實現的
Private Function LoadResourceImage(ByVal resourceName As String) As Image
DebugAssert(Not resourceName Is Nothing)
Dim ImageStream As SystemIOStream = MeResourceAssemblyGetManifestResourceStream(resourceName)
Return SystemDrawingBitmapFromStream(ImageStream)
End Function
下拉用戶接口是通過在重載的EditValue方法內動態地創建和填充一個ListBox控件實現的編輯器也處理由ListBox生成的Click和KeyDown事件因為這是攔截ENTER和ESC鍵所必需的下列偽碼顯示了在EditValue方法中的實現邏輯
Public Overloads Overrides Function EditValue()
存儲上下文信息以用於下拉ListBox事件處理器
創建並使用可用的圖像資源名填充該ListBox
添加我們的特殊Browse項
綁定ListBox事件
在一個下拉窗口中顯示該ListBox
End Function
三 幾個關鍵問題與解案
為了開發ResourceImageEditor我創建了一個重載Image屬性的MyPictureBox(派生自SystemWindowsFormsPictureBox)以便把ResourceImageEditor指定為該Image屬性的類型編輯器
然後我編譯這個控件的代碼之後就可以把該MyPictureBox控件放到一個表單上並且調用下拉框用戶接口
鼠標接口工作得很好然而當我使用鍵盤選擇一項然後按下回車鍵時該下拉列表框消失而且我的選定內容丟失了(也就是說前一個選擇圖像並沒有改變)我很快發現當按下回車鍵時該ListBox並沒有生成KeyDown事件
盡管ESC鍵也產生KeyDown事件但這不是一個問題因為該下拉列表框會被自動關閉而且我不必處理當前選擇項
很明顯在ListBox控件能夠處理它們之前這個屬性格屏蔽了ENTER和ESC鍵
為了簡化而且還要解決問題我要使用ProcessDialogKey方法在消息預處理期間(處理對話字符例如TABRETURNESCAPE和箭頭鍵)時調用這個方法這個方法是在SystemWindowsFormsControl類內聲明的—它簡單地把該調用代理給該控件的父級(如果有的話)我已經子類化該ListBox控件並且重載了ProcessDialogKey方法來攔截回車鍵如下所示
Protected Overrides Function ProcessDialogKey(ByVal keyData As Keys) As Boolean
If keyData = SystemWindowsFormsKeysReturn Then
RaiseEvent EnterPressed(Me EventArgsEmpty)
Return True True意味著我們已經處理了相應的鍵
Else
Return MyBaseProcessDialogKey(keyData)
End If
End Function
不是從ProcessDialogKey實現內部生成KeyDown事件我決定使用一種更為直接的方式EnterPressed事件為了我修改了ResourceImageEditorEditValue的實現以處理這一事件(而不是KeyDown事件)而且一切都非常順利
你可以使用這一技術來攔截任何Control派生的類(你使用它來實現你的類型編輯器中的下拉UI)中的ENTER鍵
From:http://tw.wingwit.com/Article/program/net/201311/11427.html