WindowsMobile/Win Form-界面自适应
起因
使用SmartPhone上的WinForm做了一个WM的小程序,结果放到手机上实际一运行。发现动态生成的控件在里面显示得都非常小,难以看清。
原因
我的问题是需要在InitializeComponent方法结束后,动态生成一些控件,如下:
/// <summary>
/// 这个方法会根据传入的实体模型,生成一些选择框,设置它们的大小、位置;并会改变其它控件的大小、位置。
/// </summary>
/// <param name="categories"></param>
private void GenerateCheckBoxes(IList<Category> categories)
{
……
}
原因就是因为手机分辨率较大,而这些动态生成的控件并没有进行随着分辨率不同而进行自动缩放。而由界面设计器设计出来的控件,都能很好的显示。
求索
由于界面生成的控件能够很好的自适应分辨率的不同,所以先看一下Designer生成的代码:
private void InitializeComponent()
{
this.BAdd = new System.Windows.Forms.Button();
this.PCategories = new System.Windows.Forms.Panel();
this.SuspendLayout();
// BAdd
this.BAdd.Location = new System.Drawing.Point(165, 164);
this.BAdd.Name = "BAdd";
this.BAdd.Size = new System.Drawing.Size(72, 20);
this.BAdd.TabIndex = 11;
this.BAdd.Text = "Add";
this.BAdd.Click += new System.EventHandler(this.BAdd_Click);
// PCategories
this.PCategories.Location = new System.Drawing.Point(73, 83);
this.PCategories.Name = "PCategories";
this.PCategories.Size = new System.Drawing.Size(164, 75);
// MainForm
this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi;
this.AutoScroll = true;
this.ClientSize = new System.Drawing.Size(243, 258);
this.Controls.Add(this.PCategories);
this.Controls.Add(this.BAdd);
this.Name = "MainForm";
this.Text = "MoneyManagerForm";
this.ResumeLayout(false);
}
这里的重点是使用了AutoScaleDimensions和AutoScaleMode属性来设置界面为自动缩放。(Dpi表示Dot per inch,WPF就是直接使用这种方式来控制界面的。)然后最后一步调用ResumeLayout方法,这个方法中,会调用到ContainerControl.PerformAutoScale方法进行自动缩放。
最可恶的一点:从控件的构造,到界面的自动缩放,全部在一个方法中实现!而且这个方法中,没有什么好的办法来调用我生成控件的方法……
解决过程
在Form中,重写ScaleControl方法如下:
protected override void ScaleControl(SizeF factor, BoundsSpecified specified)
{
var categories = Config.Instance.Categories;
this.GenerateCheckBoxes(categories);
base.ScaleControl(factor, specified);
}
因为调用过程是这样的:
Control.LayoutResume() –> ContainerControl.PerformAutoScale() –> Control.Scale() –> Control.ScaleControl() & Control.ScaleChildControls()。
所以,只需要重写这个方法,就可以在真正执行自动缩放所有控件前,先把动态控件生成。
不过,这样做同样有局限性:因为这里是在InitializeComponent方法中进行PerformAutoScale,所以这里的这些动态生成的控件,其实是在应用程序的开始阶段就已经被明确了。相反,如果在运行一段时间后,需要想再动态生成其它控件,就不能使用这个方法了。那时,就需要直接调用刚生成的需要缩放的控件的Scale方法。而且不能直接使用PerformAutoScale方法了,因为要保证一个控件只被调用Scale方法一次! 在这里,只需要这样简单实现就行了。:)
另外,一开始以为PerformAutoScale并不会把缩放过的控件,再缩放一次,结果就写成了这样的错误方案:
public MainForm()
{
InitializeComponent();
//暂停布局
this.SuspendLayout();
//在InitComponents调用的PerformAutoScale方法里面,最后会把这个数给置为运行时的数据。所以这里需要重新设置。
this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F);
//生成控件
this.GenerateCategories();
//自动缩放
this.PerformAutoScale();//其实这里会把InitializeComponent中缩放的控件都再缩放一遍。
//继续布局
this.ResumeLayout(false);
}
结束语
其实,这里的自动缩放过程,在WinForm开发中,也是一样的。
而且这次实践中,我还发现:我在Win7的系统上随手点了一下这个程序,居然所有功能都能够正常的运行……汗,当时做的时候,可是专门为WindowsMobile开发的窗体啊。这个“跨平台”功能,确实很强大,着实让我吃惊不小。
另外,由于VS2008自带的模拟器的屏幕分辨率和设计时的分辨率是一样大的,而我手机的分辨率比这个要大得多。所以每次调试这个缩放过程时,都要生成好了,然后拷贝到手机上看效果,真是吐血……
引用
Windows 窗体中的自动缩放
自动缩放的执行过程
Windows 窗体现在使用下面的逻辑自动对窗体及其内容进行缩放:
- 设计时,每一个 ContainerControl 分别在 AutoScaleMode 和 AutoScaleDimensions 中记录缩放模式和它的当前分辨率。
- 运行时,实际分辨率存储在 CurrentAutoScaleDimensions 属性中。AutoScaleFactor 属性会动态计算运行时分辨率与设计时分辨率的比值。
- 当加载窗体时,如果 CurrentAutoScaleDimensions 和 AutoScaleDimensions 的值不同,则会调用 PerformAutoScale 方法对该控件及其子控件进行缩放。此方法会挂起布局并调用 Scale 方法执行实际缩放。然后,会更新 AutoScaleDimensions 值以避免累进缩放。
- 在下面的情况下还会自动调用 PerformAutoScale:
- 在缩放模式为 Font 时响应 OnFontChanged 事件。
- 当继续执行容器控件的布局时检测到 AutoScaleDimensions 或 AutoScaleMode 属性发生更改。
- 与上面的情况类似,检测到父 ContainerControl 正在被缩放。每个容器控件只负责使用自己的比例因子缩放自己的子控件,并不负责缩放其父容器中的控件。
- 子控件可以通过下面的若干方式修改其缩放行为:
- 可以重写 ScaleChildren 属性以确定是否应缩放其子控件。
- 可以重写 GetScaledBounds 方法以调整要将控件缩放至的边界,但不调整缩放逻辑。
- 可以重写 ScaleControl 方法以更改当前控件的缩放逻辑。
- 我(作为一名开发者)所犯过的错误
- Hadoop-2.7.4 集群快速搭建
- Scala-2.13.0 安装及配置
- HBase-1.3.1 集群搭建
- CentOs7.3 Hadoop 用户 ssh 免密登录
- 手把手教你用Mysql-Cluster-7.5搭建数据库集群
- 简单的java开源图床
- 调度器Quartz的简述与使用总结
- 使用 RecyclerView 实现 Gallery 画廊效果,并控制 Item 停留位置
- linux chmod,chown命令详解
- Quartz任务调度快速入门
- ElasticSearch 安装报错整理
- Docker Compose 1.16.1 安装
- 教你如何用 RecyclerView 做一个好用的轮播图
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法