【问题标题】:Change font size of all children form WinForm更改所有子窗体 WinForm 的字体大小
【发布时间】:2020-05-25 13:03:09
【问题描述】:

我正在使用 WinForm 在 C# 中制作应用程序。我希望能够使用 ctrl + 滚动来调整此应用程序内所有标签的字体大小。 目前,我可以更改一个表单内所有标签的字体,但我想将此字体大小修改应用于所有表单,而不仅仅是当前表单。

要创建所有表单,我使用的是模板:

public partial class TemplateForm : DockContent
{
    static public UInt16 percentageFontSize = 100;
    public TemplateForm()
    {
        InitializeComponent();
    }

    private void TemplateForm_MouseWheel(object sender, MouseEventArgs e)
    {
        if (!ModifierKeys.HasFlag(Keys.Control))
        {
            return;
        }
        if (e.Delta > 0)
        {
            percentageFontSize += 10;
            if (percentageFontSize >= 2000)
            {
                percentageFontSize = 2000;
            }
        }
        else
        {
            percentageFontSize -= 10;
            if (percentageFontSize <= 10)
            {
                percentageFontSize = 10;
            }
        }
        SetAllControlsFont(this.Controls);
    }

    public static void SetAllControlsFont(System.Windows.Forms.Control.ControlCollection ctrls)
    {
        foreach (Control ctrl in ctrls)
        {
            if (ctrl.Controls != null)
                SetAllControlsFont(ctrl.Controls);

            ctrl.Font = new Font("Microsoft Sans Serif", 8* percentageFontSize / 100);

        }
    }
}

每个“文档”都使用这个模板:

public partial class Form1 : TemplateForm
{
    public AccForm()
    {
        InitializeComponent();
    }
}

public partial class Form2 : TemplateForm
{
    public AccForm()
    {
        InitializeComponent();
    }
}

“this.Controls”仅授予我对所选表单的控件的访问权限。我想我可以通过使用“TemplateForm”获得对表单的所有控制来实现这一点,但我不知道该怎么做。您对如何实现这一目标有任何想法吗?

【问题讨论】:

  • 您应该覆盖OnMouseWheel,但您现在所做的仍然适用于添加到派生表单的控件。 DockContent 是什么? TemplateForm 不应该派生自 Form 吗?
  • DockContent 用作“停靠管理器”并派生自Form。我所做的不适用于添加到派生表单的控件。即使percentageFontSizeStatic,它也只会更新焦点窗体的控件,而不是所有派生窗体。请您详细说明一下覆盖OnMouseWheel 吗?
  • 您可以覆盖派生表单中的WndProc 并捕获从基本表单的OnMouseWheel 覆盖发送的自定义消息或WM_FONTCHANGED。收到消息后,拨打SetAllControlsFont(this.Controls)。这个方法(属于基础Form)应该是protected internal,而percentageFontSize必须是protected static int。改变将是立竿见影的。就是这样。
  • 如果它更舒服,您还可以覆盖基本表单的 WndProc 并在其中执行所有操作,如果您由于其他原因尚未在派生表单中覆盖 WndProc。

标签: c# winforms


【解决方案1】:

那么字体大小在其他表格上没有改变,而只在当前表格上改变的原因是因为其他表格无法知道大小实际上已经改变了,你必须添加一个方法通知他们,以便他们也可以更新字体大小。 为此,您可以使用 EventHandler(或创建自定义事件处理程序),将其添加到您的 TemplateForm 类并在构造函数中订阅它,只要有字体更改就引发事件,并且在触发方法中,您更新相应的字体。 总而言之,您的代码应如下所示:

    static public EventHandler fontSizeChangedEvent;
    static public UInt16 percentageFontSize = 100;
    public TemplateForm()
    {
        MouseWheel += TemplateForm_MouseWheel;
        fontSizeChangedEvent += FontSizeChanged;
    }

    private void TemplateForm_MouseWheel(object sender, MouseEventArgs e)
    {
        //// rest of code .....

        fontSizeChangedEvent.Invoke(this, new EventArgs());
        SetAllControlsFont();
    }

    public void FontSizeChanged(object sender, EventArgs e)
    {
        // Only update for the other forms, not the one that raised the event
        if (sender != this)
            SetAllControlsFont();
    }

    public void SetAllControlsFont(ControlCollection ctrls = null)
    {
        if (ctrls == null)
            ctrls = this.Controls;

        //// rest of code .....
    }

【讨论】:

  • 感谢您的回答。这工作得很好,虽然,它不是很顺利,但它确实有效。随着 VS 文档大小的变化,我会尝试改进它以获得快速/流畅的结果。
【解决方案2】:

显然,您的设计具有“所有标签的唯一字体”的概念。每当您想到唯一时,您应该想到静态类或单例。两者都有其优点和缺点。你选择哪一个不在本题范围内

不管怎样,这个也是唯一的LabelFont,有一个改变字体的方法,一旦设置,就会引发一个事件。

class LabelFont
{
    private Font labelFont; // TODO: consider to initialize with a proper default value.
    public event EventHandler LabelFontChanged;

    protected virtual void OnLabelFontChanged()
    {
        this.LabelFontChanged?.Invoke(this, EventArgs.Empty);
    }

    public Font LabelFont
    {
        get => this.labelFont;
        set
        {
            this.labelFont = value;
            this.OnLabelFontChanged;
        }
    }
}

我们需要一个方法,输入一个控件序列,然后输出:这个控件序列加上所有子控件和孙控件等

public static IEnumerable<Control> ToDecendants(this IEnumerable<Control> controls)
{
    foreach (Control control in controls)
    {
        // first return this control
        yield return control

        // then return decendants of the child controls
        IEnumerable<Control> decendants = control.Controls
            .Cast<Control>();
            .ToDecendants();
        foreach (Control decendant in decendants)
        {
            foreach (Control decendant in decendants)
                yield return decendant;
        }
    }
}

也许您可以使用 LINQ 和 SelectMany 做一些事情。不要忘记添加控件本身,而不仅仅是子项(SelectMany 会这样做)

每当唯一的 LableFont 更改时,都需要通知每个 TemplateForm

class TemplateForm
{
    private static LabelFont LabelFont => ... // get either static, or singleton

    public TemplateForm()
    {
         ...

         // get notified when the LableFont changes
         LabelFont.LabelFontChanged += OnLabelFontChanged;
    }

    // TODO: Dispose: desubscribe from LabelFontChanged

    private void OnLableFontChanged(object sender, EventArgs e)
    {
        LabelFont labelFont = (LabelFont) sender; // if singleton, otherwise use static
        Font labelFont = labelFont.LabelFont;
        IEnumerable<Control> decendants = this.Controls.Cast<Control>
            .ToDecendants();

        foreach (Control decendant in decendants)
        {
             decendant.Font = labelFont;
        }
  }

或者如果你只想更新Label控件的字体:

IEnumerable<Control> labelDecendants= this.Controls
    .Cast<Control>
    .ToDecendants()
    .OfType<Label>();

【讨论】:

  • 它并不是真正的“所有标签中唯一的字体”,而是一个可以增加/减少的乘数。虽然,有些标签可以有不同的字体大小。
  • 即使您只想将字体设置为特定值:仍然存在每个人都有的字体。它是“当前”字体。我只是让它更通用。这是一个单独的过程,用于确定当前字体大小。如果以后你还想选择粗体,或者times new roman,或者其他的,其余的程序不会改变
  • 我明白了。您认为这种方法比 Ayoub_B 提出的解决方案更快吗?正如我在他的解决方案下评论的那样,这并不是很顺利,因为调整所有设置为“AutoSize = true”的标签、面板、组框等需要一些时间
猜你喜欢
  • 2012-05-14
  • 2016-10-15
  • 2018-02-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-12-06
  • 2012-08-21
  • 1970-01-01
相关资源
最近更新 更多