Chinaunix首页 | 论坛 | 博客
  • 博客访问: 2314866
  • 博文数量: 527
  • 博客积分: 10343
  • 博客等级: 上将
  • 技术积分: 5565
  • 用 户 组: 普通用户
  • 注册时间: 2005-07-26 23:05
文章分类

全部博文(527)

文章存档

2014年(4)

2012年(13)

2011年(19)

2010年(91)

2009年(136)

2008年(142)

2007年(80)

2006年(29)

2005年(13)

我的朋友

分类: WINDOWS

2008-03-05 09:47:33

总会有这样的情况, 需要在运行时动态调整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的行为.

Workaround

1. 设置两次
丑陋, 这样的代码不加注释对阅读代码的人简直就是犯罪.

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
下载:下载

阅读(1479) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~