DoDragDrop和MouseUp
有没有一种简单的方法来确保在拖放操作完成后,MouseUp事件不会被框架占用并忽略?
我已经找到了描述一种机制的博客文章,但是它涉及大量的手动记账,包括状态标志,MouseMove事件,手动"鼠标离开"检查等。如果可以的话,我宁愿不必执行所有这些操作避免。
解决方案
回答
我最近想将拖放功能添加到我的项目中,但并没有遇到这个问题,但是我很感兴趣,真的很想看看我是否能找到一种比我们所链接页面中描述的方法更好的方法到。我希望我清楚地了解了我们想做的所有事情,总的来说,我认为我以一种更加优雅和简单的方式成功地解决了该问题。
简单来说,如果我们提供一些代码,对于这样的问题,那将是很好的,这样我们就可以准确地看到我们要执行的操作。我之所以这样说,是因为我在解决方案中假设了一些有关代码的信息,因此希望它已经很接近了。
这是代码,我将在下面解释:
this.LabelDrag.QueryContinueDrag += new System.Windows.Forms.QueryContinueDragEventHandler(this.LabelDrag_QueryContinueDrag); this.LabelDrag.MouseDown += new System.Windows.Forms.MouseEventHandler(this.LabelDrag_MouseDown); this.LabelDrag.MouseUp += new System.Windows.Forms.MouseEventHandler(this.LabelDrag_MouseUp); this.LabelDrop.DragDrop += new System.Windows.Forms.DragEventHandler(this.LabelDrop_DragDrop); this.LabelDrop.DragEnter += new System.Windows.Forms.DragEventHandler(this.LabelMain_DragEnter); public partial class Form1 : Form { public Form1() { InitializeComponent(); } private void LabelDrop_DragDrop(object sender, DragEventArgs e) { LabelDrop.Text = e.Data.GetData(DataFormats.Text).ToString(); } private void LabelMain_DragEnter(object sender, DragEventArgs e) { if (e.Data.GetDataPresent(DataFormats.Text)) e.Effect = DragDropEffects.Copy; else e.Effect = DragDropEffects.None; } private void LabelDrag_MouseDown(object sender, MouseEventArgs e) { //EXTREMELY IMPORTANT - MUST CALL LabelDrag's DoDragDrop method!! //Calling the Form's DoDragDrop WILL NOT allow QueryContinueDrag to fire! ((Label)sender).DoDragDrop(TextMain.Text, DragDropEffects.Copy); } private void LabelDrag_MouseUp(object sender, MouseEventArgs e) { LabelDrop.Text = "LabelDrag_MouseUp"; } private void LabelDrag_QueryContinueDrag(object sender, QueryContinueDragEventArgs e) { //Get rect of LabelDrop Rectangle rect = new Rectangle(LabelDrop.Location, new Size(LabelDrop.Width, LabelDrop.Height)); //If the left mouse button is up and the mouse is not currently over LabelDrop if (Control.MouseButtons != MouseButtons.Left && !rect.Contains(PointToClient(Control.MousePosition))) { //Cancel the DragDrop Action e.Action = DragAction.Cancel; //Manually fire the MouseUp event LabelDrag_MouseUp(sender, new MouseEventArgs(Control.MouseButtons, 0, Control.MousePosition.X, Control.MousePosition.Y, 0)); } } }
我省略了大多数设计器代码,但是包括了事件处理程序链接代码,因此我们可以确定什么链接到什么。在我的示例中,拖放发生在标签LabelDrag和LabelDrop之间。
我的解决方案的主要部分是使用QueryContinueDrag事件。在该控件上调用DoDragDrop后键盘或者鼠标状态发生更改时,将触发此事件。我们可能已经在执行此操作,但是非常重要的一点是,调用控件的DoDragDrop方法作为源,而不是与表单关联的方法。否则QueryContinueDrag将不会触发!
要注意的一件事是,当我们在放置控件上释放鼠标时,QueryContinueDrag实际上会触发,因此我们需要确保允许这样做。通过检查Mouse位置(使用全局Control.MousePosition属性进行检索)是否在LabelDrop控件矩形内部来进行处理。我们还必须确保将MousePosition转换为相对于客户端窗口的点,并以PointToClient作为控件。MousePosition返回屏幕的相对位置。
因此,通过检查鼠标不在放置控件上并且鼠标按钮现在处于向上状态,我们有效地捕获了LabelDrag控件的MouseUp事件! :)现在,我们可以执行此处要执行的任何处理,但是,如果我们已经拥有在MouseUp事件处理程序中使用的代码,则效率不高。因此,只需从此处调用MouseUp事件,并向其传递必要的参数,MouseUp处理程序将永远不会知道两者之间的区别。
不过,请注意,在我的示例中从MouseDown事件处理程序中调用DoDragDrop时,此代码实际上永远不会触发直接的MouseUp事件。我只是将代码放在那里,以表明有可能做到这一点。
希望对我们有所帮助!