我有一个扩展System.Windows.Forms.TabControl
的类,并为其TabPage
实现了拖放机制,如下所示:
#region Overriden base methods
protected override void OnDragOver(DragEventArgs e)
{
if (PointedTabPage == null) return;
e.Effect = DragDropEffects.Move;
var dragTab = e.Data.GetData(typeof (ManagedTabPage)) as ManagedTabPage;
if (dragTab == null) return;
int dropIndex = TabPages.IndexOf(PointedTabPage);
int dragIndex = TabPages.IndexOf(dragTab);
if (dragIndex == dropIndex) return;
var modifiedTabPages = new List<ManagedTabPage>(from ManagedTabPage tabPage in TabPages
where TabPages.IndexOf(tabPage) != dragIndex
select TabPages[TabPages.IndexOf(tabPage)] as ManagedTabPage);
modifiedTabPages.Insert(dropIndex, dragTab);
for (byte i = 0; i < TabPages.Count; ++i)
{
var managedTabPage = TabPages[i] as ManagedTabPage;
if (managedTabPage != null && managedTabPage.Uid == modifiedTabPages[i].Uid)
continue;
TabPages[i] = modifiedTabPages[i];
}
SelectedTab = dragTab;
}
protected override void OnMouseDown(MouseEventArgs e)
{
try
{
switch (e.Button)
{
case MouseButtons.Left:
DoDragDrop(PointedTabPage, DragDropEffects.Move);
break;
case MouseButtons.Middle:
CloseTab(PointedTabPage);
break;
}
}
catch (InvalidOperationException)
{
}
finally
{
TabPages.Insert(0, String.Empty);
TabPages.RemoveAt(0);
}
}
#endregion
Nota bene在finally
方法的OnMouseDown
条款中有2行用于解决问题
问题:出于某些原因,在拖动&#39; n'删除其标题的任何TabPage
对齐后,没有这些行是错误的:
如果没有这种有臭味的解决方法,我该怎么做才能纠正这种行为?也许发送一些Windows消息可以做到这一点? 非常感谢任何建议。
修改1。 ManagedTabPage
代码100%不重要(它只是扩展TabPage
并带有一些特定属性。)
PointedTabPage
也不重要,但就是这样:
return (from ManagedTabPage tabPage in TabPages
let tabPageIndex = TabPages.IndexOf(tabPage)
where GetTabRect(tabPageIndex).Contains(PointToClient(Cursor.Position))
select TabPages[tabPageIndex]).Single() as ManagedTabPage;
我试图实现标签的完全居中对齐。你看,截图上的标签没有水平居中?
答案 0 :(得分:1)
我对发布的代码无能为力。让我们采用完全不同的方法,创建一个tabcontrol,支持使用鼠标拖动标签页。在项目中添加一个新类并粘贴此代码:
using System;
using System.Drawing;
using System.Windows.Forms;
class TabControlEx : TabControl {
private Point downPos;
private Form draggingHost;
private Rectangle draggingBounds;
private Point draggingPos;
public TabControlEx() {
this.SetStyle(ControlStyles.UserMouse, true);
}
}
变量的使用后来变得清晰。我们需要的第一件事是从TabControl获取鼠标事件,以便我们可以看到用户试图拖动选项卡。这需要打开UserMouse控件样式,默认情况下它是内置Windows控件并使用自己的鼠标处理的控件(如TabControl)关闭。
使用Build&gt;构建新控件并将其从工具箱顶部拖到窗体上。所有内容仍然看起来像常规的TabControl,但请注意,单击选项卡不再更改活动选项卡。打开UserMouse样式的副作用。让我们先修复,粘贴:
protected override void OnMouseDown(MouseEventArgs e) {
for (int ix = 0; ix < this.TabCount; ++ix) {
if (this.GetTabRect(ix).Contains(e.Location)) {
this.SelectedIndex = ix;
break;
}
}
downPos = e.Location;
base.OnMouseDown(e);
}
我们正在存储点击位置,稍后需要检测用户拖动标签。这需要MouseMove事件,我们需要在用户将鼠标移动到远离原始点击位置的距离时开始拖动:
protected override void OnMouseMove(MouseEventArgs e) {
if (e.Button == MouseButtons.Left && this.TabCount > 1) {
var delta = SystemInformation.DoubleClickSize;
if (Math.Abs(e.X - downPos.X) >= delta.Width ||
Math.Abs(e.Y - downPos.Y) >= delta.Height) {
startDragging();
}
}
base.OnMouseMove(e);
}
startDragging方法需要创建一个可以用鼠标移动的顶层窗口,其中包含我们拖动的标签的传真。我们将它显示为一个拥有的窗口,因此它始终位于顶部,与选项卡控件的大小完全相同:
private void startDragging() {
draggingBounds = this.RectangleToScreen(new Rectangle(Point.Empty, this.Size));
draggingHost = createDraggingHost(draggingBounds);
draggingPos = Cursor.Position;
draggingHost.Show(this.FindForm());
}
createDraggingHost需要进行繁重的工作并创建一个看起来像标签的窗口。一个无边框的形式,我们将用鼠标移动。我们将使用TransparencyKey属性使其看起来类似于拖动的TabPage,顶部有一个标签。只需让它显示标签页的屏幕截图即可让它看起来一样:
private Form createDraggingHost(Rectangle bounds) {
var host = new Form() { FormBorderStyle = FormBorderStyle.None, ControlBox = false, AutoScaleMode = AutoScaleMode.None, Bounds = this.draggingBounds, StartPosition = FormStartPosition.Manual };
host.BackColor = host.TransparencyKey = Color.Fuchsia;
var tabRect = this.GetTabRect(this.SelectedIndex);
var tabImage = new Bitmap(bounds.Width, bounds.Height);
using (var gr = Graphics.FromImage(tabImage)) {
gr.CopyFromScreen(bounds.Location, Point.Empty, bounds.Size);
gr.FillRectangle(Brushes.Fuchsia, new Rectangle(0, 0, tabRect.Left, tabRect.Height));
gr.FillRectangle(Brushes.Fuchsia, new Rectangle(tabRect.Right, 0, bounds.Width - tabRect.Right, tabRect.Height));
}
host.Capture = true;
host.MouseCaptureChanged += host_MouseCaptureChanged;
host.MouseUp += host_MouseCaptureChanged;
host.MouseMove += host_MouseMove;
host.Paint += (s, pe) => pe.Graphics.DrawImage(tabImage, 0, 0);
host.Disposed += delegate { tabImage.Dispose(); };
return host;
}
请注意Capture属性的使用,即我们检测用户释放鼠标按钮或通过任何其他方式中断操作的方式。我们将使用MouseMove事件来移动窗口:
private void host_MouseMove(object sender, MouseEventArgs e) {
draggingHost.Location = new Point(draggingBounds.Left + Cursor.Position.X - draggingPos.X,
draggingBounds.Top + Cursor.Position.Y - draggingPos.Y);
}
最后我们需要处理拖拽的完成。我们将交换标签,将拖动的标签插入鼠标位置并销毁窗口:
private void host_MouseCaptureChanged(object sender, EventArgs e) {
if (draggingHost.Capture) return;
var pos = this.PointToClient(Cursor.Position);
for (int ix = 0; ix < this.TabCount; ++ix) {
if (this.GetTabRect(ix).Contains(pos)) {
if (ix != this.SelectedIndex) {
var page = this.SelectedTab;
this.TabPages.RemoveAt(this.SelectedIndex);
this.TabPages.Insert(ix, page);
this.SelectedIndex = ix;
}
break;
}
}
draggingHost.Dispose();
draggingHost = null;
}
看起来很不错。
答案 1 :(得分:0)
由于您没有共享ManagedTabPage
代码,我使用了默认TabPage
控件
在方法OnDragOver
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Windows.Forms;
namespace Demo
{
public class MyTabControl : TabControl
{
public MyTabControl()
{
SizeMode = TabSizeMode.Fixed;
ItemSize = new Size(224, 20);
}
#region Overriden base methods
protected override void OnDragOver(DragEventArgs e)
{
if (DesignMode)
return;
if (PointedTabPage == null) return;
e.Effect = DragDropEffects.Move;
var dragTab = e.Data.GetData(typeof(TabPage)) as TabPage;
if (dragTab == null) return;
int dropIndex = TabPages.IndexOf(PointedTabPage);
int dragIndex = TabPages.IndexOf(dragTab);
if (dragIndex == dropIndex) return;
// change position of tab
TabPages.Remove(dragTab);
TabPages.Insert(dropIndex, dragTab);
SelectedTab = dragTab;
base.OnDragOver(e);
}
protected override void OnMouseDown(MouseEventArgs e)
{
if (DesignMode)
return;
switch (e.Button)
{
case MouseButtons.Left:
DoDragDrop(PointedTabPage, DragDropEffects.Move);
break;
case MouseButtons.Middle:
TabPages.Remove(PointedTabPage);
break;
}
}
#endregion
TabPage PointedTabPage
{
get
{
return TabPages.OfType<TabPage>()
.Where((p, tabPageIndex) => GetTabRect(tabPageIndex).Contains(PointToClient(Cursor.Position)))
.FirstOrDefault();
}
}
}
}