隨著案頭系統的推出,利用滑鼠的拖放(Drag and Drop)操作由於其簡單、直接,受到了越來越多的讀者的歡迎,為迎合這種趨勢,越來越多程式員在自己的程式中使用了拖放操作。拖放操作方便了程式的使用者,但由於拖放操作在程式中的設計工作比較還有點麻煩,甚至是一個痛點,許多程式員對其都有點心有餘悸。本文就結合微軟公司最新的.Net程式開發語言--C#,來全面介紹一下在C#中是如何處理拖放操作的。 在本文中,我們是通過二個代表組件,也是在拖放操作中經常使用到的二個組件--TreeView組件和ListView組件,之間互相進行拖放操作來說明此類問題的。在進行拖放操作之前,必須要對進行拖放操作的組件的"AllowDrop"屬性值設定為"True",因為此屬性是確定組件是否可以進行拖放操作的。 一. 本文中介紹的程式的設計和啟動並執行軟體環境: (1).微軟公司視窗2000伺服器版 (2)..Net FrameWork SDK Beta 2 二. 由TreeView組件到ListView組件的拖放操作: 要完成此次的拖放操作,必須處理好三種事件:"ItemDrag"、"DragEnter"、"DragDrop"。其中只有第一種事件是在源組件中觸發的,另外二種事件是在目標組件中觸發的。其中當使用者拖動組件觸發"ItemDrag"事件;當拖動資料進入目標組件地區觸發"DragEnter"事件;當使用者在目標組件地區放置拖動的資料觸發"DragDrop"事件。下面就根據拖放操作的操作順序來詳細介紹: (1).開始"拖"(Drag)操作: 通過"DoDragDrop"方法拉開了拖放操作的第一步。"DoDragDrop"方法的文法為: DoDragDrop ( object data , DragDropEffects allowedEffects ) ; 其中第二個參數來是說明此次拖放操作最後所要實現的效果,因為拖放操作有時實現的效果是把源組件中的內容"拖"到目標組件中,這種效果就是"Move";有時拖放的效果是在目標組件中加入拖動的資料,對源組件的內容是沒有什麼影響的,這種效果就是"Copy"。當然無論是"Move"還是"Copy",這都要通過具體的編程來實現,設定這些效果只是告訴作業系統,你進行拖放操作的類型,從而為拖放操作設定特定的表徵圖。此例中實現開始"拖放"操作的具體實現代碼如下: private void treeView1_ItemDrag ( object sender , ItemDragEventArgs e ) { string strItem = e.Item.ToString ( ) ; //開始進行"Drag"操作 DoDragDrop ( strItem , DragDropEffects.Copy | DragDropEffects.Move ) ; } 在上面代碼中,我們定義的拖放資料類型是字串,其實拖放的資料類型可以是很多種的,你可以通過修改"DoDragDrop"方法的第一個參數來設定你所要拖放資料類型,譬如:位元影像或者其他什麼。 (2).目標組件允許進行拖放操作: 既然你已經開始進行拖放操作,你還必須告訴你要拖放到的目標組件,要接受你所拖放的資料,"DragEnter"事件正好可以處理。在下列的代碼中,我們是通過判斷拖放資料類型來確定是否接受拖放,如果是字串,則可以,否則,則不行。具體代碼如下: private void listView1_DragEnter ( object sender , DragEventArgs e ) { //判斷是否目前拖動的資料是字串,如果是,則拖動符串對目的組件進行拷貝 if ( e.Data.GetDataPresent ( DataFormats.Text ) ) e.Effect = DragDropEffects.Move ; else e.Effect = DragDropEffects.None ; } (3).獲得拖放的字串,在目標組件中加入相應的內容: 此步的處理過程是十分明確的,要分成二步來進行,首先要得到拖放的字串,其次是在目標組件中加入以此字串為標題的項目。當然還要在相應的位置了。下面就是實現這二步操作的具體代碼: private void listView1_DragDrop ( object sender , DragEventArgs e ) { string dummy = "temp" ; //獲得進行"Drag"操作中拖動的字串 string s = ( string ) e.Data.GetData ( dummy.GetType ( ) ) ; s = s.Substring ( s.IndexOf ( ":" ) + 1 ).Trim ( ) ; Position.X = e.X ; Position.Y = e.Y ; Position = listView1.PointToClient ( Position ) ; //在目標組件中加入以此字串為標題的項目 listView1.Items.Add ( new ListViewItem ( s , 0 ) ) ; } 此致通過對這三個事件的編程,已經完成了由TreeView組件到ListView組件的拖放操作。 三. 由ListView組件到TreeView組件的拖放操作: 由ListView組件到TreeView組件的拖放操作和從TreeView組件到ListView組件相類似,也是通過"ItemDrag"、"DragEnter"、"DragDrop"三個事件來處理的,具體如下: (1).開始"拖"(Drag)操作: 這和前者沒有什麼實質上的區別,只是在此次的拖放操作開始之前,多加入了一些邏輯判斷,讓程式更穩健的允許,實現的代碼如下: private void listView1_ItemDrag ( object sender , ItemDragEventArgs e ) { //判斷是否是滑鼠右鍵按動 if ( e.Button == MouseButtons.Right ) return ; int nTotalSelected = listView1.SelectedIndices.Count ; //判斷組件中是否存在項目 if ( nTotalSelected <= 0 ) return ; IEnumerator selCol = listView1.SelectedItems.GetEnumerator ( ) ; selCol.MoveNext ( ) ; ListViewItem lvi = ( ListViewItem )selCol.Current ; string mDir = "" ; for ( int i = 0 ; i < lvi.SubItems.Count ; i++ ) mDir += lvi.SubItems[ i ].Text + " ," ; string str = mDir.Substring ( 0 , mDir.Length-1 ) ; if ( str == "" ) return ; //對組件中的字串開始拖放操作 listView1.DoDragDrop ( str , DragDropEffects.Copy | DragDropEffects.Move ) ; } (2).目標組件允許進行拖放操作: 這一步是進行拖放操作最為一致的,除非你所要進行拖放的資料類型有改變,否則,沒有必要對原始碼進行什麼修改,具體如下: private void treeView1_DragEnter ( object sender , DragEventArgs e ) { //判斷是否目前拖動的資料是字串,如果是,則拖動符串對目的組件進行拷貝 if ( e.Data.GetDataPresent ( DataFormats.Text ) ) e.Effect = DragDropEffects.Copy ; else e.Effect = DragDropEffects.None ; } (3).獲得拖放的字串,在目標組件中加入相應的內容: 對於進行拖放操作的不同組件,獲得其拖放的資料的實現方法是不一樣的,在本步驟中也不例外,但總歸大同小異,掌握程式設計的步驟和要點,加上探索、研究的精神,這個問題應該能夠解決,下面是實現此步驟的程式碼: private void treeView1_DragDrop ( object sender , DragEventArgs e ) { //獲得進行"Drag"操作中拖動的字串 string dummy = "temp" ; string s = ( string ) e.Data.GetData ( dummy.GetType ( ) ) ; s = s.Substring ( s.IndexOf ( ":" ) + 1 ).Trim ( ) ; Position.X = e.X ; Position.Y = e.Y ; Position = treeView1.PointToClient ( Position ) ; TreeNode DropNode = this.treeView1.GetNodeAt ( Position ) ; //在目標組件中加入以此字串為標題的項目 if ( DropNode != null ) { TreeNode DragNode = new TreeNode ( s ) ; treeView1.Nodes.Insert ( DropNode.Index+1 , DragNode ) ; } } 四. 二個組件進行拖放操作的完整來源程式代碼(dragdrop.cs): 在掌握了上面的這些步驟過以後,可以得到這二個組件相互進行拖放操作的完整代碼和編譯後程式的運行介面,如下: 圖01:二個組件相互進行拖放的程式運行介面 dragdrop.cs的代碼如下: using System ; using System.Drawing ; using System.Collections ; using System.ComponentModel ; using System.Windows.Forms ; using System.Data ; //匯入程式中使用的命名空間 public class Form1 : Form { private TreeView treeView1 ; private Point Position = new Point( 0 , 0) ; // bool lv1_mdown = false ; private ListView listView1 ; private System.ComponentModel.Container components = null ; public Form1 ( ) { InitializeComponent ( ) ; //初始化表單中的各個組件 } //清除程式中使用到的各種資源 protected override void Dispose ( bool disposing ) { if ( disposing ) { if ( components != null ) { components.Dispose ( ) ; } } base.Dispose ( disposing ) ; } private void InitializeComponent ( ) { ListViewItem listViewItem1 = new ListViewItem ( "Item01" ) ; ListViewItem listViewItem2 = new ListViewItem ( "Item02" ) ; treeView1 = new TreeView ( ) ; listView1 = new ListView ( ) ; SuspendLayout ( ) ; //此屬性必須設定為"真",這樣才能進行拖放操作 treeView1.AllowDrop = true ; treeView1.ImageIndex = -1 ; treeView1.Location = new Point ( 48 , 40 ) ; treeView1.Name = "treeView1" ; //在TreeView組件中加入初始化的節點 treeView1.Nodes.Add ( new TreeNode ( "節點01" ) ); treeView1.Nodes.Add ( new TreeNode ( "節點02" ) ); treeView1.SelectedImageIndex = -1 ; treeView1.Size = new Size ( 121 , 144 ) ; treeView1.TabIndex = 0 ; treeView1.DragEnter += new DragEventHandler ( treeView1_DragEnter ) ; treeView1.ItemDrag += new ItemDragEventHandler ( treeView1_ItemDrag ) ; treeView1.DragDrop += new DragEventHandler ( treeView1_DragDrop ) ; //此屬性必須設定為"真",這樣才能進行拖放操作 listView1.AllowDrop = true ; //在ListView組件中加入項目 listView1.Items.Add ( listViewItem1 ) ; listView1.Items.Add ( listViewItem2 ) ; listView1.Location = new Point ( 208 , 40 ) ; listView1.Name = "listView1" ; listView1.Size = new Size ( 121 , 144 ) ; listView1.TabIndex = 2 ; listView1.View = View.List ; listView1.DragDrop += new DragEventHandler ( listView1_DragDrop ) ; listView1.DragEnter += new DragEventHandler ( listView1_DragEnter ) ; listView1.ItemDrag += new ItemDragEventHandler ( listView1_ItemDrag ) ; AutoScaleBaseSize = new Size ( 6 , 14 ) ; ClientSize = new Size ( 376 , 237 ) ; Controls.Add ( listView1 ); Controls.Add ( treeView1 ); this.MaximizeBox = false ; this.MinimizeBox = false ; this.Name = "Form1" ; this.Text = "全面掌握C#中的拖放操作" ; this.ResumeLayout ( false ) ; } static void Main ( ) { Application.Run ( new Form1 ( ) ) ; } private void treeView1_ItemDrag ( object sender , ItemDragEventArgs e ) { string strItem = e.Item.ToString ( ) ; //開始進行"Drag"操作 DoDragDrop ( strItem , DragDropEffects.Copy | DragDropEffects.Move ) ; } private void listView1_DragEnter ( object sender , DragEventArgs e ) { //判斷是否目前拖動的資料是字串,如果是,則拖動符串對目的組件進行拷貝 if ( e.Data.GetDataPresent ( DataFormats.Text ) ) e.Effect = DragDropEffects.Move ; else e.Effect = DragDropEffects.None ; } private void listView1_DragDrop ( object sender , DragEventArgs e ) { string dummy = "temp" ; //獲得進行"Drag"操作中拖動的字串 string s = ( string ) e.Data.GetData ( dummy.GetType ( ) ) ; s = s.Substring ( s.IndexOf ( ":" ) + 1 ).Trim ( ) ; Position.X = e.X ; Position.Y = e.Y ; Position = listView1.PointToClient ( Position ) ; //在目標組件中加入以此字串為標題的項目 listView1.Items.Add ( new ListViewItem ( s , 0 ) ) ; } private void listView1_ItemDrag ( object sender , ItemDragEventArgs e ) { //判斷是否是滑鼠右鍵按動 if ( e.Button == MouseButtons.Right ) return ; int nTotalSelected = listView1.SelectedIndices.Count ; //判斷組件中是否存在項目 if ( nTotalSelected <= 0 ) return ; IEnumerator selCol = listView1.SelectedItems.GetEnumerator ( ) ; selCol.MoveNext ( ) ; ListViewItem lvi = ( ListViewItem )selCol.Current ; string mDir = "" ; for ( int i = 0 ; i < lvi.SubItems.Count ; i++ ) mDir += lvi.SubItems[ i ].Text + " ," ; string str = mDir.Substring ( 0 , mDir.Length-1 ) ; if ( str == "" ) return ; //對組件中的字串開始拖放操作 listView1.DoDragDrop ( str , DragDropEffects.Copy | DragDropEffects.Move ) ; } private void treeView1_DragEnter ( object sender , DragEventArgs e ) { //判斷是否目前拖動的資料是字串,如果是,則拖動符串對目的組件進行拷貝 if ( e.Data.GetDataPresent ( DataFormats.Text ) ) e.Effect = DragDropEffects.Copy ; else e.Effect = DragDropEffects.None ; } private void treeView1_DragDrop ( object sender , DragEventArgs e ) { //獲得進行"Drag"操作中拖動的字串 string dummy = "temp" ; string s = ( string ) e.Data.GetData ( dummy.GetType ( ) ) ; s = s.Substring ( s.IndexOf ( ":" ) + 1 ).Trim ( ) ; Position.X = e.X ; Position.Y = e.Y ; Position = treeView1.PointToClient ( Position ) ; TreeNode DropNode = this.treeView1.GetNodeAt ( Position ) ; //在目標組件中加入以此字串為標題的項目 if ( DropNode != null ) { TreeNode DragNode = new TreeNode ( s ) ; treeView1.Nodes.Insert ( DropNode.Index+1 , DragNode ) ; } } } 五. 其他組件的拖放操作: 本文雖然對TreeView組件和ListView組件之間的拖放操作進行了詳細的介紹,對於其他的可以用於拖放操作的組件,很多組件的拖放操作的實現方法都和這二種差不多。但也有一些組件有一些區別,譬如:ListBox組件等,在進行拖放操作的時候,他就沒有本文介紹的"ItemDrag"事件,那這怎麼辦。我們是通過一個變通的方法來實現的。具體是通過"MouseMove"事件和"MouseDown"事件來代替"ItemDrag"事件,其中"MouseMove"事件主要是起到觸發拖放操作的作用,"MouseDown"事件主要是起著判斷此次拖放操作是否已經完成的作用。對於ListBox組件拖放操作的其他步驟也和上面介紹的二個組件沒有什麼太大區別。由於篇幅的關係ListBox組件和其他不存在"ItemDrag"事件的組件的拖放操作,這裡就不一一介紹了,相信大家能夠搞定。 六. 總結: 對於大多數組件來說掌握了"ItemDrag"、"DragEnter"、"DragDrop"三個事件的解決辦法也就掌握了組件間的拖放操作。當然還有一些例外的組件,但總而言之,拖放操作的實現步驟都是一樣的,解決的思路也是大致一致的。由於拖放操作的自身的優點,對於程式員來說儘快掌握是十分必要的,希望本文介紹的內容能夠令你滿意。 |