C# 實現(xiàn)拖拉控件改變位置與大小的方法
前言:
很多時候我們需要在運行時,動態(tài)地改變控件的位置以及大小,以獲得更好的布局。比如說實際項目中的可自定義的報表、可自定義的單據(jù)等諸如此類。它們有個特點就是允許客戶或者二次開發(fā)人員設(shè)計它們需要的界面設(shè)置功能。
本人以前也做過可自定義系統(tǒng),包括界面和功能,主要為了減少開發(fā)人員的工作量以及程序的靈活性和健壯性。
本篇主要討論下,在運行時如何實現(xiàn)拖拉控件,達到改變控件位置與大小。功能將模擬VS設(shè)計界面時的拖拉功能。
(本篇暫不涉及多控件同時操作)
一、技術(shù)概述
其實實現(xiàn)運行時控件的拖拉并不難,主要是改變控件的Location與Size即可。動態(tài)調(diào)整時再捕獲MouseDown、MouseMove及MouseUp事件來實時修改上述兩個屬性就可以實現(xiàn)。
二、功能規(guī)劃
在此之前,我們先來看下.net設(shè)計界面,一旦選中某個控件時,將會出現(xiàn)如下圖的邊框:
之后就可以通過拖拉出現(xiàn)的邊框改變其大小。而改變控件的位置,實際上是當鼠標點擊在控件內(nèi)部拖動時實現(xiàn)的。
所有本例也將功能分為兩個部分實現(xiàn),分別為控件內(nèi)部拖動改變位置與控件邊框拖拉改變大小。
三、具體實現(xiàn)
1.拖動控件改變位置
首先,新建一個項目,然后添加一個類,取名叫MoveControl,該類用來給控件掛載事件實現(xiàn)拖動。
接著在該類中添加字段currentControl,用來保存需要操作的控件,即通過構(gòu)造函數(shù)傳遞的控件。
接著創(chuàng)建一方法--AddEvents,用來給當前的控件掛載事件。
代碼如下:
DragControl
using System; using System.Collections.Generic; using System.Text; using System.Windows.Forms; using System.Drawing; namespace DragControl { public class MoveControl { #region Constructors public MoveControl(Control ctrl) { currentControl = ctrl; AddEvents(); } #endregion #region Fields private Control currentControl; //傳入的控件 #endregion #region Properties #endregion #region Methods /// <summary> /// 掛載事件 /// </summary> private void AddEvents() { currentControl.MouseClick += new MouseEventHandler(MouseClick); currentControl.MouseDown += new MouseEventHandler(MouseDown); currentControl.MouseMove += new MouseEventHandler(MouseMove); currentControl.MouseUp += new MouseEventHandler(MouseUp); } #endregion #region Events /// <summary> /// 鼠標單擊事件:用來顯示邊框 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> void MouseClick(object sender, MouseEventArgs e) { } /// <summary> /// 鼠標按下事件:記錄當前鼠標相對窗體的坐標 /// </summary> void MouseDown(object sender, MouseEventArgs e) { } /// <summary> /// 鼠標移動事件:讓控件跟著鼠標移動 /// </summary> void MouseMove(object sender, MouseEventArgs e) { } /// <summary> /// 鼠標彈起事件:讓自定義的邊框出現(xiàn) /// </summary> void MouseUp(object sender, MouseEventArgs e) { } #endregion } }
接著我們需要實現(xiàn)MouseDown、MouseMove、MouseUp三個事件。
不過在此之前,我們必須要弄清楚,移動即表示坐標的改變,所以必定要有個起始坐標和終點坐標。
所以我們在MoveControl類中加入兩個字段。
private Point pPoint; //上個鼠標坐標 private Point cPoint; //當前鼠標坐標
而且在開始拖動之前,我們肯定需要先單擊一次控件。在MouseDown時獲取當前光標的位置,保存到pPoint中。
(此處用Cursor獲得坐標的好處,就是忽略掉容器的麻煩問題)
/// <summary> /// 鼠標單擊事件:用來顯示邊框 /// </summary> void MouseClick(object sender, MouseEventArgs e) { pPoint = Cursor.Position; }
接著便實現(xiàn)MouseMove的事件,當鼠標左鍵按下時,接著移動鼠標后,繼續(xù)鼠標移動后的坐標,然后與MouseDown時記下的坐標相減,就得到鼠標的位移值,接著控件的Location加上該位移值即可,然后更新pPoint。
/// <summary> /// 鼠標移動事件:讓控件跟著鼠標移動 /// </summary> void MouseMove(object sender, MouseEventArgs e) { Cursor.Current = Cursors.SizeAll; //當鼠標處于控件內(nèi)部時,顯示光標樣式為SizeAll //當鼠標左鍵按下時才觸發(fā) if (e.Button == MouseButtons.Left) { cPoint = Cursor.Position; //獲得當前鼠標位置 int x = cPoint.X - pPoint.X; int y = cPoint.Y - pPoint.Y; currentControl.Location = new Point(currentControl.Location.X + x, currentControl.Location.Y + y); pPoint = cPoint; } }
由于此時還沒涉及到邊框,所以MouseUp暫時不用處理。至此拖動的基本功能已經(jīng)實現(xiàn)!
目前MoveControl的完整代碼如下:
MoveControl
using System; using System.Collections.Generic; using System.Text; using System.Windows.Forms; using System.Drawing; namespace DragControl { public class MoveControl { #region Constructors public MoveControl(Control ctrl) { currentControl = ctrl; AddEvents(); } #endregion #region Fields private Control currentControl; //傳入的控件 private Point pPoint; //上個鼠標坐標 private Point cPoint; //當前鼠標坐標 #endregion #region Properties #endregion #region Methods /// <summary> /// 掛載事件 /// </summary> private void AddEvents() { currentControl.MouseDown += new MouseEventHandler(MouseDown); currentControl.MouseMove += new MouseEventHandler(MouseMove); currentControl.MouseUp += new MouseEventHandler(MouseUp); } /// <summary> /// 繪制拖拉時的黑色邊框 /// </summary> public static void DrawDragBound(Control ctrl) { ctrl.Refresh(); Graphics g = ctrl.CreateGraphics(); int width = ctrl.Width; int height = ctrl.Height; Point[] ps = new Point[5]{new Point(0,0),new Point(width -1,0), new Point(width -1,height -1),new Point(0,height-1),new Point(0,0)}; g.DrawLines(new Pen(Color.Black), ps); } #endregion #region Events /// <summary> /// 鼠標按下事件:記錄當前鼠標相對窗體的坐標 /// </summary> void MouseDown(object sender, MouseEventArgs e) { pPoint = Cursor.Position; } /// <summary> /// 鼠標移動事件:讓控件跟著鼠標移動 /// </summary> void MouseMove(object sender, MouseEventArgs e) { Cursor.Current = Cursors.SizeAll; //當鼠標處于控件內(nèi)部時,顯示光標樣式為SizeAll //當鼠標左鍵按下時才觸發(fā) if (e.Button == MouseButtons.Left) { MoveControl.DrawDragBound(this.currentControl); cPoint = Cursor.Position; //獲得當前鼠標位置 int x = cPoint.X - pPoint.X; int y = cPoint.Y - pPoint.Y; currentControl.Location = new Point(currentControl.Location.X + x, currentControl.Location.Y + y); pPoint = cPoint; } } /// <summary> /// 鼠標彈起事件:讓自定義的邊框出現(xiàn) /// </summary> void MouseUp(object sender, MouseEventArgs e) { this.currentControl.Refresh(); } #endregion } }
下面我們來測試下拖動的功能。
創(chuàng)建一個Form窗體,可以再界面上添加你要測試的控件類型,此處我只用TextBox左下測試。在Load的中添加以下代碼,將Form中的所有控件掛載上拖拉功能。
private void Form1_Load(object sender, EventArgs e) { foreach (Control ctrl in this.Controls) { new MoveControl(ctrl); } }
此時,有心人可能會發(fā)現(xiàn)VS中拖動控件時,將會出現(xiàn)黑色邊框,而處于沒有。
這也很簡單,我們在MouseMove時加上如下代碼即可。
/// <summary> /// 繪制拖拉時的黑色邊框 /// </summary> public static void DrawDragBound(Control ctrl) { ctrl.Refresh(); Graphics g = ctrl.CreateGraphics(); int width = ctrl.Width; int height = ctrl.Height; Point[] ps = new Point[5]{new Point(0,0),new Point(width -1,0), new Point(width -1,height -1),new Point(0,height-1),new Point(0,0)}; g.DrawLines(new Pen(Color.Black), ps); } /// <summary> /// 鼠標移動事件:讓控件跟著鼠標移動 /// </summary> void MouseMove(object sender, MouseEventArgs e) { Cursor.Current = Cursors.SizeAll; //當鼠標處于控件內(nèi)部時,顯示光標樣式為SizeAll //當鼠標左鍵按下時才觸發(fā) if (e.Button == MouseButtons.Left) { MoveControl.DrawDragBound(this.currentControl); cPoint = Cursor.Position; //獲得當前鼠標位置 int x = cPoint.X - pPoint.X; int y = cPoint.Y - pPoint.Y; currentControl.Location = new Point(currentControl.Location.X + x, currentControl.Location.Y + y); pPoint = cPoint; } }
同時要在MoveUp的時候,刷新一下自己,讓黑色邊框消失掉!
/// <summary> /// 鼠標彈起事件:讓自定義的邊框出現(xiàn) /// </summary> void MouseUp(object sender, MouseEventArgs e) { this.currentControl.Refresh(); }
接著用沒有邊框的控件測試下就會很明顯。如下圖所示:
2.通過邊框拖拉控件改變大小
此處的主要思路為:點擊控件的時候,創(chuàng)建一個自定義的用戶控件,該用戶控件響應(yīng)區(qū)域就是傳入控件的邊框區(qū)域,同時給它畫上虛線與8個小圓圈。
第一、創(chuàng)建用戶控件--FrameControl(邊框控件),然后增加一個字段用來保存?zhèn)魅氲目丶?,還有加載事件,此處類同前面的MoveControl。
FrameControl
using System; using System.Collections.Generic; using System.ComponentModel; using System.Drawing; using System.Data; using System.Linq; using System.Text; using System.Windows.Forms; namespace DragControl { public partial class FrameControl : UserControl { #region Constructors public FrameControl(Control ctrl) { baseControl = ctrl; AddEvents(); } #endregion #region Fields Control baseControl; //基礎(chǔ)控件,即被包圍的控件 #endregion #region Methods /// <summary> /// 加載事件 /// </summary> private void AddEvents() { this.Name = "FrameControl" + baseControl.Name; this.MouseDown += new MouseEventHandler(FrameControl_MouseDown); this.MouseMove += new MouseEventHandler(FrameControl_MouseMove); this.MouseUp += new MouseEventHandler(FrameControl_MouseUp); } #endregion #region Events /// <summary> /// 鼠標按下事件:記錄當前鼠標相對窗體的坐標 /// </summary> void FrameControl_MouseDown(object sender, MouseEventArgs e) { } /// <summary> /// 鼠標移動事件:讓控件跟著鼠標移動 /// </summary> void FrameControl_MouseMove(object sender, MouseEventArgs e) { } /// <summary> /// 鼠標彈起事件:讓自定義的邊框出現(xiàn) /// </summary> void FrameControl_MouseUp(object sender, MouseEventArgs e) { } #endregion } }
做完這些準備工作后,將到了主要的部分,就是給控件畫邊框。
整個邊框分為三個部分:四邊框(用來設(shè)置可視區(qū)域與區(qū)域)+四條虛線(只用來顯示)+八個小圓圈(用來斜角拖拉)。
所以要建立三個字段,用來分別保存這個數(shù)據(jù)。
Rectangle[] smallRects = new Rectangle[8];//邊框中的八個小圓圈 Rectangle[] sideRects = new Rectangle[4];//四條邊框,用來做響應(yīng)區(qū)域 Point[] linePoints = new Point[5];//四條邊,用于畫虛線
接著就是創(chuàng)建用戶控件的可視區(qū)域,和上面的三個變量數(shù)值。
(以下計算位置的代碼,有興趣的人可以研究下,沒有的就直接Copy)
創(chuàng)建邊框
#region 創(chuàng)建邊框 /// <summary> /// 建立控件可視區(qū)域 /// </summary> private void CreateBounds() { //創(chuàng)建邊界 int X = baseControl.Bounds.X - square.Width - 1; int Y = baseControl.Bounds.Y - square.Height - 1; int Height = baseControl.Bounds.Height + (square.Height * 2) + 2; int Width = baseControl.Bounds.Width + (square.Width * 2) + 2; this.Bounds = new Rectangle(X, Y, Width, Height); this.BringToFront(); SetRectangles(); //設(shè)置可視區(qū)域 this.Region = new Region(BuildFrame()); g = this.CreateGraphics(); } /// <summary> /// 設(shè)置定義8個小矩形的范圍 /// </summary> void SetRectangles() { //左上 smallRects[0] = new Rectangle(new Point(0, 0), square); //右上 smallRects[1] = new Rectangle(new Point(this.Width - square.Width - 1, 0), square); //左下 smallRects[2] = new Rectangle(new Point(0, this.Height - square.Height - 1), square); //右下 smallRects[3] = new Rectangle(new Point(this.Width - square.Width - 1, this.Height - square.Height - 1), square); //上中 smallRects[4] = new Rectangle(new Point(this.Width / 2 - 1, 0), square); //下中 smallRects[5] = new Rectangle(new Point(this.Width / 2 - 1, this.Height - square.Height - 1), square); //左中 smallRects[6] = new Rectangle(new Point(0, this.Height / 2 - 1), square); //右中 smallRects[7] = new Rectangle(new Point(square.Width + baseControl.Width + 1, this.Height / 2 - 1), square); //四條邊線 //左上 linePoints[0] = new Point(square.Width / 2, square.Height / 2); //右上 linePoints[1] = new Point(this.Width - square.Width / 2 - 1, square.Height / 2); //右下 linePoints[2] = new Point(this.Width - square.Width / 2 - 1, this.Height - square.Height / 2); //左下 linePoints[3] = new Point(square.Width / 2, this.Height - square.Height / 2 - 1); //左上 linePoints[4] = new Point(square.Width / 2, square.Height / 2); //整個包括周圍邊框的范圍 ControlRect = new Rectangle(new Point(0, 0), this.Bounds.Size); } /// <summary> /// 設(shè)置邊框控件可視區(qū)域 /// </summary> /// <returns></returns> private GraphicsPath BuildFrame() { GraphicsPath path = new GraphicsPath(); //上邊框 sideRects[0] = new Rectangle(0, 0, this.Width - square.Width - 1, square.Height + 1); //左邊框 sideRects[1] = new Rectangle(0, square.Height + 1, square.Width + 1, this.Height - square.Height - 1); //下邊框 sideRects[2] = new Rectangle(square.Width + 1, this.Height - square.Height - 1, this.Width - square.Width - 1, square.Height + 1); //右邊框 sideRects[3] = new Rectangle(this.Width - square.Width - 1, 0, square.Width + 1, this.Height - square.Height - 1); path.AddRectangle(sideRects[0]); path.AddRectangle(sideRects[1]); path.AddRectangle(sideRects[2]); path.AddRectangle(sideRects[3]); return path; } #endregion
設(shè)置完位置后,接著就是繪制的工作。增加一個Draw的方法用來畫,同時設(shè)置為Public。此處不用控件的Paint,而是讓用戶調(diào)用,只因為這樣方便在不同控件之間切換,也就是一個容器中,只有當前控件有邊框。
/// <summary> /// 繪圖 /// </summary> public void Draw() { this.BringToFront(); Pen pen = new Pen(Color.Black); pen.DashStyle = DashStyle.Dot;//設(shè)置為虛線,用虛線畫四邊,模擬微軟效果 g.DrawLines(pen, linePoints);//繪制四條邊線 g.FillRectangles(Brushes.White, smallRects); //填充8個小矩形的內(nèi)部 foreach (Rectangle smallRect in smallRects) { g.DrawEllipse(Pens.Black, smallRect); //繪制8個小橢圓 } //g.DrawRectangles(Pens.Black, smallRects); //繪制8個小矩形的黑色邊線 }
做到這里,我們可以去前臺看一下效果,不過再此之前,我們需要調(diào)用該用戶控件。
調(diào)用的地方就是在控件上點擊的時候,所以在MoveControl中加入MouseClick的事件。
/// <summary> /// 鼠標單擊事件:用來顯示邊框 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> protected void MouseClick(object sender, MouseEventArgs e) { this.currentControl.Parent.Refresh();//刷新父容器,清除掉其他控件的邊框 this.currentControl.BringToFront(); fc = new FrameControl(this.currentControl); this.currentControl.Parent.Controls.Add(fc); fc.Visible = true; fc.Draw(); }
這時有了邊框之后會有一個小問題,就是拖動控件的時候,控件移動了,但是邊框還留在原地。
所以,這里需要注意的,就是移動的時候,將邊框控件隱藏掉,當MouseUp的時候再顯示。
此時的完整代碼如下:
MoveControl
using System; using System.Collections.Generic; using System.Text; using System.Windows.Forms; using System.Drawing; namespace DragControl { public class MoveControl { #region Constructors public MoveControl(Control ctrl) { currentControl = ctrl; AddEvents(); } #endregion #region Fields private Control currentControl; //傳入的控件 private Point pPoint; //上個鼠標坐標 private Point cPoint; //當前鼠標坐標 FrameControl fc;//邊框控件 #endregion #region Properties #endregion #region Methods /// <summary> /// 掛載事件 /// </summary> private void AddEvents() { currentControl.MouseClick += new MouseEventHandler(MouseClick); currentControl.MouseDown += new MouseEventHandler(MouseDown); currentControl.MouseMove += new MouseEventHandler(MouseMove); currentControl.MouseUp += new MouseEventHandler(MouseUp); } /// <summary> /// 繪制拖拉時的黑色邊框 /// </summary> public static void DrawDragBound(Control ctrl) { ctrl.Refresh(); Graphics g = ctrl.CreateGraphics(); int width = ctrl.Width; int height = ctrl.Height; Point[] ps = new Point[5]{new Point(0,0),new Point(width -1,0), new Point(width -1,height -1),new Point(0,height-1),new Point(0,0)}; g.DrawLines(new Pen(Color.Black), ps); } #endregion #region Events /// <summary> /// 鼠標單擊事件:用來顯示邊框 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> protected void MouseClick(object sender, MouseEventArgs e) { this.currentControl.Parent.Refresh();//刷新父容器,清除掉其他控件的邊框 this.currentControl.BringToFront(); fc = new FrameControl(this.currentControl); this.currentControl.Parent.Controls.Add(fc); fc.Visible = true; fc.Draw(); } /// <summary> /// 鼠標按下事件:記錄當前鼠標相對窗體的坐標 /// </summary> void MouseDown(object sender, MouseEventArgs e) { pPoint = Cursor.Position; } /// <summary> /// 鼠標移動事件:讓控件跟著鼠標移動 /// </summary> void MouseMove(object sender, MouseEventArgs e) { Cursor.Current = Cursors.SizeAll; //當鼠標處于控件內(nèi)部時,顯示光標樣式為SizeAll //當鼠標左鍵按下時才觸發(fā) if (e.Button == MouseButtons.Left) { MoveControl.DrawDragBound(this.currentControl); if (fc != null) fc.Visible = false; //先隱藏 cPoint = Cursor.Position; //獲得當前鼠標位置 int x = cPoint.X - pPoint.X; int y = cPoint.Y - pPoint.Y; currentControl.Location = new Point(currentControl.Location.X + x, currentControl.Location.Y + y); pPoint = cPoint; } } /// <summary> /// 鼠標彈起事件:讓自定義的邊框出現(xiàn) /// </summary> void MouseUp(object sender, MouseEventArgs e) { this.currentControl.Refresh(); if (fc != null) { fc.Visible = true; fc.Draw(); } } #endregion } }
FrameControl
using System; using System.Collections.Generic; using System.ComponentModel; using System.Drawing; using System.Data; using System.Text; using System.Windows.Forms; using System.Drawing.Drawing2D; namespace DragControl { public partial class FrameControl : UserControl { #region Constructors public FrameControl(Control ctrl) { baseControl = ctrl; AddEvents(); CreateBounds(); } #endregion #region Fields const int Band = 6; //調(diào)整大小的響應(yīng)邊框 Size square = new Size(Band, Band);//小矩形大小 Control baseControl; //基礎(chǔ)控件,即被包圍的控件 Rectangle[] smallRects = new Rectangle[8];//邊框中的八個小圓圈 Rectangle[] sideRects = new Rectangle[4];//四條邊框,用來做響應(yīng)區(qū)域 Point[] linePoints = new Point[5];//四條邊,用于畫虛線 Graphics g; //畫圖板 Rectangle ControlRect; //控件包含邊框的區(qū)域 #endregion #region Methods /// <summary> /// 加載事件 /// </summary> private void AddEvents() { this.Name = "FrameControl" + baseControl.Name; this.MouseDown += new MouseEventHandler(FrameControl_MouseDown); this.MouseMove += new MouseEventHandler(FrameControl_MouseMove); this.MouseUp += new MouseEventHandler(FrameControl_MouseUp); } #region 創(chuàng)建邊框 /// <summary> /// 建立控件可視區(qū)域 /// </summary> private void CreateBounds() { //創(chuàng)建邊界 int X = baseControl.Bounds.X - square.Width - 1; int Y = baseControl.Bounds.Y - square.Height - 1; int Height = baseControl.Bounds.Height + (square.Height * 2) + 2; int Width = baseControl.Bounds.Width + (square.Width * 2) + 2; this.Bounds = new Rectangle(X, Y, Width, Height); this.BringToFront(); SetRectangles(); //設(shè)置可視區(qū)域 this.Region = new Region(BuildFrame()); g = this.CreateGraphics(); } /// <summary> /// 設(shè)置定義8個小矩形的范圍 /// </summary> void SetRectangles() { //左上 smallRects[0] = new Rectangle(new Point(0, 0), square); //右上 smallRects[1] = new Rectangle(new Point(this.Width - square.Width - 1, 0), square); //左下 smallRects[2] = new Rectangle(new Point(0, this.Height - square.Height - 1), square); //右下 smallRects[3] = new Rectangle(new Point(this.Width - square.Width - 1, this.Height - square.Height - 1), square); //上中 smallRects[4] = new Rectangle(new Point(this.Width / 2 - 1, 0), square); //下中 smallRects[5] = new Rectangle(new Point(this.Width / 2 - 1, this.Height - square.Height - 1), square); //左中 smallRects[6] = new Rectangle(new Point(0, this.Height / 2 - 1), square); //右中 smallRects[7] = new Rectangle(new Point(square.Width + baseControl.Width + 1, this.Height / 2 - 1), square); //四條邊線 //左上 linePoints[0] = new Point(square.Width / 2, square.Height / 2); //右上 linePoints[1] = new Point(this.Width - square.Width / 2 - 1, square.Height / 2); //右下 linePoints[2] = new Point(this.Width - square.Width / 2 - 1, this.Height - square.Height / 2); //左下 linePoints[3] = new Point(square.Width / 2, this.Height - square.Height / 2 - 1); //左上 linePoints[4] = new Point(square.Width / 2, square.Height / 2); //整個包括周圍邊框的范圍 ControlRect = new Rectangle(new Point(0, 0), this.Bounds.Size); } /// <summary> /// 設(shè)置邊框控件可視區(qū)域 /// </summary> /// <returns></returns> private GraphicsPath BuildFrame() { GraphicsPath path = new GraphicsPath(); //上邊框 sideRects[0] = new Rectangle(0, 0, this.Width - square.Width - 1, square.Height + 1); //左邊框 sideRects[1] = new Rectangle(0, square.Height + 1, square.Width + 1, this.Height - square.Height - 1); //下邊框 sideRects[2] = new Rectangle(square.Width + 1, this.Height - square.Height - 1, this.Width - square.Width - 1, square.Height + 1); //右邊框 sideRects[3] = new Rectangle(this.Width - square.Width - 1, 0, square.Width + 1, this.Height - square.Height - 1); path.AddRectangle(sideRects[0]); path.AddRectangle(sideRects[1]); path.AddRectangle(sideRects[2]); path.AddRectangle(sideRects[3]); return path; } #endregion /// <summary> /// 繪圖 /// </summary> public void Draw() { this.BringToFront(); Pen pen = new Pen(Color.Black); pen.DashStyle = DashStyle.Dot;//設(shè)置為虛線,用虛線畫四邊,模擬微軟效果 g.DrawLines(pen, linePoints);//繪制四條邊線 g.FillRectangles(Brushes.White, smallRects); //填充8個小矩形的內(nèi)部 foreach (Rectangle smallRect in smallRects) { g.DrawEllipse(Pens.Black, smallRect); //繪制8個小橢圓 } //g.DrawRectangles(Pens.Black, smallRects); //繪制8個小矩形的黑色邊線 } #endregion #region Events /// <summary> /// 鼠標按下事件:記錄當前鼠標相對窗體的坐標 /// </summary> void FrameControl_MouseDown(object sender, MouseEventArgs e) { } /// <summary> /// 鼠標移動事件:讓控件跟著鼠標移動 /// </summary> void FrameControl_MouseMove(object sender, MouseEventArgs e) { } /// <summary> /// 鼠標彈起事件:讓自定義的邊框出現(xiàn) /// </summary> void FrameControl_MouseUp(object sender, MouseEventArgs e) { } #endregion } }
測試界面:
到目前為止,還只是有邊框,下面將實現(xiàn)拖拉功能。
首先來實現(xiàn),當鼠標放在響應(yīng)區(qū)域的時候,根據(jù)不同的位置顯示不同的箭頭樣子。
為此先創(chuàng)建一個枚舉,用來記錄當前鼠標的位置,等拖拉的時候根據(jù)該枚舉值做不同的計算。
/// <summary> /// 鼠標在控件中位置 /// </summary> enum MousePosOnCtrl { NONE = 0, TOP = 1, RIGHT = 2, BOTTOM = 3, LEFT = 4, TOPLEFT = 5, TOPRIGHT = 6, BOTTOMLEFT = 7, BOTTOMRIGHT = 8, }
創(chuàng)建一個方法,用來改變光標的樣子以及枚舉值
/// <summary> /// 設(shè)置光標狀態(tài) /// </summary> public bool SetCursorShape(int x, int y) { Point point = new Point(x, y); if (!ControlRect.Contains(point)) { Cursor.Current = Cursors.Arrow; return false; } else if (smallRects[0].Contains(point)) { Cursor.Current = Cursors.SizeNWSE; mpoc = MousePosOnCtrl.TOPLEFT; } else if (smallRects[1].Contains(point)) { Cursor.Current = Cursors.SizeNESW; mpoc = MousePosOnCtrl.TOPRIGHT; } else if (smallRects[2].Contains(point)) { Cursor.Current = Cursors.SizeNESW; mpoc = MousePosOnCtrl.BOTTOMLEFT; } else if (smallRects[3].Contains(point)) { Cursor.Current = Cursors.SizeNWSE; mpoc = MousePosOnCtrl.BOTTOMRIGHT; } else if (sideRects[0].Contains(point)) { Cursor.Current = Cursors.SizeNS; mpoc = MousePosOnCtrl.TOP; } else if (sideRects[1].Contains(point)) { Cursor.Current = Cursors.SizeWE; mpoc = MousePosOnCtrl.LEFT; } else if (sideRects[2].Contains(point)) { Cursor.Current = Cursors.SizeNS; mpoc = MousePosOnCtrl.BOTTOM; } else if (sideRects[3].Contains(point)) { Cursor.Current = Cursors.SizeWE; mpoc = MousePosOnCtrl.RIGHT; } else { Cursor.Current = Cursors.Arrow; } return true; }
接著就是處理相關(guān)的三大事件MouseDown、MouseMove、MouseUp來實現(xiàn)拖拉。如同MoveControl都要增加以下兩個字段。
private Point pPoint; //上個鼠標坐標 private Point cPoint; //當前鼠標坐標
/// <summary> /// 鼠標按下事件:記錄當前鼠標相對窗體的坐標 /// </summary> void FrameControl_MouseDown(object sender, MouseEventArgs e) { pPoint = Cursor.Position; } /// <summary> /// 鼠標移動事件:讓控件跟著鼠標移動 /// </summary> void FrameControl_MouseMove(object sender, MouseEventArgs e) { if (e.Button == MouseButtons.Left) { this.Visible = false; MoveControl.DrawDragBound(baseControl); ControlMove(); } else { this.Visible = true; SetCursorShape(e.X, e.Y); //更新鼠標指針樣式 } } /// <summary> /// 鼠標彈起事件:讓自定義的邊框出現(xiàn) /// </summary> void FrameControl_MouseUp(object sender, MouseEventArgs e) { this.baseControl.Refresh(); //刷掉黑色邊框 this.Visible = true; CreateBounds(); Draw(); }
在上面的MouseMove中多了一個方法--ControlMove,這個就是根據(jù)不同的枚舉值,計算控件的移動方式和大小的方法。該方法中同時對控件的最小寬度和高度做了處理。添加如下兩個字段。
private int MinWidth = 20; //最小寬度 private int MinHeight = 20;//最小高度
/// <summary> /// 控件移動 /// </summary> private void ControlMove() { cPoint = Cursor.Position; int x = cPoint.X - pPoint.X; int y = cPoint.Y - pPoint.Y; switch (this.mpoc) { case MousePosOnCtrl.TOP: if (baseControl.Height - y > MinHeight) { baseControl.Top += y; baseControl.Height -= y; } else { baseControl.Top -= MinHeight - baseControl.Height; baseControl.Height = MinHeight; } break; case MousePosOnCtrl.BOTTOM: if (baseControl.Height + y > MinHeight) { baseControl.Height += y; } else { baseControl.Height = MinHeight; } break; case MousePosOnCtrl.LEFT: if (baseControl.Width - x > MinWidth) { baseControl.Left += x; baseControl.Width -= x; } else { baseControl.Left -= MinWidth - baseControl.Width; baseControl.Width = MinWidth; } break; case MousePosOnCtrl.RIGHT: if (baseControl.Width + x > MinWidth) { baseControl.Width += x; } else { baseControl.Width = MinWidth; } break; case MousePosOnCtrl.TOPLEFT: if (baseControl.Height - y > MinHeight) { baseControl.Top += y; baseControl.Height -= y; } else { baseControl.Top -= MinHeight - baseControl.Height; baseControl.Height = MinHeight; } if (baseControl.Width - x > MinWidth) { baseControl.Left += x; baseControl.Width -= x; } else { baseControl.Left -= MinWidth - baseControl.Width; baseControl.Width = MinWidth; } break; case MousePosOnCtrl.TOPRIGHT: if (baseControl.Height - y > MinHeight) { baseControl.Top += y; baseControl.Height -= y; } else { baseControl.Top -= MinHeight - baseControl.Height; baseControl.Height = MinHeight; } if (baseControl.Width + x > MinWidth) { baseControl.Width += x; } else { baseControl.Width = MinWidth; } break; case MousePosOnCtrl.BOTTOMLEFT: if (baseControl.Height + y > MinHeight) { baseControl.Height += y; } else { baseControl.Height = MinHeight; } if (baseControl.Width - x > MinWidth) { baseControl.Left += x; baseControl.Width -= x; } else { baseControl.Left -= MinWidth - baseControl.Width; baseControl.Width = MinWidth; } break; case MousePosOnCtrl.BOTTOMRIGHT: if (baseControl.Height + y > MinHeight) { baseControl.Height += y; } else { baseControl.Height = MinHeight; } if (baseControl.Width + x > MinWidth) { baseControl.Width += x; } else { baseControl.Width = MinWidth; } break; } pPoint = Cursor.Position; }
到此為止,功能已經(jīng)基本上實現(xiàn)。
完成代碼如下:
MoveControl
/****************************************************************** * 創(chuàng) 建 人: SamWang * 創(chuàng)建時間: 2012-5-10 16:06 * 描 述: * 移動控件但不改變大小 * 原 理: * 版 本: V1.0 * 環(huán) 境: VS2010 ******************************************************************/ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Windows.Forms; using System.Drawing; namespace DragControl { public class MoveControl { #region Constructors public MoveControl(Control ctrl) { currentControl = ctrl; AddEvents(); } #endregion #region Fields private Control currentControl; //傳入的控件 private Point pPoint; //上個鼠標坐標 private Point cPoint; //當前鼠標坐標 FrameControl fc;//邊框控件 #endregion #region Properties #endregion #region Methods /// <summary> /// 掛載事件 /// </summary> private void AddEvents() { currentControl.MouseClick += new MouseEventHandler(MouseClick); currentControl.MouseDown += new MouseEventHandler(MouseDown); currentControl.MouseMove += new MouseEventHandler(MouseMove); currentControl.MouseUp += new MouseEventHandler(MouseUp); } /// <summary> /// 繪制拖拉時的黑色邊框 /// </summary> public static void DrawDragBound(Control ctrl) { ctrl.Refresh(); Graphics g = ctrl.CreateGraphics(); int width = ctrl.Width; int height = ctrl.Height; Point[] ps = new Point[5]{new Point(0,0),new Point(width -1,0), new Point(width -1,height -1),new Point(0,height-1),new Point(0,0)}; g.DrawLines(new Pen(Color.Black), ps); } #endregion #region Events /// <summary> /// 鼠標單擊事件:用來顯示邊框 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> protected void MouseClick(object sender, MouseEventArgs e) { this.currentControl.Parent.Refresh();//刷新父容器,清除掉其他控件的邊框 this.currentControl.BringToFront(); fc = new FrameControl(this.currentControl); this.currentControl.Parent.Controls.Add(fc); fc.Visible = true; fc.Draw(); } /// <summary> /// 鼠標按下事件:記錄當前鼠標相對窗體的坐標 /// </summary> void MouseDown(object sender, MouseEventArgs e) { pPoint = Cursor.Position; } /// <summary> /// 鼠標移動事件:讓控件跟著鼠標移動 /// </summary> void MouseMove(object sender, MouseEventArgs e) { Cursor.Current = Cursors.SizeAll; //當鼠標處于控件內(nèi)部時,顯示光標樣式為SizeAll //當鼠標左鍵按下時才觸發(fā) if (e.Button == MouseButtons.Left) { MoveControl.DrawDragBound(this.currentControl); if(fc != null ) fc.Visible = false; //先隱藏 cPoint = Cursor.Position;//獲得當前鼠標位置 int x = cPoint.X - pPoint.X; int y = cPoint.Y - pPoint.Y; currentControl.Location = new Point(currentControl.Location.X + x, currentControl.Location.Y + y); pPoint = cPoint; } } /// <summary> /// 鼠標彈起事件:讓自定義的邊框出現(xiàn) /// </summary> void MouseUp(object sender, MouseEventArgs e) { this.currentControl.Refresh(); if (fc != null) { fc.Visible = true; fc.Draw(); } } #endregion } }
FrameControl
/****************************************************************** * 創(chuàng) 建 人: SamWang * 創(chuàng)建時間: 2012-5-10 17:00 * 描 述: * 在控件外部加上邊框,用于拖拉,以改變內(nèi)部控件的大小 * 原 理: * 版 本: V1.0 * 環(huán) 境: VS2010 ******************************************************************/ using System; using System.Collections.Generic; using System.Text; using System.Windows.Forms; using System.Drawing; using System.Drawing.Drawing2D; namespace DragControl { public class FrameControl : UserControl { #region Constructors /// <summary> /// 構(gòu)造函數(shù) /// </summary> public FrameControl(Control ctrl) { baseControl = ctrl; AddEvents(); CreateBounds(); } #endregion #region Fields const int Band = 6; //調(diào)整大小的響應(yīng)邊框 private int MinWidth = 20; //最小寬度 private int MinHeight = 20;//最小高度 Size square = new Size(Band, Band);//小矩形大小 Control baseControl; //基礎(chǔ)控件,即被包圍的控件 Rectangle[] smallRects = new Rectangle[8];//邊框中的八個小圓圈 Rectangle[] sideRects = new Rectangle[4];//四條邊框,用來做響應(yīng)區(qū)域 Point[] linePoints = new Point[5];//四條邊,用于畫虛線 Graphics g; //畫圖板 Rectangle ControlRect; //控件包含邊框的區(qū)域 private Point pPoint; //上個鼠標坐標 private Point cPoint; //當前鼠標坐標 private MousePosOnCtrl mpoc; #endregion #region Properties /// <summary> /// 鼠標在控件中位置 /// </summary> enum MousePosOnCtrl { NONE = 0, TOP = 1, RIGHT = 2, BOTTOM = 3, LEFT = 4, TOPLEFT = 5, TOPRIGHT = 6, BOTTOMLEFT = 7, BOTTOMRIGHT = 8, } #endregion #region Methods /// <summary> /// 加載事件 /// </summary> private void AddEvents() { this.Name = "FrameControl" + baseControl.Name; this.MouseDown += new MouseEventHandler(FrameControl_MouseDown); this.MouseMove += new MouseEventHandler(FrameControl_MouseMove); this.MouseUp += new MouseEventHandler(FrameControl_MouseUp); } #region 創(chuàng)建邊框 /// <summary> /// 建立控件可視區(qū)域 /// </summary> private void CreateBounds() { //創(chuàng)建邊界 int X = baseControl.Bounds.X - square.Width - 1; int Y = baseControl.Bounds.Y - square.Height - 1; int Height = baseControl.Bounds.Height + (square.Height * 2) + 2; int Width = baseControl.Bounds.Width + (square.Width * 2) + 2; this.Bounds = new Rectangle(X, Y, Width, Height); this.BringToFront(); SetRectangles(); //設(shè)置可視區(qū)域 this.Region = new Region(BuildFrame()); g = this.CreateGraphics(); } /// <summary> /// 設(shè)置定義8個小矩形的范圍 /// </summary> void SetRectangles() { //左上 smallRects[0] = new Rectangle(new Point(0, 0), square); //右上 smallRects[1] = new Rectangle(new Point(this.Width - square.Width - 1, 0), square); //左下 smallRects[2] = new Rectangle(new Point(0, this.Height - square.Height - 1), square); //右下 smallRects[3] = new Rectangle(new Point(this.Width - square.Width - 1, this.Height - square.Height - 1), square); //上中 smallRects[4] = new Rectangle(new Point(this.Width / 2 - 1, 0), square); //下中 smallRects[5] = new Rectangle(new Point(this.Width / 2 - 1, this.Height - square.Height - 1), square); //左中 smallRects[6] = new Rectangle(new Point(0, this.Height / 2 - 1), square); //右中 smallRects[7] = new Rectangle(new Point(square.Width + baseControl.Width + 1, this.Height / 2 - 1), square); //四條邊線 //左上 linePoints[0] = new Point(square.Width / 2, square.Height / 2); //右上 linePoints[1] = new Point(this.Width - square.Width / 2 - 1, square.Height / 2); //右下 linePoints[2] = new Point(this.Width - square.Width / 2 - 1, this.Height - square.Height / 2); //左下 linePoints[3] = new Point(square.Width / 2, this.Height - square.Height / 2 - 1); //左上 linePoints[4] = new Point(square.Width / 2, square.Height / 2); //整個包括周圍邊框的范圍 ControlRect = new Rectangle(new Point(0, 0), this.Bounds.Size); } /// <summary> /// 設(shè)置邊框控件可視區(qū)域 /// </summary> /// <returns></returns> private GraphicsPath BuildFrame() { GraphicsPath path = new GraphicsPath(); //上邊框 sideRects[0] = new Rectangle(0, 0, this.Width - square.Width - 1, square.Height + 1); //左邊框 sideRects[1] = new Rectangle(0, square.Height + 1, square.Width + 1, this.Height - square.Height - 1); //下邊框 sideRects[2] = new Rectangle(square.Width + 1, this.Height - square.Height - 1, this.Width - square.Width - 1, square.Height + 1); //右邊框 sideRects[3] = new Rectangle(this.Width - square.Width - 1, 0, square.Width + 1, this.Height - square.Height - 1); path.AddRectangle(sideRects[0]); path.AddRectangle(sideRects[1]); path.AddRectangle(sideRects[2]); path.AddRectangle(sideRects[3]); return path; } #endregion /// <summary> /// 繪圖 /// </summary> public void Draw() { this.BringToFront(); //g.FillRectangles(Brushes.LightGray, sideRects); //填充四條邊框的內(nèi)部 Pen pen = new Pen(Color.Black); pen.DashStyle = DashStyle.Dot;//設(shè)置為虛線,用虛線畫四邊,模擬微軟效果 g.DrawLines(pen, linePoints);//繪制四條邊線 g.FillRectangles(Brushes.White, smallRects); //填充8個小矩形的內(nèi)部 foreach (Rectangle smallRect in smallRects) { g.DrawEllipse(Pens.Black, smallRect); //繪制8個小橢圓 } //g.DrawRectangles(Pens.Black, smallRects); //繪制8個小矩形的黑色邊線 } /// <summary> /// 設(shè)置光標狀態(tài) /// </summary> public bool SetCursorShape(int x, int y) { Point point = new Point(x, y); if (!ControlRect.Contains(point)) { Cursor.Current = Cursors.Arrow; return false; } else if (smallRects[0].Contains(point)) { Cursor.Current = Cursors.SizeNWSE; mpoc = MousePosOnCtrl.TOPLEFT; } else if (smallRects[1].Contains(point)) { Cursor.Current = Cursors.SizeNESW; mpoc = MousePosOnCtrl.TOPRIGHT; } else if (smallRects[2].Contains(point)) { Cursor.Current = Cursors.SizeNESW; mpoc = MousePosOnCtrl.BOTTOMLEFT; } else if (smallRects[3].Contains(point)) { Cursor.Current = Cursors.SizeNWSE; mpoc = MousePosOnCtrl.BOTTOMRIGHT; } else if (sideRects[0].Contains(point)) { Cursor.Current = Cursors.SizeNS; mpoc = MousePosOnCtrl.TOP; } else if (sideRects[1].Contains(point)) { Cursor.Current = Cursors.SizeWE; mpoc = MousePosOnCtrl.LEFT; } else if (sideRects[2].Contains(point)) { Cursor.Current = Cursors.SizeNS; mpoc = MousePosOnCtrl.BOTTOM; } else if (sideRects[3].Contains(point)) { Cursor.Current = Cursors.SizeWE; mpoc = MousePosOnCtrl.RIGHT; } else { Cursor.Current = Cursors.Arrow; } return true; } /// <summary> /// 控件移動 /// </summary> private void ControlMove() { cPoint = Cursor.Position; int x = cPoint.X - pPoint.X; int y = cPoint.Y - pPoint.Y; switch (this.mpoc) { case MousePosOnCtrl.TOP: if (baseControl.Height - y > MinHeight) { baseControl.Top += y; baseControl.Height -= y; } else { baseControl.Top -= MinHeight - baseControl.Height; baseControl.Height = MinHeight; } break; case MousePosOnCtrl.BOTTOM: if (baseControl.Height + y > MinHeight) { baseControl.Height += y; } else { baseControl.Height = MinHeight; } break; case MousePosOnCtrl.LEFT: if (baseControl.Width - x > MinWidth) { baseControl.Left += x; baseControl.Width -= x; } else { baseControl.Left -= MinWidth - baseControl.Width; baseControl.Width = MinWidth; } break; case MousePosOnCtrl.RIGHT: if (baseControl.Width + x > MinWidth) { baseControl.Width += x; } else { baseControl.Width = MinWidth; } break; case MousePosOnCtrl.TOPLEFT: if (baseControl.Height - y > MinHeight) { baseControl.Top += y; baseControl.Height -= y; } else { baseControl.Top -= MinHeight - baseControl.Height; baseControl.Height = MinHeight; } if (baseControl.Width - x > MinWidth) { baseControl.Left += x; baseControl.Width -= x; } else { baseControl.Left -= MinWidth - baseControl.Width; baseControl.Width = MinWidth; } break; case MousePosOnCtrl.TOPRIGHT: if (baseControl.Height - y > MinHeight) { baseControl.Top += y; baseControl.Height -= y; } else { baseControl.Top -= MinHeight - baseControl.Height; baseControl.Height = MinHeight; } if (baseControl.Width + x > MinWidth) { baseControl.Width += x; } else { baseControl.Width = MinWidth; } break; case MousePosOnCtrl.BOTTOMLEFT: if (baseControl.Height + y > MinHeight) { baseControl.Height += y; } else { baseControl.Height = MinHeight; } if (baseControl.Width - x > MinWidth) { baseControl.Left += x; baseControl.Width -= x; } else { baseControl.Left -= MinWidth - baseControl.Width; baseControl.Width = MinWidth; } break; case MousePosOnCtrl.BOTTOMRIGHT: if (baseControl.Height + y > MinHeight) { baseControl.Height += y; } else { baseControl.Height = MinHeight; } if (baseControl.Width + x > MinWidth) { baseControl.Width += x; } else { baseControl.Width = MinWidth; } break; } pPoint = Cursor.Position; } #endregion #region Events /// <summary> /// 鼠標按下事件:記錄當前鼠標相對窗體的坐標 /// </summary> void FrameControl_MouseDown(object sender, MouseEventArgs e) { pPoint = Cursor.Position; } /// <summary> /// 鼠標移動事件:讓控件跟著鼠標移動 /// </summary> void FrameControl_MouseMove(object sender, MouseEventArgs e) { if (e.Button == MouseButtons.Left) { this.Visible = false; MoveControl.DrawDragBound(baseControl); ControlMove(); } else { this.Visible = true; SetCursorShape(e.X, e.Y); //更新鼠標指針樣式 } } /// <summary> /// 鼠標彈起事件:讓自定義的邊框出現(xiàn) /// </summary> void FrameControl_MouseUp(object sender, MouseEventArgs e) { this.baseControl.Refresh(); //刷掉黑色邊框 this.Visible = true; CreateBounds(); Draw(); } #endregion } }
四、遺留問題
1.ListBox存在拖拉高度時,存在莫名奇妙的BUG。
2.目前該版本只支持單控件的拖拉,多控件同時拖拉等下次有空再弄。
以上這篇C# 實現(xiàn)拖拉控件改變位置與大小的方法就是小編分享給大家的全部內(nèi)容了,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
C#實現(xiàn)SQL批量插入數(shù)據(jù)到表的方法
這篇文章主要介紹了C#實現(xiàn)SQL批量插入數(shù)據(jù)到表的方法,涉及C#批量操作SQL的相關(guān)技巧,需要的朋友可以參考下2016-04-04WPF 在image控件用鼠標拖拽出矩形的實現(xiàn)方法
這篇文章主要介紹了WPF 在image控件用鼠標拖拽出矩形的實現(xiàn)方法,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2018-08-08C#將PDF轉(zhuǎn)為多種圖像文件格式的方法(Png/Bmp/Emf/Tiff)
這里介紹將PDF轉(zhuǎn)換多種不同格式的圖像文件格式,如PNG,BMP,EMF,TIFF等,同時,轉(zhuǎn)換文檔也分為轉(zhuǎn)換全部文檔和轉(zhuǎn)換部分文檔為圖片兩種情況,本文也將作進一步介紹2018-02-02C# DataGridView中實現(xiàn)勾選存儲數(shù)據(jù)和右鍵刪除數(shù)據(jù)(示例代碼)
這篇文章主要介紹了C# DataGridView中實現(xiàn)勾選存儲數(shù)據(jù)和右鍵刪除數(shù)據(jù)的示例代碼,通過示例代碼給大家展示運行效果圖,需要的朋友可以參考下2021-07-07C#遠程發(fā)送和接收數(shù)據(jù)流生成圖片的方法
這篇文章主要介紹了C#遠程發(fā)送和接收數(shù)據(jù)流生成圖片的方法,涉及C#通過數(shù)據(jù)流傳輸圖片的相關(guān)技巧,具有一定參考借鑒價值,需要的朋友可以參考下2015-07-07C#入門之checked和unchecked的區(qū)別實例解析
這篇文章主要介紹了C#中checked和unchecked的區(qū)別,是學習C#必須要牢固掌握的,需要的朋友可以參考下2014-08-08【C#基礎(chǔ)】Substring截取字符串的方法小結(jié)(推薦)
這篇文章主要介紹了Substring截取字符串方法小結(jié),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2019-05-05