Winform下调整缩放与布局以及分辨率下界面混乱解决办法

本文提供了解决Winform界面在不同分辨率和缩放比例下出现布局混乱的方法,包括通过设置DPI和字体缩放,以及根据窗口变化动态调整控件位置和大小的技术。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Winform下调整缩放与布局以及分辨率下界面混乱解决办法

前言

当调整了分辨率或win10里的“更改文件、应用等项目的大小”选项时,winform界面会出现覆盖,混乱等情况,解决办法可参考方案一方案二
如果在程序运行时手动拖拽窗体改变大小触发了SizeChange事件,下次重新打开程序还是还来的样子,则使用方案四。

解决方案

方案一,设置this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi

第一种方式是设置this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi。 如果要相对于屏幕确定控件或窗体的大小,则按 Dpi 缩放十分有用。例如,对于显示图表或其他图形的控件,可能希望使用每英寸点数 (DPI) 缩放,以便该控件始终占据一定百分比的屏幕。
但这种方式能解决部分问题,算是第一种尝试方案,对于无法解决的用下面方案。

方案二this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font

第二种方式是设置this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font。然后在窗体里拖拽一个Panel控件,所有的后面添加的控件都放到Panel控件里。亲测,可行。

方案三:设置Form里字体大小(不推荐)

根据像素来设置界面每个控件大小,界面将整体缩放而不会凌乱,利用的就是DPI本身的特性。
缺点是字体大小都一样的,如果每个控件单独设置那太麻烦了。不推荐

public static void SetAutoScaleMode(Form form)
{
     //设定按字体来缩放控件
     form.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
     loopControls(form);
 }

 private static void loopControls(Control control)
 {
     control.Font = new System.Drawing.Font("Microsoft Sans Serif", 16F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Pixel, ((byte)(134)));
     if (control.Controls.Count > 0)
     {
         foreach (Control tmp in control.Controls)
         {
             loopControls(tmp);
         }
     }
 }

方案四:根据缩放比例重新定义控件的位置和宽高(不推荐)

网上有种说法是计算新旧窗体的缩放比例,计算位置和宽高的,比如WinForm 根据屏幕分辨率自适应。这种解决办法只试用能触发SizeChange事件的情况,比如手动缩放窗体,或调整分辨率的时刻,并不适用重新打开程序的计算。
我优化了上述情况下计算
AutoSizeFormUtil.cs

public class AutoSizeFormUtil
    {
        //(1).声明结构,只记录窗体和其控件的初始位置和大小。  
        public struct controlRect
        {
            public int Left;
            public int Top;
            public int Width;
            public int Height;
        }

        public Dictionary<string, controlRect> oldCtrlDir;
        public void controllInitializeSize(Form mForm)
        {
            oldCtrlDir = new Dictionary<string, controlRect>();
            controlRect cR;
            cR.Left = mForm.Left; cR.Top = mForm.Top; cR.Width = mForm.Width; cR.Height = mForm.Height;
            oldCtrlDir.Add(mForm.Name, cR);
            LoopInitializeSize(mForm);
        }

        private void LoopInitializeSize(Control control)
        {
            if(control.Controls.Count > 0)
            {
                foreach (Control c in control.Controls)
                {
                    controlRect cR;
                    cR.Left = c.Left; cR.Top = c.Top; cR.Width = c.Width; cR.Height = c.Height;
                    if (oldCtrlDir.ContainsKey(c.Name))
                    {
                        oldCtrlDir.Remove(c.Name);
                    }
                    oldCtrlDir.Add(c.Name, cR);
                    if(c.Controls.Count > 0)
                    {
                        LoopInitializeSize(c);
                    }
                }
            }
        }

        public void controlAutoSize(Form mForm)
        {
            float wScale = (float)mForm.Width / (float)oldCtrlDir[mForm.Name].Width;//新旧窗体之间的比例,与最早的旧窗体  
            float hScale = (float)mForm.Height / (float)oldCtrlDir[mForm.Name].Height;//.Height;  
            loopControls(mForm, wScale, hScale);
        }

        private void loopControls(Control c, float wScale, float hScale)
        {
            controlRect rect = oldCtrlDir[c.Name];
            c.Left = (int)((rect.Left) * wScale);//新旧控件之间的线性比例。控件位置只相对于窗体,所以不能加 + wLeft1  
            c.Top = (int)((rect.Top) * hScale);//  
            c.Width = (int)(rect.Width * wScale);//只与最初的大小相关,所以不能与现在的宽度相乘 (int)(c.Width * w);  
            c.Height = (int)(rect.Height * hScale);// 
            if (c.Controls.Count > 0)
            {
                foreach (Control tmp in c.Controls)
                {
                    loopControls(tmp, wScale, hScale);
                }
            }
        }

在Load事件里调用AutoSizeFormUtil.controllInitializeSize(this),在SizeChange事件里调用AutoSizeFormUtil.controlAutoSize(this)

总结

目前没找到一劳永逸的方案。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值