以前做过一些winform的项目,实现标题描述的效果通常是在鼠标拖拽控件的时候,通常是在拖动过程中生成一个透明的form,把控件放进去,form的位置跟随鼠标一起改变。前几天和几位同事讨论winform的一些心得,正好又谈到这个,有同事提供了一种比较新颖的思路:就是生成一张透明的图片,设置成光标的形状。正好周末有时间试了一下。
class="code_img_closed" src="/Upload/Images/2013072120/0015B68B3C38AA5B.gif" alt="" />logs_code_hide('7fa70255-84ff-4024-9f50-783ed0258304',event)" src="/Upload/Images/2013072120/2B1B950FA3DF188F.gif" alt="" />1 using System.Collections.Generic; 2 using System.Drawing; 3 using System.Windows.Forms; 4 5 delegate void DropCallBack<in Source, in Target>(Source source, Target target) 6 where Source : Control 7 where Target : Control; 8 9 interface IDragDropController<in Source, in Target> 10 where Source : Control 11 where Target : Control 12 { 13 void EnableDragDrop(); 14 void DisableDragDrop(); 15 } 16 17 sealed class DragDropController<Source, Target> : IDragDropController<Source, Target> 18 where Source : Control 19 where Target : Control 20 { 21 private IEnumerable<Source> sources; 22 private IEnumerable<Target> targets; 23 private bool isMovePrepared; 24 private Bitmap cursorImage; 25 private Cursor cursor; 26 private Cursor defaultCursor; 27 private DropCallBack<Source, Target> dropCallBack; 28 29 public DragDropController(IEnumerable<Source> sources, IEnumerable<Target> targets, DropCallBack<Source, Target> dropCallBack) 30 { 31 this.sources = sources; 32 this.targets = targets; 33 this.dropCallBack = dropCallBack; 34 } 35 36 public void EnableDragDrop() 37 { 38 this.DisableDragDrop(); 39 foreach (var source in this.sources) 40 { 41 source.MouseDown += this.OnSourceMouseDown; 42 source.MouseMove += this.OnSourceMouseMove; 43 source.MouseUp += this.OnSourceMouseUp; 44 } 45 } 46 47 public void DisableDragDrop() 48 { 49 foreach (var source in this.sources) 50 { 51 source.MouseDown -= this.OnSourceMouseDown; 52 source.MouseMove -= this.OnSourceMouseMove; 53 source.MouseUp -= this.OnSourceMouseUp; 54 } 55 } 56 57 private void OnSourceMouseDown(object sender, MouseEventArgs e) 58 { 59 if (e.Button == MouseButtons.Left) 60 { 61 var source = sender as Source; 62 if (source != null) 63 { 64 source.Capture = true; 65 this.isMovePrepared = true; 66 } 67 } 68 } 69 70 private void OnSourceMouseMove(object sender, MouseEventArgs e) 71 { 72 if (this.isMovePrepared) 73 { 74 var source = sender as Source; 75 if (source == null) 76 { 77 return; 78 } 79 80 if (this.cursor == null) 81 { 82 this.defaultCursor = source.Cursor; 83 // Generate an image for cursor 84 int cursorWidth = source.Width >= 2 * e.X ? 2 * (source.Width - e.X) : 2 * e.X; 85 int cursorHeight = source.Height >= 2 * e.Y ? 2 * (source.Height - e.Y) : 2 * e.Y; 86 this.cursorImage = new Bitmap(cursorWidth, cursorHeight); 87 Rectangle bounds = new Rectangle(source.Width >= 2 * e.X ? cursorWidth - source.Width : 0, source.Height >= 2 * e.Y ? cursorHeight - source.Height : 0, source.Width, source.Height); 88 source.DrawToBitmap(cursorImage, bounds); 89 this.defaultCursor.Draw(Graphics.FromImage(this.cursorImage), new Rectangle(bounds.X + e.X, bounds.Y + e.Y, this.defaultCursor.Size.Width, this.defaultCursor.Size.Height)); 90 this.ChangeAlpha(cursorImage, bounds); 91 this.cursor = new Cursor(this.cursorImage.GetHicon()); 92 source.Cursor = this.cursor; 93 } 94 } 95 } 96 97 private void OnSourceMouseUp(object sender, MouseEventArgs e) 98 { 99 if (this.isMovePrepared) 100 { 101 var source = sender as Source; 102 if (source == null) 103 { 104 return; 105 } 106 107 if (this.cursor != null) 108 { 109 source.Cursor = defaultCursor; 110 this.cursorImage.Dispose(); 111 this.cursorImage = null; 112 this.cursor.Dispose(); 113 this.cursor = null; 114 } 115 116 this.isMovePrepared = false; 117 source.Capture = false; 118 // find the target control where the source control is dropped by the mouse up location. 119 Target target = null; 120 var mouseLocation = source.PointToScreen(e.Location); 121 foreach (var item in this.targets) 122 { 123 Point offset = item.PointToScreen(new Point(0, 0)); 124 Rectangle bounds = new Rectangle(offset, item.Size); 125 if (bounds.Contains(mouseLocation)) 126 { 127 target = item; 128 break; 129 } 130 } 131 132 if (target != null && this.dropCallBack != null) 133 { 134 this.dropCallBack(source, target); 135 } 136 } 137 } 138 139 private void ChangeAlpha(Bitmap img, Rectangle bounds) 140 { 141 for (int x = bounds.Left; x < bounds.Right; x++) 142 { 143 for (int y = bounds.Top; y < bounds.Bottom; y++) 144 { 145 Color orgColor = img.GetPixel(x, y); 146 img.SetPixel(x, y, Color.FromArgb(150, orgColor.R, orgColor.G, orgColor.B)); 147 } 148 } 149 } 150 }DragDropController
这个类可以实现将拖拽source到targets中任意一个控件过程中,鼠标跟随一张透明的source控件的图片。下面是使用该类的方法:
1 public partial class Form1 : Form 2 { 3 private DragDropController<PictureBox, PictureBox> dragDropTest; 4 5 public Form1() 6 { 7 InitializeComponent(); 8 this.dragDropTest = new DragDropController<PictureBox, PictureBox>(new List<PictureBox>() { this.pictureBox1, this.pictureBox2 }, new List<PictureBox>() { this.pictureBox3, this.pictureBox4 }, this.DropCallBack); 9 this.dragDropTest.EnableDragDrop(); 10 } 11 12 private void DropCallBack(PictureBox source, PictureBox target) 13 { 14 target.Image = source.Image; 15 } 16 }Demo
该方法实现从pictureBox1拖拽图片到pictureBox2或pictureBox3中的效果。
主要发现一个问题:在开始拖拽的时候,Cursor图片的生成比较复杂,性能似乎比较低,不知是否有优化之法。