【问题标题】:How to create a User Control with rounded corners?如何创建带有圆角的用户控件?
【发布时间】:2016-01-04 09:56:58
【问题描述】:

我正在尝试使用圆角的用户控件。它没有固定大小,但宽度通常不会超过 120 像素。

我需要用户控件及其内容(标签和表格)具有圆角边缘并看起来像一个圆框。

我用过这段代码。

[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 static System.Drawing.Region GetRoundedRegion(int controlWidth, int controlHeight)
    {
            return System.Drawing.Region.FromHrgn(CreateRoundRectRgn(0, 0, controlWidth - 5, controlHeight - 5, 20, 20));
    } 

这会给控件带来圆角,但是在它运行了几次之后,我将多个用户控件添加到表单中,这将导致泄漏,我将在我的用户控件上获得带有红十字的白框。

有更好的方法吗?

【问题讨论】:

  • 我也有这个问题。相信我,放下 WinForms 并从 WPF 开始。自定义更好!
  • 你如何使用CreateRoundRectRgn?类似于this?那么你当然会有一个手柄泄漏。您想修复错误(然后在您使用它的地方发布代码)还是正在寻找替代方案?另一种方法可能是带有圆角图像的transparent 控件。
  • Sinatr,你说得对,这正是我这样做的方式,重新绘制表单时调用 GetRoundedRegion,不幸的是,我无法按照链接中的建议在 OnHandleCreated() 中调用此方法由于大小是高度可变的,并且在创建句柄时,控件大小是默认大小并且它有点随机。

标签: c# .net winforms custom-controls gdi+


【解决方案1】:

如果你想要真正的圆角而不只是透明的技巧,你可以使用这个例子:

private int radius = 20;
[DefaultValue(20)]
public int Radius
{
    get { return radius; }
    set
    {
        radius = value;
        this.RecreateRegion();
    }
}

[System.Runtime.InteropServices.DllImport("gdi32.dll")]
private static extern IntPtr CreateRoundRectRgn(int nLeftRect, int nTopRect,
    int nRightRect, int nBottomRect, int nWidthEllipse, int nHeightEllipse);

private GraphicsPath GetRoundRectagle(Rectangle bounds, int radius)
{
    float r = radius;
    GraphicsPath path = new GraphicsPath();
    path.StartFigure();
    path.AddArc(bounds.Left, bounds.Top, r, r, 180, 90);
    path.AddArc(bounds.Right - r, bounds.Top, r, r, 270, 90);
    path.AddArc(bounds.Right - r, bounds.Bottom - r, r, r, 0, 90);
    path.AddArc(bounds.Left, bounds.Bottom - r, r, r, 90, 90);
    path.CloseFigure();
    return path;
}

private void RecreateRegion()
{
    var bounds = ClientRectangle;

    //using (var path = GetRoundRectagle(bounds, this.Radius))
    //    this.Region = new Region(path);

    //Better round rectangle
    this.Region = Region.FromHrgn(CreateRoundRectRgn(bounds.Left, bounds.Top,
        bounds.Right, bounds.Bottom, Radius, radius));
    this.Invalidate();
}

protected override void OnSizeChanged(EventArgs e)
{
    base.OnSizeChanged(e);
    this.RecreateRegion();
}

截图如下:

这种方式与透明化的区别:

  • 设置圆形区域,控件有真正的圆角,你可以看到圆形部分后面是什么,尽管它是透明的,你会看到窗体的背景。
  • 设置圆形区域,当你点击移除的圆形部分时,点击穿过区域并到达后面,但如果你使用透明技巧点击透明区域将由控件处理。

您可以使用这两个选项中的任何一个。根据您的要求制作透明或设置区域。

下载

您可以在此处下载代码或克隆存储库:

【讨论】:

  • 如何给它添加边框?我需要类似于 FixedSingle BorderStyle 的东西,并在控件上设置了它,但是当上面的代码执行时,它似乎被删除了。
  • @Sam GetRoundRectagle 返回一个路径,您可以使用e.Graphics.DrawPath() 中的路径在路径周围绘制边框。请仔细阅读答案的最后两个注释。 (对于更平滑的边缘,您可以选择保持区域不变并仅使控件支持透明背景色,不同之处在于重叠某些控件时。为了更好地理解我的意思,请查看this post,您可能会发现它很有用)。
【解决方案2】:

我已经回答了我自己的问题。

根据 Sinatr 的评论,我发现我无法使用 OnHandleCreated,因为我需要在知道它的大小之前绘制对象。按照 Sinatr 提供的链接 GetRoundedRegion exception

所以我所做的是向我的 UserControl 添加一个 IntPtr 变量,该变量在每次使用句柄绘制时都在 CreateRoundRectRgn 方法上分配。在此触发之前,我使用 DeleteObject 删除旧句柄。

不是最佳解决方案,但目前看来工作正常。

其他建议虽然不错,但在我的情况下不起作用。

【讨论】:

  • 我发布的答案正常,你可以看截图。它使用 GDI+ :)
  • 您的方法适用于 Reza,但边角并不相同,并且我的控件的侧面有所缩小。一些摆弄会解决这个问题。将来当我有空闲时间时,我可能会回去实施您的解决方案,因为我认为这是一个更好的解决方案,因为现在我对我所拥有的感到满意。不过干杯:)
【解决方案3】:

只有当您想“点击”透明区域时,设置Region 才有意义。如果圆角不是那么大,而您只想让圆角在视觉上透明,您可以像 Button 一样做。

此解决方案的优点是您可以在此处拥有一个很好的抗锯齿圆角,而区域的边缘始终是锐利的。更不用说Region 实例持有非托管资源,应该以某种方式处置。

protected override void OnPaint(PaintEventArgs e)
{
    PaintTransparentBackground(this, e);
    // TODO: Paint your actual content here with rounded corners
}

private static void PaintTransparentBackground(Control c, PaintEventArgs e)
{
    if (c.Parent == null || !Application.RenderWithVisualStyles)
        return;

    ButtonRenderer.DrawParentBackground(e.Graphics, c.ClientRectangle, c);
}

【讨论】:

  • 不幸的是,当我尝试覆盖 Paint 方法时,它不会在我的用户控件上触发。
  • 我修复了示例,与Paint 事件不同,OnPaint 方法没有sender 参数。
猜你喜欢
  • 2011-04-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-11-03
  • 2023-03-19
  • 2010-09-19
相关资源
最近更新 更多