其實(shí)鼠標(biāo)用戶所最期盼的就是對拖放操作的充分支持。仔細(xì)端詳大多數(shù)的Windows應(yīng)用軟件或Windows操作系統(tǒng)本身,我們會發(fā)現(xiàn)拖放能力是無處不在的。舉例來說,用戶早已非常習(xí)慣在Windows資源管理器中拖曳和置放文件,并且在Microsoft Word中拖曳和置放文字。
令人遺憾的是,只有極少數(shù)的VisualC#程序設(shè)計(jì)師會在他們所開發(fā)的應(yīng)用程序中提供完善的拖放功能,當(dāng)然,造成此現(xiàn)象的原因之一,就是要實(shí)現(xiàn)拖放功能確實(shí)有其困難度與復(fù)雜度。本節(jié)將讓您知道要利用Visual C#2003~2005以后的版本來實(shí)拖現(xiàn)放功能是多么簡單的一件事情。我們將實(shí)際展現(xiàn)如何在窗體內(nèi)、在窗體之間,以及在應(yīng)用程序之間移動和復(fù)制文字、圖片以及文件。
拖放操作是如何運(yùn)作的
拖放操作其實(shí)與剪切與粘貼(或復(fù)制與粘貼)沒有什么不同,只不過它是使用鼠標(biāo)而不是使用鍵盤。在這兩類操作中,您都會擁有一個來源(也就是您剪切或復(fù)制的對象)以及一個目標(biāo)(也就是您所粘貼之處)。不論是哪一種操作,在操作期間,都會在內(nèi)存中存在數(shù)據(jù)的一份副本。剪切與粘貼會使用到剪貼板,而拖放則會使用到一個DataObject對象,其實(shí)DataObject對象就好比是一個私有剪貼板。
在一個典型的拖放操作中,將會依序引發(fā)下列事件:
1.您可以通過調(diào)用源控件的DoDragDrop方法來初始化拖曳操作。DoDragDrop方法的語法如下所示:
DragDropEffects DoDragDrop(
Object data,
DragDropEffects allowedEffects)DoDragDrop方法會接受下列兩個參數(shù):
- data參數(shù)用來指定所要拖曳(傳遞)的數(shù)據(jù)。
- allowedEffects參數(shù)用來指定哪些操作(“復(fù)制”和/或“移動”)是被允許的。
2.接下來會引發(fā)源控件的GiveFeedback事件。在大多數(shù)的情況下,您并不需要去處理GiveFeedback事件,但是如果您想在拖曳期間顯示一個自定義的鼠標(biāo)指針,則可以在GiveFeedback事件處理函數(shù)中編寫程序代碼來完成此項(xiàng)設(shè)定。
3.AllowDrop屬性被設(shè)定成True的任何控件都可以是置放目標(biāo)。您可以在設(shè)計(jì)階段在“屬性”窗口中將要作為目標(biāo)控件的AllowDrop屬性設(shè)定成True,或者是于運(yùn)行階段在窗體的Load事件處理函數(shù)中將要作為目標(biāo)控件的AllowDrop屬性設(shè)定成True。
4.當(dāng)您將鼠標(biāo)指針移至任何一個控件的上方時,便會引發(fā)該控件的DragEnter事件。我們通常會在目標(biāo)控件的DragEnter事件處理函數(shù)中,使用GetDataPresent方法去檢測所拖曳的數(shù)據(jù)格式是否適用于目標(biāo)控件,并使用DragEventArgs類型參數(shù)的Effect屬性來設(shè)定所允許的置放操作。
5.如果用戶在一個有效的置放目標(biāo)上放開鼠標(biāo)按鍵,將會引發(fā)目標(biāo)控件的DragDrop事件。我們通常會在目標(biāo)控件的DragDrop事件處理函數(shù)中編寫程序代碼,從DataObject對象擷取數(shù)據(jù)并將其顯示于目標(biāo)控件中。
關(guān)于拖放操作,您還必須注意下列事項(xiàng):
- 某些控件具有自定義的特定拖曳事件。例如,ListView與TreeView控件就擁有ItemDrag事件。
- 當(dāng)一項(xiàng)拖曳操作正在執(zhí)行的時候,您可以處理QueryContinueDrag事件,該事件會向系統(tǒng)“要求使用權(quán)限”來繼續(xù)執(zhí)行拖曳操作。當(dāng)以該方法處理的時候,也是一種對調(diào)用那些對拖曳操作有影響的方法非常恰當(dāng)?shù)臅r機(jī)。比方說,當(dāng)鼠標(biāo)指針停留在TreeView控件上方的時候展開一個TreeNode。
- 您也可以定義您自己的DataFormats。做法非常簡單,您只需將您的對象指定為SetData方法中的Object參數(shù),同時請確定所指定的對象是可序列化的。
- 除此之外,您還可以使用KeyState屬性,以便根據(jù)拖放操作期間所按下的按鍵來產(chǎn)生特定效果。舉例來說,當(dāng)Ctrl鍵被按下時所拖曳的數(shù)據(jù)通常要進(jìn)行復(fù)制。
程序范例CH8_DemoForm011.cs示范如何在兩個TextBox控件間拖曳文字,其功能特性如下所示:圖8.10示范如何拖曳文字
- 如圖8.10所示,由于右側(cè)上方TextBox控件的AllowDrop屬性被設(shè)定成False,因此您無法從左側(cè)的TextBox控件中將文字拖放其中。
- 如圖8.11所示,由于右側(cè)下方之TextBox控件的AllowDrop屬性被設(shè)定成True,因此您可以使用拖放方式將左側(cè)TextBox控件中的文字移動至右側(cè)下方的TextBox控件中。
- 值得一提的是,如果您持續(xù)按Ctrl鍵,則可以使用拖放方式將左側(cè)TextBox控件的文字復(fù)制到右側(cè)下方的TextBox控件中(如圖8.12所示)。
圖8.11通過拖放操作來移動文字
圖8.12通過拖放操作來復(fù)制文字
程序范例CH8_DemoForm011.cs在拖放操作方面的程序代碼如下所示:// 聲明一個常量以便調(diào)試在拖曳期間Ctrl鍵是否被按下。
constbyte CtrlMask =8;
// 替左側(cè)的 TextBox 控件處理 MouseDown 事件。
// 當(dāng)用戶在此控件的范圍內(nèi)按下鼠標(biāo)按鍵時便會引發(fā)此事件。
privatevoid txtLeft_MouseDown(object sender, MouseEventArgs e)
...{
// 如果用戶按下鼠標(biāo)左鍵。
if (e.Button == System.Windows.Forms.MouseButtons.Left)
...{
// 選取文本框中所有的文字。
txtLeft.SelectAll();
// 初始化拖放操作。
txtLeft.DoDragDrop(
txtLeft.SelectedText,
DragDropEffects.Move | DragDropEffects.Copy);
}
}
// 處理右側(cè)下方 TextBox 控件的 DragEnter 事件。
// 當(dāng)一個對象被拖曳至目標(biāo)控件的范圍內(nèi)時,就會引發(fā)
// 目標(biāo)控件的 DragEnter 事件。
privatevoid txtLowerRight_DragEnter(object sender, DragEventArgs e)
...{
// 檢查被拖曳的數(shù)據(jù)的類型是否適用于目標(biāo)控件。如果不適用,則拒絕置放。
if (e.Data.GetDataPresent(DataFormats.Text))
...{
// 如果在拖曳期間按著 Ctrl 鍵,則執(zhí)行復(fù)制操作;反之,則執(zhí)行移動操作。
if ((e.KeyState & CtrlMask) == CtrlMask)
...{
e.Effect = DragDropEffects.Copy;
}
else
...{
e.Effect = DragDropEffects.Move;
}
}
else
...{
e.Effect = DragDropEffects.None;
}
}
// 處理右側(cè)下方 TextBox 控件的 DragDrop 事件。
// 當(dāng)用戶放開鼠標(biāo)按鍵時就會引發(fā)此事件,并終止拖放操作。
privatevoid txtLowerRight_DragDrop(object sender, DragEventArgs e)
...{
txtLowerRight.Text = e.Data.GetData(
DataFormats.Text).ToString();
// 如果 Ctrl 鍵沒有被按下,移除源文字以便營造出移動文字的效果。
if ((e.KeyState & CtrlMask) != CtrlMask)
...{
txtLeft.Text ="";
}
}
從以上的程序代碼可以看出,我們會在拖放源(也就是左側(cè)的TextBox控件)的MouseDown事件處理函數(shù)中判斷鼠標(biāo)按鍵已經(jīng)被按下,而且如果用戶是按下鼠標(biāo)左鍵的話,便會調(diào)用DoDragDrop 方法并傳遞下列兩個參數(shù)給它以便初始化拖曳操作:
- 我們使用TextBox控件中被選取的文字作為第一個參數(shù)(即data參數(shù))的值,也就是TextBox控件中的文字將成為被拖曳的數(shù)據(jù)。
- 我們將第二個參數(shù)(也就是allowedEffects參數(shù))設(shè)定成DragDropEffects.Move Or DragDropEffects.Copy,以便允許用戶移動或復(fù)制。
1.先使用GetDataPresent方法來檢查被拖曳的數(shù)據(jù)是否為純文字(DataFormats.Text)。如果不是純文字的話,便將Effect屬性設(shè)定成DragDropEffects.None表示置放目標(biāo)不接受數(shù)據(jù);如果是純文字的話,則繼續(xù)進(jìn)行后續(xù)處理。
2.檢查Ctrl鍵是否被按下。如果Ctrl鍵被按下的話,便將Effect屬性設(shè)定成DragDropEffects.Copy,表示復(fù)制數(shù)據(jù)到置放目標(biāo)中,此時鼠標(biāo)指針將會顯示成復(fù)制指針圖標(biāo);如果Ctrl鍵沒有被按下的話,便將Effect屬性設(shè)定成DragDropEffects.Move,表示移動數(shù)據(jù)到置放目標(biāo)中。
我們會于置放目標(biāo)(也就是右側(cè)下方的TextBox控件)的DragDrop事件處理函數(shù)中執(zhí)行下列處理:
1.使用GetData方法從DataObject對象中提取被拖曳的文字并將它賦給置放目標(biāo)。
2.判斷Ctrl鍵是否被按下。如果Ctrl鍵沒有被按下,表示要執(zhí)行移動操作,此時會移除來源文字以便營造出移動文字的效果。