【问题标题】:C# WinForms - Custom borders of a programmatically created form only shows left and top borderC# WinForms - 以编程方式创建的表单的自定义边框仅显示左边框和上边框
【发布时间】:2023-03-17 00:26:01
【问题描述】:

尝试学习编程并进行了一些实验。遇到了一个我无法找到解决方案的小问题。我有一个以编程方式创建的按钮和一个以编程方式创建新表单的单击事件。该表单有一些创建的标签,以及一个自定义边框。主窗体上使用相同的自定义边框,即使在自动调整大小和动态创建的内容会改变它的大小时也能正常工作。但在这个以编程方式创建的表单上,边框仅显示在左侧和顶部,我无法找到答案或通过实验解决。

不确定多少代码就足够了,但我做了一个简化的程序来显示问题。它是从我的程序代码中简化而来的,该代码使用数百个以编程方式创建的按钮和几个信息数组,这些信息填充了 myForm 表单上使用的标签,所以有些事情可能看起来很奇怪,因为我删除了数组和按钮创建过程。

using System;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Windows.Forms;

namespace StackoverflowSample
{
public partial class Form1 : Form
{
    private Button myButton;

    /// <summary>
    /// Borrowed code snippet for custom border: http://stackoverflow.com/q/5092216. Would have preferred no to do dllimport yet, but will let it pass this time.
    /// </summary>
    /// <param name="nLeftRect"></param>
    /// <param name="nTopRect"></param>
    /// <param name="nRightRect"></param>
    /// <param name="nBottomRect"></param>
    /// <param name="nWidthEllipse"></param>
    /// <param name="nHeightEllipse"></param>
    /// <returns></returns>
    [DllImport("Gdi32.dll", EntryPoint = "CreateRoundRectRgn")]
    private static extern IntPtr CreateRoundRectRgn
    (
        int nLeftRect, // x-coordinate of upper-left corner
        int nTopRect, // y-coordinate of upper-left corner
        int nRightRect, // x-coordinate of lower-right corner
        int nBottomRect, // y-coordinate of lower-right corner
        int nWidthEllipse, // height of ellipse
        int nHeightEllipse // width of ellipse
     );

    public Form1()
    {
        InitializeComponent();

        this.BackColor = Color.White;
        this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None;

        Region = System.Drawing.Region.FromHrgn(CreateRoundRectRgn(0, 0, 0, 0, 0, 0)); // Since our form is dynamically created and/or filled and auto sized, set starting values here to 0.
        Region.MakeInfinite(); // This one was my own, to solve size problems with my dynamically created forms auto size, further reading required for other ways to do it.
    }

    private void Form1_Load(object sender, EventArgs e)
    {
        Font font = new System.Drawing.Font("Meiryo UI", 16.0f);

        myButton = new Button();
        myButton.Name = "tstButton";
        myButton.Size = new Size(50, 50);
        myButton.Location = new Point(10, 10);
        myButton.Font = font;
        myButton.Text = "Test Button";
        myButton.Click += (sender2, e2) => { ButtonClick(sender, e); };

        this.Controls.Add(myButton);
    }

    private void ButtonClick(object sender, EventArgs e)
    {
        Font font = new System.Drawing.Font("Meiryo UI", 16.0f);

        Button myButton = (sender as Button);

        Form myForm = new Form();
        Label row1Label = new Label();
        Label row2Label = new Label();
        Label row3Label = new Label();
        Label row4Label = new Label();
        Label row5Label = new Label();

        row2Label.AutoSize = true;
        row2Label.Text = "Row 1: ";
        row2Label.Font = font;
        row2Label.Location = new Point(5, 5);

        row1Label.AutoSize = true;
        row1Label.Text = "Row 2: ";
        row1Label.Font = font;
        row1Label.Location = new Point(5, 35);

        row3Label.AutoSize = true;
        row3Label.Text = "Row 3: ";
        row3Label.Font = font;
        row3Label.Location = new Point(5, 65);

        row4Label.AutoSize = true;
        row4Label.Text = "Row 4: ";
        row4Label.Font = font;
        row4Label.Location = new Point(5, 95);

        row5Label.AutoSize = true;
        row5Label.Text = "Row 5: ";
        row5Label.Font = font;
        row5Label.Location = new Point(5, 125);

        myForm.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink;
        myForm.AutoSize = true;
        myForm.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None;

        myForm.Controls.Add(row1Label);
        myForm.Controls.Add(row2Label);
        myForm.Controls.Add(row3Label);
        myForm.Controls.Add(row4Label);
        myForm.Controls.Add(row5Label);

        myForm.Font = font;
        myForm.BackColor = Color.White;
        myForm.Padding = new System.Windows.Forms.Padding(10, 10, 10, 10);
        myForm.Show();
        myForm.Paint += (sender4, e4) => { Form1_Paint(sender4, e4); }; // Hijacking the borrowed code for custom borders again for the info boxes.
        myForm.Location = new Point(Cursor.Position.X + 25, Cursor.Position.Y - 100); // Create info box a little to the right and up from the cursors position.
        myForm.LostFocus += (sender3, e3) => { CloseForm(sender3, e3, myForm); }; // If the info box loses focus, for example by clicking another button, close that box (form)
        myForm.MouseEnter += (sender3, e3) => { CloseForm(sender3, e3, myForm); }; // Also, if the mouse enters the box, also close, so we can show the buttons under it.
    }

    /// <summary>
    /// Event for closing my "custom" info boxes. Runs at LostFocus or MouseEnter events. So, clicking another button or entering the info box will close it.
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    /// <param name="myForm">Gets the relevant form that the event will run on. Solved my problem of targeting dynamically created forms from outside its scope.</param>
    private void CloseForm(object sender, EventArgs e, Form myForm)
    {
        myForm.Close();
    }

    /// <summary>
    /// Paint event. Holds some borrowed code for getting custom border. Works so far, but further reading required.
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    private void Form1_Paint(object sender, PaintEventArgs e)
    {
        // Borrowed code snippet for custom border: http://stackoverflow.com/q/5092216
        ControlPaint.DrawBorder(e.Graphics, this.ClientRectangle,
            Color.LightBlue, 5, ButtonBorderStyle.Solid,
            Color.LightBlue, 5, ButtonBorderStyle.Solid,
            Color.LightBlue, 5, ButtonBorderStyle.Solid,
            Color.LightBlue, 5, ButtonBorderStyle.Solid);
    }
}
}

我是编程初学者,所以要宽容一些,展示正确的方法,而不是谴责任何糟糕的编码。

亲切的问候,

马蒂亚斯

【问题讨论】:

  • 你能显示Form2_Paint(sender4, e4);事件的代码吗?您显示的代码中缺少它!
  • 对不起,当我试图从一个单独的项目运行它时才注意到它。它应该是 Form1_Paint,我已经在示例中对其进行了更正。此外,我必须再次创建 Load 和 Paint 事件才能使程序正确显示。当您获得一个带有浅蓝色边框且没有标题栏和一个按钮的主窗口时,您就知道它工作正常。单击该按钮将显示一个带有一些标签的类似窗口。

标签: c# winforms dynamic border customization


【解决方案1】:

问题是您对两种表单使用相同的事件处理程序。如果您想继续这种方式,您应该根据调用疼痛事件处理程序的表单更改ClientRectangle 的大小(现在您始终使用主表单的ClientSize)。将Form1_Paint事件处理器的代码改成这样:

private void Form1_Paint(object sender, PaintEventArgs e)
{
        Form frm = (Form)sender;
        ControlPaint.DrawBorder(e.Graphics, frm.ClientRectangle,
        Color.LightBlue, 5, ButtonBorderStyle.Solid,
        Color.LightBlue, 5, ButtonBorderStyle.Solid,
        Color.LightBlue, 5, ButtonBorderStyle.Solid,
        Color.LightBlue, 5, ButtonBorderStyle.Solid);
}

在您之前的代码中,您在子窗体重绘的情况下传递了主窗体的ClientRectangle。更改代码后,DrawBorder 方法将为调用它的表单获取正确的 ClientRectangle。

【讨论】:

  • 谢谢,完美!对 myForm 进行一些填充,这样标签就不会覆盖边框,并且可以按我的意愿工作!将填充添加到上面的代码中,但省略更正的代码,这样读者就不会感到困惑。我的代码将继续显示主要问题,您的帖子将显示正确的方法。
【解决方案2】:

davidovic 的方案会有一个副作用,当有控件停靠左/右/上/下时,你不能在这个区域绘画,因为你使用的是被控件覆盖的“ClientRectangle”。

重绘边框有两种正常的解决方案:

  1. 在表格下放另一个表格,但会有表格处理问题,可能导致内存不足

  2. 捕获非客户区窗口消息,重新绘制边框。但它很棘手,可能会导致很多问题,例如,当窗体最小化时,当窗体调整大小时,某些窗体区域被隐藏,你应该处理这个事件。

如果你想使用方法2,我建议你参考Lizard.dll,它仍然有bug,但比其他类型的库稳定得多。

【讨论】:

    猜你喜欢
    • 2020-12-02
    • 2023-03-23
    • 1970-01-01
    • 1970-01-01
    • 2013-07-03
    • 1970-01-01
    • 1970-01-01
    • 2011-08-17
    • 1970-01-01
    相关资源
    最近更新 更多