【问题标题】:How do I get a control's position and size from the original designer settings?如何从原始设计器设置中获取控件的位置和大小?
【发布时间】:2020-02-17 15:03:10
【问题描述】:

Windows(取决于字体/缩放/缩放)正在调整我的表单大小。我想以编程方式找到表单和控件的原始位置和大小(在设计器中设置。在运行时,这些大小和位置会随着表单的创建而变化。

【问题讨论】:

  • 很确定唯一的方法是将表单和控件的 x/y 位置复制到变量中
  • 你为什么想要这些值?它们有什么可能的用途?
  • 这是 Anchor / Docking 问题,还是计算机的显示设置问题?
  • 但是如果你正确地锚定或停靠你的控件,这应该不是问题。
  • 在用户名前使用@符号通知他们您正在回复评论。我们不知道您的表单有多复杂,但 TableLayoutPanel 能够进行基于百分比的布局。

标签: c# winforms


【解决方案1】:

Windows(取决于字体/缩放/缩放)正在调整我的表单大小。

Windows 没有调整您的表单大小。您的表单配置为自动缩放。这由AutoScaleMode Property 控制。 designer 默认值为System.Windows.Forms.AutoScaleMode.Font(真正的默认值为Inherit)。 WinForm 自动缩放的高级概述可以在文档Current support for automatic scaling 中找到。一个常用的替代值是System.Windows.Forms.AutoScaleMode.DPI。启用自动缩放时,使用AutoScaleDimensions 的值计算的缩放因子和使用报告给应用程序的 DPI 在运行时评估的值。 AutoScaleDimensions 在设计器生成的代码中设置。

如果应用程序被声明为可识别 DPI,则使用真正的 DPI 值。如果它不支持 DPI,Windows 会告诉该应用程序它以 96 dpi 运行。报告的 DPI 值也会影响在基于字体的缩放中使用的指标。

我想以编程方式找到表单和控件的原始位置和大小(在设计器中设置。在运行时,这些大小和位置会随着表单的创建而变化。

这可以通过在应用自动缩放之前截取该点来实现。由于您正在处理自动生成的代码(Form.designer.cs 文件的 InitializeComponent 方法),因此没有直接的方法可以做到这一点。正是在这种方法中设置了AutoScaleDimensionsAutoScaleMode

当您创建一个新表单时,此方法如下所示。

private void InitializeComponent()
{
    this.components = new System.ComponentModel.Container();
    this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
    this.ClientSize = new System.Drawing.Size(800, 450);
    this.Text = "Form1";
}

注意AutoScaleDimensions 属性尚未在方法中声明;一旦您对表单进行了一些更改(更改属性或添加控件)并将新信息写入文件,它将被添加。仅当 AutoScaleMode 设置为 DPI 或字体时,才会写入 AutoScaleDimensions 属性。

以下是更改表单Size的结果。

private void InitializeComponent()
{
    this.SuspendLayout();
    // 
    // Form1
    // 
    this.AutoScaleDimensions = new System.Drawing.SizeF(8F, 16F);
    this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
    this.ClientSize = new System.Drawing.Size(782, 453);
    this.Name = "Form1";
    this.Text = "Form1";
    this.ResumeLayout(false);
}

展示上述内容的目的是向您展示如何在以下建议的解决方案中获取设置AutoScaleDimensions属性的值。

建议的解决方案

在设计器中,将表单的 AutoScaleMode 属性设置为 InheritNone。然后修改表单的构造函数代码以反映以下内容:

SizeF scalingFactor;
public Form1()
{
    InitializeComponent();

    SuspendLayout();
    AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
    AutoScaleDimensions = new System.Drawing.SizeF(8.0F, 16.0F);

    // at this point you can obtain the scaling factor that will
    // be applied on ResumeLayout
    scalingFactor = AutoScaleFactor;

    // store the design-time bounds in the control's Tag property
    RecursivelyRecordBounds(this);
    ResumeLayout(true);
}

将此方法添加到表单的代码中。

private static void RecursivelyRecordBounds(Control c)
{
    c.Tag = c.Bounds;
    foreach (Control cc in c.Controls)
    {
        cc.Tag = cc.Bounds;
        if (cc.Controls.Count > 0)
        {
            RecursivelyRecordBounds(cc);
        }
    }
}

就本示例而言,每个控件的设计时Bounds 都存储在其Tag 属性中。请注意,设置两个自动缩放属性必须包含在 SupendLayout/ResumeLayout 区域中。此外,在设置 AutoScaleDimensions 属性之前先设置 AutoScaleMode 属性也很重要。

另一种解决方案(hack)是将AutoScaleMode 属性设置为Font 或DPI,并依赖Initialize 方法的观察模式。在此方法中,表单的Text 属性始终设置在两个缩放属性之后。这允许使用 TextChanged 事件来指示检索设计时边界的适当时间。

public Form1()
{
    EventHandler eh = (s,e) => RecursivelyRecordBounds(this);;
    TextChanged += eh;
    InitializeComponent();
    TextChanged -= eh;
}

【讨论】:

  • 感谢@TnTinMn 非常彻底的回复和解释。这对于理解潜在的挑战非常有帮助。
  • 这是我偶然发现的一种解决方法...我有一个面板,其中包含我正在尝试管理/缩放的随机放置的控件。如果我处理“ParentChanged”事件,我可以枚举面板的所有控件(并获取它们的位置)。有了这些信息,我现在可以在面板调整大小时以编程方式放置它们。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-03-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多