关于使用的一些指示和示例Regions https://learn.microsoft.com/en-us/dotnet/api/system.drawing.region定义表示非矩形形状的自定义控件的可见区域。
-
您正在将大多数浮点值转换为整数值:绘图时不要这样做,除非您没有其他直接选择。绘图需要浮点测量(float
) 大多数时候。始终产生正确的计算。
例如:围绕枢轴点重复旋转点 https://stackoverflow.com/a/50478311/7444103,看看差异。
-
您正在使用似乎是某种类型的固定措施,未在OP中定义并且显然从未修改过:DEFAULT_WIDTH
and DEFAULT_HEIGHT
。由于控件可以在设计时和运行时随时调整大小,因此使用固定度量并不是真正有用(假设这就是这些值所代表的内容)。无论如何,您需要使用控件的当前客户区作为主要参考:该值由Control.Client矩形 https://learn.microsoft.com/en-us/dotnet/api/system.windows.forms.control.clientrectangle财产。
-
控件的区域未在OnPaint()
覆盖,但要么在OnResize()
or OnLayout()
覆盖,具体取决于您正在构建的控件的功能。
设置属性,如FlatStyle = FlatStyle.Flat;
您在那里的控件(您是否从标签派生您的控件?)也不属于绘图过程:您可能会生成级联事件,导致控件不断重新绘制自身(直到它崩溃)。
-
使用 GraphicsPath,笔对齐 https://learn.microsoft.com/en-us/dotnet/api/system.drawing.pen.alignment属性并不完全有用。另请参阅文档中的备注部分。
当您设置控件的区域以修改其形状时,您需要考虑该区域不支持抗锯齿,因此您无法沿着它创建的边界进行绘制。你需要deflate绘图区域,因此您始终在您定义的区域内进行绘制。或者,您可以创建一个完全透明/半透明的控件,并在透明区域内绘制您需要的任何内容(形状和/或位图)。但这是另一回事,让我们坚持区域和图形路径 https://learn.microsoft.com/en-us/dotnet/api/system.drawing.drawing2d.graphicspath创造了它。
在示例代码中,GetRegionPath()
方法生成一个GraphicsPath对象,创建时传递PointF
定义形状的坐标,然后使用添加行() https://learn.microsoft.com/en-us/dotnet/api/system.drawing.drawing2d.graphicspath.addlines method.
此处显示的自定义控件使用设置样式() https://learn.microsoft.com/en-us/dotnet/api/system.windows.forms.control.setstyle在其构造函数中设置:
SetStyle(ControlStyles.OptimizedDoubleBuffer |
ControlStyles.AllPaintingInWmPaint |
ControlStyles.ResizeRedraw, true);
这将启用双缓冲并导致控件在调整大小时重新绘制。
然后该区域将在OnResize()
覆盖。
In OnPaint()
, the GetRegionPath()
再次调用方法以根据当前客户区获取 GraphicsPath 对象。
然后它使用一个简单的矩阵缩放 GraphicsPath:
(请参阅矩阵功能的描述翻转绘制文本/字符串的 GraphicsPath https://stackoverflow.com/a/53182901/7444103)
float scaleX = 1.0f - ((border * 2.5f) / rect.Width);
float scaleY = 1.0f - ((border * 2.0f) / rect.Height);
var mx = new Matrix(scaleX, 0, 0, scaleY, border, border);
[GraphicsPath].Transform(mx);
这会根据边框的大小(上面的值)缩放 GraphicsPath1.0f
放大,低于它的值缩小)。
然后它按照边界的尺寸向右和向下移动(平移)。
如果未设置 Border,则 GraphicsPath 不会缩放或moved: e.g.
1.0f + (((border * 2.5f) / rect.Width)) = 1.0f + 0.0f
这允许在区域内绘制形状及其边框(如果有)。
在这种情况下,可以应用抗锯齿并出现形状的边框smooth.
这就是它在设计时的样子:
在运行时:
也可以看看:
如何避免带有圆角的可缩放用户控件的彩色边框的视觉伪影? https://stackoverflow.com/a/54794097/7444103
如何绘制圆角矩形作为圆角表单的边框? https://stackoverflow.com/a/56533229/7444103
使用 PathGradientBrush 创建的阴影显示出不需要的刺结果 https://stackoverflow.com/a/56221869/7444103
自定义控制:
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Windows.Forms;
[ToolboxItem(true)]
[DesignerCategory("code")]
public class NavigationShape : Control
{
private Color m_ArrowColor = Color.SteelBlue;
private Color m_BorderColor = Color.Orange;
private float m_BorderSize = 1.5f;
private bool m_BorderVisible = true;
public NavigationShape() {
SetStyle(ControlStyles.OptimizedDoubleBuffer |
ControlStyles.AllPaintingInWmPaint |
ControlStyles.ResizeRedraw, true);
MinimumSize = new Size(40, 20);
}
protected override void OnResize(EventArgs e)
{
base.OnResize(e);
Region = new Region(GetRegionPath());
}
private GraphicsPath GetRegionPath()
{
// The arrow shape begins at 3/4 or the current width of the container
float arrowSection = ClientSize.Width * .75f;
PointF[] arrowPoints = new PointF[] {
new PointF (0, 0),
new PointF (arrowSection, 0),
new PointF(ClientSize.Width, ClientSize.Height / 2.0f),
new PointF (arrowSection, ClientSize.Height),
new PointF (0, ClientSize.Height),
new PointF (0, 0)
};
var path = new GraphicsPath();
path.AddLines(arrowPoints);
path.CloseFigure();
return path;
}
protected override void OnPaint(PaintEventArgs e)
{
e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
float border = m_BorderVisible ? m_BorderSize : .5f;
using (var path = GetRegionPath()) {
var rect = path.GetBounds();
float scaleX = 1.0f - ((border * 2.5f) / rect.Width);
float scaleY = 1.0f - ((border * 2.0f) / rect.Height);
using (var mx = new Matrix(scaleX, 0, 0, scaleY, border, border))
using (var brush = new SolidBrush(m_ArrowColor)) {
path.Transform(mx);
e.Graphics.FillPath(brush, path);
if (m_BorderVisible) {
using (Pen pen = new Pen(m_BorderColor, m_BorderSize)) {
e.Graphics.DrawPath(pen, path);
}
}
}
}
base.OnPaint(e);
}
[DefaultValue(typeof(Color), "SteelBlue")]
[Description("Color of the shape")]
public Color ArrowColor {
get => m_ArrowColor;
set {
if (m_ArrowColor != value) {
m_ArrowColor = value;
Invalidate();
}
}
}
[DefaultValue(true)]
[Description("Show or hide the Border")]
public bool BorderVisible {
get => m_BorderVisible;
set {
m_BorderVisible = value;
Invalidate();
}
}
[DefaultValue(typeof(Color), "Orange")]
[Description("Color of the Border")]
public Color BorderColor {
get => m_BorderColor;
set {
if (m_BorderColor != value) {
m_BorderColor = value;
Invalidate();
}
}
}
[DefaultValue(1.5f)]
[Description("Size of the Border [1.0, 6.0]")]
public float BorderSize {
get => m_BorderSize;
set {
if (m_BorderSize != value) {
m_BorderSize = Math.Max(Math.Min(value, 6.0f), 1.0f);
Invalidate();
}
}
}
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public BorderStyle BorderStyle{ get; set; } // Implement if needed
}