总会有这样的情况, 需要在运行时动态调整WinForms上控件的位置, 大小, 子控件是否出现, 整个Form的大小等等.
下面用一个简单的例子来说明.NET 中WinForms在这方面的一个bug:
其中OK按钮的Anchor属性设为Bottom, Right, 这样在窗体被调整大小时, 其位置总是相对于窗体的右下角保持不变(不管是用户用鼠标手工调整还是通过程序动态改变窗体的Size/Width/Height Property).
程序的目标是根据groupBox1 控件的大小, 动态的调整窗体的大小, 使得OK按钮位于groupBox1的底部向下20像素, 保持窗体高度适中.
在程序的动态函数中加入下面的代码实现:
public Form1()
{
//
// Required for Windows Form Designer support
//
InitializeComponent();
//
// TODO: Add any constructor code after InitializeComponent call
//
int grp_height = 50;
groupBox1.Height = grp_height;
int idea_btn_ok_top = groupBox1.Bottom + 20;
int delta = btn_OK.Top - idea_btn_ok_top;
this.Height -= delta;
}
|
这里强制把groupBox1的高度设为50像素, 然后设置 OK按钮应该出现的"理想"位置是groupBox1底部向下20像素, 根据这个"理想"位置与OK按钮的当前实际位置, 求出一个差值, 以这个差值来调整整个窗体的大小. 这里没有调整OK按钮本身的位置, 是因为它的位置将通过 Anchor属性的Bottom, Right起作用.
但是, 这段代码在窗体的BorderStyle是Fixed时均不能正常工作, 同样的代码再运行一次却可以.
在窗体显示出来之后, 通过上面的TextBox中输入一个高度值, 按button2按钮, 触发上面同样的代码时, 不论窗体风格是什么样的, 都可以正确设置.
这是在VS.NET 2003中的属性窗口中, 对Form的FormBorderStyle 做不同选择, 得到的结果也不同.
此外, 对于SizeableToolWindow, 也不能得到正确调整的结果, 而且其调整后的布局与其它 Fixed***风格也不同.
.NET 2.0 中表现跟.NET 1.1一样.
用reflector并不能看到所有的代码, 在Form的Height被设置一个新值时, 触发了窗体的Layout, 其中关键的Layout代码恰恰看不到. 但如此怪异的窗口行为不管怎么样也解释不通. MSDN里面也没有看到什么地方说 FormBorderStyle这样的 Property会影响 Anchor, Owner窗口Height的行为.
Workaround1. 设置两次
丑陋, 这样的代码不加注释对阅读代码的人简直就是犯罪.
for (int i = 0; i < 2; i++)
{
int pixel_btun_ok_bottom = this.Height - btn_OK.Bottom;
int delta = btn_OK.Top - idea_ok_btn_top;
this.Height -= delta;
//Tricks: If the form is FixedXXX such as FixedDialog, The above
//manipulation of Form/Control's Location/Size won't work properly for
//the first time.
//Maybe a microsoft bug
if(this.Height - btn_OK.Bottom == pixel_btun_ok_bottom &&
btn_OK.Top == idea_ok_btn_top) break;
}
|
2. 在主程序中再次设置 FormBorderStyle
在构造函数中, InitializeComponent之后加上这么一句看似废话的东西:
this.FormBorderStyle = FormBorderStyle;
.NET中的Property实际上引起了函数调用, 所以它只是看似什么都没做. 实际上有可能执行了某种操作. 它与下面的C代码不同.
int i =5;
i = i; //这是真正的废话
设置这句话起作用的真正原因在于这条语句是在 this.ResumeLayout(false); 之后执行的. 将InitializeComponent 相应的语句手工移动到 this.ResumeLayout(false);之后, 也可消除该问题.
3. 窗口的标题栏中 ControlBox 不可设为False.
我观察到, 只要标题栏右侧的 最小化, 最大化, 关闭 三个按钮都不显示, 就会出现这个问题, 但凡有一个按钮显示, 不论FormBorderStyle 如何设置, 结果都是正确的.
另外. InitializeComponent函数中, 如果你设置FormBorderStyle 为 Sizable, 则代码中不会出现对
this.FormBorderStyle 赋值的语句(如此此前有也会删除), 对其它的FormBorderStyle 取值则会出现这么一条赋值语句.
与设置FormBorderStyle类似, 设置 this.ControlBox = false的语句位于 this.ResumeLayout(false)之后结果就是正确的.
真正的问题出在 this.Text = "Form1"; 上, 一个窗口, 如果其窗口风格不显示标题栏的按钮, 那么标题栏是否显示就看窗口的标题文字是否是空的, 如果是非空则仍要显示标题栏. this.Text的赋值位于FormBorderStyle 之后, 查看出错时相差的距离, 也正是标题栏的高度.
从这里也暴露出一个问题, VS.NET通过Property来实现一种貌似声明式编程的特性, 程序员可以简单地在PropertyGrid 中设置相关的属性来实现一些功能, 但是, 背后生成的对Property赋值的顺序孰先孰后如何决定?
附上测试其效果的C#程序.
该程序中我手工修改了InitializeComponent中对this.FormBorderStyle 赋值的语句, 把一个来自主UI界面中选中的FormBorderStyle值设置为窗口的this.FormBorderStyle 属性.
如果在VS中打开窗体进行编辑这句话可能会被删除.
|
文件: | TestAnchor.rar |
大小: | 34KB |
下载: | 下载 |
|
阅读(1523) | 评论(0) | 转发(0) |