利用C#更专业的实现运行时调整控件大小和位置

2009年4月24日星期五

利用C#更专业的实现运行时调整控件大小和位置




草上爬的个人空间 - Hang around with .Net



【本博文章除在标题明确注明 [转载] 外,均为作者原创!若无特别注明,本博所有原创文章均为IT168首发!任何个人/团队/公司在未经作者同意下不得擅自转载!】 本博文章所附带的源码(以下简称:源码)均遵循GPL开源协议,任何个人和团体都可以引用/修改/衍生该源码,并且可以再次开源/免费使用,使用时请在源码中保留作者信息!但是,决不可以将该源码以任何非开源形式发布及任何形式的商业用途!


利用C#更专业的实现运行时调整控件大小和位置


上一篇 / 下一篇  2008-07-29 21:43:41 / 个人分类:.Net [C#]


从Delphi、VB到现在的Visual Studio .Net可视化的控件式编程越来越简化我们的开发过程,对于控件的可视化操作必不可少。在Visual Studio .Net的IDE中开发桌面应用程序时我们可以在设计时从控件工具栏拖拽若干个控件放置到窗体上,随意移动和调整控件大小,稍加布置后我们就会得到一个非常满意的UI。若是运行时也能够移动和调整控件则会给我们带来更多的好处,譬如在.Net下运行时拖动控件可以应用于自定义窗体打印,这样就能动态的更改窗体上的控件布局和大小,打印出更加美观的报表来。本文就将利用C#演示在运行时显示一个操作边框利用它来拖动和调整控件,程序运行界面如下:
利用C更专业的实现运行时调整控件大小和位置 - chilli - 吥特意滴改变自己 ァ﹏.利用C更专业的实现运行时调整控件大小和位置 - chilli - 吥特意滴改变自己 ァ﹏.

运行时调整控件大小和位置很简单,在.Net下只需修改控件的location和size属性即可,动态调整时再捕获MouseDown、MouseMove及MouseUp事件来实时修改上述两个属性就可以实现。但是我们会发现在Visual Studio.Net的开发环境中设计窗体布局时,选择了控件后总会有一个可以调整其大小和位置的边框出现以方便用户进行操作。在VC6中MFC曾经为我们提供了CRectTracker类来完成这项工作,但是C#里我却没有发现相关类。运行时可以调整控件位置和大小在某些情况下非常有用,譬如.Net提供了窗体打印功能,若能够在运行时调整控件大小和位置我们将会动态的生成非常漂亮和规整的报表来。借鉴CRectTracker类我们发现它实际上就相当于一个父控件,然后传递给它不同类的指针进行附着。在C#下我们以另外一种思维来考虑这个问题,当在设计时的窗体上放置一个Panel控件,然后再往该Panel控件上放置一个子控件并填充之,那么在设计时调整Panel大小和位置时其子控件都会随之改变,我们就利用这个原理在运行时捕获获得焦点的子控件,然后让其成为类似于前述Panel父控件的子控件,并且在父控件周围利用.Net GDI+画上用于调整的边框和锚点,当鼠标在特定位置按下并拖动时激活MouseMove事件进行响应。实现的关键就是针对于类似Panel控件的操作,直接使用Panel控件进行上述操作也未尝不可,但是我们将会创建一个专门用于运行时动态调整控件大小和位置的自定义控件,我们把它命名为:CRectControl。


启动Visual Studio .Net 2005,首先创建C#类库。要想创建一个可以包容其它控件的容器控件,那么控件基类必须从System.Windows.Forms.UserControl继承,代码如下:


publicclassCRectControl: System.Windows.Forms.


……


}

    新建类库时默认引用不包括System.Windows.Forms和System.Drawing,我们必须手动将上述程序集添加到项目引用中。System.Windows.Forms为我们提供了丰富的创建界面的功能和方法,System.DrawingSystem.Drawing.Drawing2D,该命名空间

usingSystem.Drawing.Drawing2D;


CRectControl创建时传递需要调整的控件实例,根据控件大小及位置手动绘制CRectControl的边框,包括8个用于调整大小的锚点都是需要手动绘制的,代码如下:



baseRect =newRectangle(X, Y, Width, Height);


SmallRect[0] =newRectangle(newPoint(baseRect.X - Square.Width, baseRect.Y - Square.Height), Square);


SmallRect[4] =newRectangle(newPoint(baseRect.X + (baseRect.Width / 2) - (Square.Width / 2), baseRect.Y - Square.Height), Square);


SmallRect[1] =newRectangle(newPoint(baseRect.X + baseRect.Width, baseRect.Y - Square.Height), Square);


SmallRect[2] =newRectangle(newPoint(baseRect.X - Square.Width, baseRect.Y + baseRect.Height), Square);


SmallRect[5] =newRectangle(newPoint(baseRect.X + (baseRect.Width / 2) - (Square.Width / 2), baseRect.Y + baseRect.Height), Square);


SmallRect[3] =newRectangle(newPoint(baseRect.X + baseRect.Width, baseRect.Y + baseRect.Height), Square);


SmallRect[6] =newRectangle(newPoint(baseRect.X - Square.Width, baseRect.Y + (baseRect.Height / 2) - (Square.Height / 2)), Square);


SmallRect[7] =newRectangle(newPoint(baseRect.X + baseRect.Width, baseRect.Y + (baseRect.Height / 2) - (Square.Height / 2)), Square);


ControlRect =newRectangle(newPoint(0, 0),this.Bounds.Size);


publicCRectControl(ControltheControl)


currentControl = theControl;


intX = currentControl.Bounds.X - Square.Width;


intY = currentControl.Bounds.Y - Square.Height;


intHeight = currentControl.Bounds.Height + (Square.Height * 2);


intWidth = currentControl.Bounds.Width + (Square.Width * 2);


this.Bounds =newRectangle(X, Y, Width + 1, Height + 1);


Rect = currentControl.Bounds;


this.Region =newRegion(BuildFrame());


private


GraphicsPathpath =newGraphicsPath();


BoundRect[0] =newRectangle(0, 0, currentControl.Width + (Square.Width * 2) + 1, Square.Height + 1);


BoundRect[1] =newRectangle(0, Square.Height + 1, Square.Width + 1, currentControl.Bounds.Height + Square.Height + 1);


BoundRect[2] =newRectangle(Square.Width + 1, currentControl.Bounds.Height + Square.Height-1, currentControl.Width + Square.Width + 2, Square.Height + 2);


BoundRect[3] =newRectangle(currentControl.Width + Square.Width-1, Square.Height + 1, Square.Width + 2, currentControl.Height - 1);


path.AddRectangle(BoundRect[0]);


path.AddRectangle(BoundRect[1]);


path.AddRectangle(BoundRect[2]);


path.AddRectangle(BoundRect[3]);


}


边框和锚点的大小位置计算完毕后,我们开始实际的绘制操作:


g.FillRectangles(Brushes.LightGray, BoundRect);//填充用于调整的边框的内部


g.FillRectangles(Brushes.White, SmallRect);//填充8个锚点的内部


g.DrawRectangles(Pens.Black, SmallRect);//绘制8个锚点的黑色边线


Console.WriteLine(ex.Message);


}


    界面绘制完毕后,我们需要对用户移动和调整边框事件发生时作出响应。当鼠标移动到边框时鼠标指针首先应该设置成十字形的可以拖动的鼠标指针样式,当鼠标移动到8个锚点时鼠标指针应该设置成可以调整大小的鼠标指针样式,下图是程序运行时鼠标指针的样子:


利用C更专业的实现运行时调整控件大小和位置 - chilli - 吥特意滴改变自己 ァ﹏.


利用C更专业的实现运行时调整控件大小和位置 - chilli - 吥特意滴改变自己 ァ﹏.


    为了实时更新鼠标指针的样式,我们需要在CRectControl中捕获Mouse_Move事件,在Mouse_Move事件中我们进一步检测是否有鼠标左键按下,若没有鼠标按下我们只需更新鼠标指针的样式即可,若同时有鼠标按下我们还必须根据鼠标拖动的位置相应调整被控控件的位置和大小,为此我们新建个枚举类型的变量来保存当前鼠标停留的位置信息:


private


privatevoidRectTracker_MouseMove(objectsender, System.Windows.Forms.MouseEventArgse)


if(e.Button ==MouseButtons.Left)


prevLeftClick =newPoint(e.X, e.Y);


调整位置或大小


prevLeftClick =newPoint(e.X, e.Y);


更新鼠标指针样式


    鼠标拖动结束释放按键后我们需要根据被控控件新的位置或大小重新绘制CRectControl并显示出来:


privatevoidRectTracker_MouseUp(objectsender, System.Windows.Forms.MouseEventArgse)


}


Mouse_Move(this, e)和Hit_Test(e.X, e.Y)两个方法具体的实现请参见附带源码,不在这里占用篇幅。


该程序在Visual Studio 2005下成功编译,在Windows XP SP2+.Net 2.0下载本文附带源码



TAG:


请教一下,怎样可以把拖动后的位置保存下来,下次再运行就是拖动后的位置???
你的需求对于不具备焦点的控件倒挺好实现的,参照源码中关于鼠标拖动的代码即可;但是有些可获得焦点的控件必须借助附加键,比如在Textbox内按住Ctrl键,同时按住鼠标左键实施拖动。
 




0 评论:

发表评论