C# 实现可拖动的工具栏,可停靠浮动工具栏,也就是说,可以将工具栏拖出其原先的停靠位置,而且可以将拖出来的工具栏再拖放回去。
实现的基本思路如下:
1、拖动出来以后,需要创建一个大小合适的窗口,作为工具栏新的停靠容器,这个窗口可以这样设置:
FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedToolWindow;
ShowIcon = false;
ShowInTaskbar = false;
TopMost = true;
2、浮动工具栏可以扩展自.Net Framework提供的ToolStrip,它被拖动都某个位置,松开鼠标左键时,会触发EndDarg事件,在这个事件中,我们将其从原来的停靠容器中移除,同时根据鼠标左键松开时,在鼠标所在位置上创建一个窗口,作为工具栏的新容器。
这个就是基本的思路了,下面是浮动工具栏FloatToolstrip 具体的实现代码:
using System; using System.Collections.Generic; using System.ComponentModel; using System.Drawing; using System.Data; using System.Text; using System.Windows.Forms; namespace FloatToolStripDemo { public partial class FloatToolstrip : ToolStrip { private ToolStripPanel tsPanel; public FloatToolstrip() { InitializeComponent(); this.EndDrag += new EventHandler(MyToolStrip_EndDrag); this.SizeChanged += new EventHandler(MyToolStrip_SizeChanged); } private ToolStripFloatWindow floatForm; public ToolStripFloatWindow FloatForm { get { return floatForm; } set { floatForm = value; if (floatForm != null) { floatForm.LocationChanged += new EventHandler(floatForm_LocationChanged); floatForm.FormClosing += new FormClosingEventHandler(floatForm_FormClosing); } } } void floatForm_FormClosing(object sender, FormClosingEventArgs e) { e.Cancel = true; } private void floatForm_LocationChanged(object sender, EventArgs e) { //当floatwindws的位置移动到toolstrippanel中时,将this放置到 toolstripPanel上 if (this.floatForm == null) { return; } else { if (floatForm.HasCreated) { Point currentPt = new Point(floatForm.Location.X, floatForm.Location.Y); Point minPt = this.tsPanel.PointToScreen(tsPanel.Location); Point maxPt; if (this.tsPanel.Height <= 20) { maxPt = new Point(minPt.X + this.tsPanel.Width, minPt.Y + 20); } else { maxPt = new Point(minPt.X + this.tsPanel.Width, minPt.Y + this.tsPanel.Height); } if ((currentPt.X > minPt.X) && (currentPt.X < maxPt.X) && (currentPt.Y > minPt.Y - 25) && (currentPt.Y < maxPt.Y - 25)) { this.floatForm.Controls.Remove(this); this.tsPanel.SuspendLayout(); this.tsPanel.Controls.Add(this); this.Location = this.tsPanel.PointToClient(currentPt); this.tsPanel.ResumeLayout(); this.floatForm.Dispose(); this.floatForm = null; } } } } public bool isFloating { get { return (floatForm != null); } } public ToolStripPanel ToolStripPanel { get { return this.tsPanel; } set { this.tsPanel = value; } } private void MyToolStrip_EndDrag(object sender, EventArgs e) { //判断移除时 if (this.tsPanel == null) { MessageBox.Show("请先设置ToolStripPanel属性"); return; } Point dockPoint = Cursor.Position; int openX, openY; openX = dockPoint.X; openY = dockPoint.Y; Point clientPt = this.tsPanel.Parent.PointToClient(dockPoint); if (clientPt.Y > tsPanel.Height) { ToolStripFloatWindow tsfw = new ToolStripFloatWindow(); this.tsPanel.Controls.Remove(this); tsfw.Controls.Add(this); this.Left = 0; this.Top = 0; this.FloatForm = tsfw; Point newLoc = new Point(openX, openY); tsfw.Show(); tsfw.Location = newLoc; tsfw.SetBounds(newLoc.X, newLoc.Y, this.ClientSize.Width, this.ClientSize.Height+25); } } private void MyToolStrip_SizeChanged(object sender, EventArgs e) { if (this.isFloating) { this.floatForm.Width = this.ClientSize.Width; } } } }
动态创建的作为浮动工具栏临时容器的窗口,需要加入一些处理技巧,以免发生在创建该窗口的工作尚未结束时,Dispose了这个窗口,这种情况发生在快速拖动工具栏时,触发了EndDrag事件,导致去创建临时窗口作为工具栏新的容器,而这时又将工具栏拖回原停靠容器中,会导致Dispose还没有创建完毕的临时窗口,发生异常!
下面是具体的代码:
using System; using System.Collections.Generic; using System.Text; using System.Windows.Forms; namespace FloatToolStripDemo { public class ToolStripFloatWindow : Form { //解决鼠标拖动后,已经正在创建,但是触发Dispose的问题 private bool hasCreated=false; public bool HasCreated { get { return hasCreated; } } public ToolStripFloatWindow():base() { this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedToolWindow; this.Name = "ToolStripFloatWindow"; this.ShowIcon = false; this.ShowInTaskbar = false; this.TopMost = true; this.Load += new System.EventHandler(this.ToolStripFloatWindow_Load); } private void ToolStripFloatWindow_Load(object sender, EventArgs e) { this.hasCreated = true; } } }