Windows用戶一般分為兩類:一類習慣用鍵盤操作,一類習慣用鼠標操作。程序員們在編程時一般會提供快捷鍵(在命令或字母中加下劃線)或快捷方式(用CTRL加字母的組合)來照顧到那些習慣鍵盤的用戶,但是那些鼠標用戶卻被忽略了。因為程序員們一般都習慣用鍵盤,所以他們這種重視鍵盤操作的特點是可以理解的,但是每個程序員也應該考慮到提供鼠標支持。
鼠標用戶所期望的一件事就是能夠在應用程序中實現拖放。如果你注意到一些大型應用程序或windows自身的話,拖放操作幾乎隨處可見。例如,用戶可能已經習慣了在windows資源管理器中拖放文件,或在word中拖放文本等操作了。
盡管拖放操作隨處可見,但是隻有極少數程序員在他們所編寫的程序中實現拖放功能,最可能的原因是他們認為實現拖放可能比想象的還要難。這篇文章列舉了如何在窗體內、窗體之間,甚至應用程序之間移動文本、圖片或者文件的例子,顯示了在中實現拖放是非常容易得。
拖放如何進行
拖放實際上就如同用鼠標複製粘帖,因此你必須有一個可以複製或移動的源,也要有一個可以粘貼的目的地。在這兩個操作過程中,數據是保存在內存中的。複製粘貼用的是剪切版,而拖放用的卻是一種本質上是私有剪切板的Dataobject的對象。
下麵是典型的托放操作的時間序列:
1、拖(dragging)是通過調用源控件的DoDragDrop方法來初始化的,DoDragDrop有兩個參數
data,指定將要傳送的數據
allowedEffects,指定允許進行的操作(複製或移動)
這樣自動創建了一個新的Dataobject對象
2、接下來就依次激發了GiveFeedBack事件。在大多數情況下,你並不需要擔心GiveFeedBack事件,然而你如果想自定義拖放過程中的鼠標指針的話,你可以在這些地方加上你的代碼。
3、任何有AllowDrop屬性而且被設置成True的控件都是隱含的Drop對象。AllowDrop屬性可以在設計時的屬性窗口中進行設置,也可以在Form_load事件自動加載。
4、當鼠標移到某個控件時,就同時激發了這個控件的DragEnter事件。GetDataPresent方法是用來確認拖過來的數據是否適合目標控件,Effect屬性是用來顯示適當的鼠標指針。
5、如果用戶在有效的目標控件上釋放鼠標,就同時激發了DragDrop事件。DragDrop事件句柄中的代碼從DataObject對象中釋放數據並把它顯示在目標控件中。
從VB6到VB.NET有何變化?
(略)
拖放文本
拖放操作的一個很簡單然而很有用的情形是從一個TextBox控件複製文本到另一個TextBox控件。當然你可以隻用鍵盤就能實現(CTRL + C and CTRL + V),然而拖放更簡單因為它僅需要鼠標的移動就可以完成。
1、向一個窗體中添加兩個文本框,並把第二個TextBox控件的AllowDrop屬性設置成True,添加如下代碼。
Private MouseIsDown As Boolean = False
Private Sub TextBox1_MouseDown(ByVal sender As Object, ByVal e As _
System.Windows.Forms.MouseEventArgs) Handles TextBox1.MouseDown
' Set a flag to show that the mouse is down.
MouseIsDown = True
End Sub
Private Sub TextBox1_MouseMove(ByVal sender As Object, ByVal e As _
System.Windows.Forms.MouseEventArgs) Handles TextBox1.MouseMove
If MouseIsDown Then
' Initiate dragging.
TextBox1.DoDragDrop(TextBox1.Text, DragDropEffects.Copy)
End If
MouseIsDown = False
End Sub
Private Sub TextBox2_DragEnter(ByVal sender As Object, ByVal e As _
System.Windows.Forms.DragEventArgs) Handles TextBox2.DragEnter
' Check the format of the data being dropped.
If (e.Data.GetDataPresent(DataFormats.Text)) Then
' Display the copy cursor.
e.Effect = DragDropEffects.Copy
Else
' Display the no-drop cursor.
e.Effect = DragDropEffects.None
End If
End Sub
Private Sub TextBox2_DragDrop(ByVal sender As Object, ByVal e As _
System.Windows.Forms.DragEventArgs) Handles TextBox2.DragDrop
' Paste the text.
TextBox2.Text = e.Data.GetData(DataFormats.
End Sub
在上麵的例子中,MouseDown事件是用來判斷鼠標是否按下的,MouseMove事件中用到了DoDragDrop方法。盡管你可以在MouseDown事件中來初始化Drag,然而這麼做會帶來出人意料之外的結果。在用戶點擊控件時,將顯示no-drag 指針。DoDragDrop方法有兩個參數
data,這個例子中代表的是第一個TextBox的Text屬性。
allowedEffects,這個例子中是隻允許複製。
在MouseMove事件中MouseIsDown標誌設置成了False,盡管在這個例子沒有必要,但是如果你有很多控件支持拖放時,你將會得到一個運行時例外。
在DragEnter事件中,GetDataPresent方法檢查正在拖動的數據格式,在本例中是文本,所以Effect屬性設置成複製,同時也顯示copy指針。
在DragDrop事件中,GetData方法用來從DataObject中獲得文本,並把它送給目標文本框。
拖動圖片
盡管拖放圖片並不像拖放文本那樣經常用到,然而它在許多應用程序中仍然是很有用的。實際上這兩者之間也沒有什麼不同,隻不過是數據類型發生了變化而已。
1、 在Form中添加兩個PictureBox控件。
2、 在代碼窗體中添加如下代碼
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As _
System.EventArgs) Handles MyBase.Load
' Enable dropping.
PictureBox2.AllowDrop = True
End Sub
Private Sub PictureBox1_MouseDown(ByVal sender As Object, ByVal e As _
System.Windows.Forms.MouseEventArgs) Handles PictureBox1.MouseDown
If Not PictureBox1.Image Is Nothing Then
' Set a flag to show that the mouse is down.
m_MouseIsDown = True
End If
End Sub
Private Sub PictureBox1_MouseMove(ByVal sender As Object, ByVal e As _
System.Windows.Forms.MouseEventArgs) Handles PictureBox1.MouseMove
If m_MouseIsDown Then
' Initiate dragging and allow either copy or move.
PictureBox1.DoDragDrop(PictureBox1.Image, DragDropEffects.Copy Or _
DragDropEffects.Move)
End If
m_MouseIsDown = False
End Sub
Private Sub PictureBox2_DragEnter(ByVal sender As Object, ByVal e As _
System.Windows.Forms.DragEventArgs) Handles PictureBox2.DragEnter
If e.Data.GetDataPresent(DataFormats.Bitmap) Then
' Check for the CTRL key.
If e.KeyState = 9 Then
e.Effect = DragDropEffects.Copy
Else
e.Effect = DragDropEffects.Move
End If
Else
e.Effect = DragDropEffects.None
End If
End Sub
Private Sub PictureBox2_DragDrop(ByVal sender As Object, ByVal e As _
System.Windows.Forms.DragEventArgs) Handles PictureBox2.DragDrop
' Assign the image to the PictureBox.
PictureBox2.Image = e.Data.GetData(DataFormats.Bitmap)
' If the CTRL key is not pressed, delete the source picture.
If Not e.KeyState = 8 Then
PictureBox1.Image = Nothing
End If
End Sub
注意到上麵的例子中第二個PictureBox控件的AllowDrop屬性是在Form1_load事件中設置的,這是因為設計時PictureBox並沒有AllowDrop屬性。
在MouseDown事件中,代碼首先檢測是否有要賦給PictureBox的圖片;如果沒有的話,當你移動圖片後,接下來的click將引發一個意外。
還應該注意到的是在DragEnter和DragDrop事件中代碼檢測CTRL鍵是否被按下,從而決定是否是複製還是移動圖片。為什麼值會不同呢?在DragEnter事件中,當鼠標左鍵按下時,產生的值是1,在加上CTRL的值8,從而值為9。見KeyState枚舉列表DragEventArgs.KeyState Property(?url=/library/en-us/cpref/html/frlrfsystemwindowsformsdrageventargsclasskeystatetopic.asp)。
到目前為止,這兩個例子處理的都是同一窗體不同控件間的拖放,然而在同一應用程序的不同窗體上同樣適用。
拖動文件
在windows中拖放通常是複製或移動文件,windows完全支持該功能,而且對許多用戶來說這也是操作文件的優選方式。除此之外,用戶已經習慣了把文件拖動到一個程序來打開文件的方式,像拖動一個doc文件到word來打開。
在這個例子中用從windows資源管理器拖來的文件來操作ListBox控件。
向窗體中添加一個ListBox控件,並設置其AllowDrop屬性為True,並添加如下代碼:
Private Sub ListBox1_DragEnter(ByVal sender As Object, ByVal e As _
System.Windows.Forms.DragEventArgs) Handles ListBox1.DragEnter
If e.Data.GetDataPresent(DataFormats.FileDrop) Then
e.Effect = DragDropEffects.All
End If
End Sub
Private Sub ListBox1_DragDrop(ByVal sender As Object, ByVal e As _
System.Windows.Forms.DragEventArgs) Handles ListBox1.DragDrop
If e.Data.GetDataPresent(DataFormats.FileDrop) Then
Dim MyFiles() As String
Dim i As Integer
' Assign the files to an array.
MyFiles = e.Data.GetData(DataFormats.FileDrop)
' Loop through the array and add the files to the list.
For i = 0 To MyFiles.Length - 1
ListBox1.Items.Add(MyFiles(i))
Next
End If
End Sub
你可能已經注意到了DragEnter事件中的Effect屬性被設置成DragDropEffects.All。因為文件本身並不是真的就被複製或移動了,因此源控件設置成哪個AllowedEffects並沒有關係,所以指定All對任何FileDrop都可以。
在上麵的例子中FileDrop格式包含了每個被拖動文件的全路徑。
下麵的例子講述了拖放的一個特殊情況:在兩個列表間來回拖放。
表間拖放
拖放的另一個情況是從一個列表移動項目到另一個列表。這種情況下拖放將變得更加簡單。
向窗體中添加兩個ListView控件,並把他們的AllowDrop、Multiselect、View屬性分別設置成True、True、List。並添加如下代碼:
Private Sub ListView_ItemDrag(ByVal sender As Object, ByVal e As _
System.Windows.Forms.ItemDragEventArgs) Handles ListView1.ItemDrag, _
ListView2.ItemDrag
Dim myItem As ListViewItem
Dim myItems(sender.SelectedItems.Count - 1) As ListViewItem
Dim i As Integer = 0
' Loop though the SelectedItems collection for the source.
For Each myItem In sender.SelectedItems
' Add the ListViewItem to the array of ListViewItems.
myItems(i) = myItem
i = i + 1
Next
' Create a DataObject containg the array of ListViewItems.
sender.DoDragDrop(New _
DataObject(System.Windows.Forms.ListViewItem(), myItems), _
DragDropEffects.Move)
End Sub
Private Sub ListView_DragEnter(ByVal sender As Object, ByVal e As _
System.Windows.Forms.DragEventArgs) Handles ListView1.DragEnter, _
ListView2.DragEnter
' Check for the custom DataFormat ListViewItem array.
If e.Data.GetDataPresent(System.Windows.Forms.ListViewItem()) Then
e.Effect = DragDropEffects.Move
Else
e.Effect = DragDropEffects.None
End If
End Sub
Private Sub ListView_DragDrop(ByVal sender As Object, ByVal e As _
System.Windows.Forms.DragEventArgs) Handles ListView1.DragDrop, _
ListView2.DragDrop
Dim myItem As ListViewItem
Dim myItems() As ListViewItem = _ e.Data.GetData(System.Windows.Forms.ListViewItem())
Dim i As Integer = 0
For Each myItem In myItems
' Add the item to the target list.
sender.Items.Add(myItems(i).Text)
' Remove the item from the source list.
If sender Is ListView1 Then
ListView2.Items.Remove(ListView2.SelectedItems.Item(0))
Else
ListView1.Items.Remove(ListView1.SelectedItems.Item(0))
End If
i = i + 1
Next
End Sub
你可能不明白為什麼這個例子中用的是ListView控件而不是ListBox控件,這個問題題的好,因為ListBox控件不支持多項拖放。
ListView和TreeView控件有個ItemDrag事件。上麵的例子中,一個ItemDrag事件句柄覆蓋了兩個控件,並在列在Handles從句。Sender參數表明哪個控件正在初始化Drag。
因為DataFormats類沒有ListViewItem類型成員,數據必須傳遞給一個係統類型。ItemDrag創建了一個ListViewItem類型的數組,並用一個循環來遍曆SelectedItem集合。在DoDragDrop方法中,創建了一個新的DataObject並用數組來來對它進行操作。可以用這種方法來拖放任何係統類型。
結論:
正像你從這些例子中所看到的一樣,為應用程序添加拖放操作並不是很難。當你理解了這些基本的技巧後,你就可以為你自己的程序添加拖放的代碼了