• 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 窗体现在使用下面的逻辑自动对窗体及其内容进行缩放:

    1. 设计时,每一个 ContainerControl 分别在 AutoScaleModeAutoScaleDimensions 中记录缩放模式和它的当前分辨率。

    2. 运行时,实际分辨率存储在 CurrentAutoScaleDimensions 属性中。AutoScaleFactor 属性会动态计算运行时分辨率与设计时分辨率的比值。

    3. 当加载窗体时,如果 CurrentAutoScaleDimensionsAutoScaleDimensions 的值不同,则会调用 PerformAutoScale 方法对该控件及其子控件进行缩放。此方法会挂起布局并调用 Scale 方法执行实际缩放。然后,会更新 AutoScaleDimensions 值以避免累进缩放。

    4. 在下面的情况下还会自动调用 PerformAutoScale

      • 在缩放模式为 Font 时响应 OnFontChanged 事件。

      • 当继续执行容器控件的布局时检测到 AutoScaleDimensionsAutoScaleMode 属性发生更改。

      • 与上面的情况类似,检测到父 ContainerControl 正在被缩放。每个容器控件只负责使用自己的比例因子缩放自己的子控件,并不负责缩放其父容器中的控件。

    5. 子控件可以通过下面的若干方式修改其缩放行为:

      • 可以重写 ScaleChildren 属性以确定是否应缩放其子控件。

      • 可以重写 GetScaledBounds 方法以调整要将控件缩放至的边界,但不调整缩放逻辑。

      • 可以重写 ScaleControl 方法以更改当前控件的缩放逻辑。

  • 相关阅读:
    zabbix 4.0 监控磁盘IO的实施笔记
    梅登黑德定位系统
    sdrplay sdr 支持的sample rate
    记录一下几个中移动可以PING的检测地址及部份DNS设置
    升级mariadb 10后目录权限问题的笔记
    C#单独启动进程的几种方式及使用特点(使用不当导致端口无法释放)
    SqlBulkCopy批量插入数据时,不执行触发器和约束的解决方法
    C# 处理大量数据的技巧
    C# 几种集合性能比较
    WPF学习网址整理
  • 原文地址:https://www.cnblogs.com/zgynhqf/p/1638284.html
Copyright © 2020-2023  润新知