C# 使用透明背景绘制形状

C# 使用透明背景绘制形状,c#,winforms,gdi+,C#,Winforms,Gdi+,我有一个程序,有很多代表管道(油管)的直线。 这些线是用户控件,其中在每个控件的绘制事件中绘制线,垂直线的示例代码如下: e.Graphics.DrawLine(linePen, new Point(0, 0), new Point(0, this.Height)); 问题是,我想显示管道中机油的流向,因此需要添加一个箭头。 StartCap和EndCap在此不起作用,原因如下: 用户控件本身必须正好是线条(管道)的宽度,以使线条周围没有任何“死区”,这将在稍后与表单上的其他用户控件重叠。 如

我有一个程序,有很多代表管道(油管)的直线。 这些线是用户控件,其中在每个控件的绘制事件中绘制线,垂直线的示例代码如下:

e.Graphics.DrawLine(linePen, new Point(0, 0), new Point(0, this.Height));
问题是,我想显示管道中机油的流向,因此需要添加一个箭头。 StartCap和EndCap在此不起作用,原因如下:

用户控件本身必须正好是线条(管道)的宽度,以使线条周围没有任何“死区”,这将在稍后与表单上的其他用户控件重叠。 如果使用StartCap或EndCap,且线宽为2像素,则用户控件必须更宽,才能绘制箭头(StartCap或EndCap)

简单的方法是使“空白”区域透明,但在谷歌搜索了很长时间后,我放弃了;似乎没有一种可靠的方法可以通过用户控件实现这一点

然后我想我可以制作一个单独的用户控件,只绘制箭头,但是我仍然有一个问题,未绘制的区域覆盖了其他用户控件

是否有人建议如何:

  • 是否将未绘制的用户控制区域设置为透明
  • 实现上述目标的其他方法
由于我的“管道”只有2像素宽,因此无法在管线/管道内绘制任何内容:(


非常感谢您的任何建议/评论!

有一种方法可以使控件的背景在
winforms
中透明(相互重叠)。但是,在运行时移动控件可能会使其闪烁。另一种选择是使用
Region
指定控件的区域,使其在理论上具有任何形状。这是我可以为您做的,只是一个演示:

public partial class VerticalArrow : UserControl
{
    public VerticalArrow()
    {
        InitializeComponent();
        Direction = ArrowDirection.Up;                       
    }
    public enum ArrowDirection
    {
        Up,
        Down
    }
    ArrowDirection dir;
    public ArrowDirection Direction
    {
        get { return dir; }
        set
        {
            if (dir != value)
            {
                dir = value;
                UpdateRegion();
            }
        }
    }
    //default values of ArrowWidth and ArrowHeight
    int arrowWidth = 14;
    int arrowHeight = 18;
    public int ArrowWidth
    {
        get { return arrowWidth; }
        set
        {
            if (arrowWidth != value)
            {
                arrowWidth = value;
                UpdateRegion();                    
            }
        }
    }
    public int ArrowHeight
    {
        get { return arrowHeight; }
        set
        {
            if (arrowHeight != value)
            {
                arrowHeight = value;
                UpdateRegion();
            }
        }
    }
    //This will keep the size of the UserControl fixed at design time.
    protected override void SetBoundsCore(int x, int y, int width, int height, BoundsSpecified specified)
    {
        base.SetBoundsCore(x, y, Math.Max(ArrowWidth, 4), height, specified);
    }        
    private void UpdateRegion()
    {            
        GraphicsPath gp = new GraphicsPath();
        int dx = ArrowWidth / 2 - 1;
        int dy = ArrowHeight / 2;
        Point p1 = new Point(dx, Direction == ArrowDirection.Up ? dy : 1);
        Point p2 = new Point(ArrowWidth - dx, Direction == ArrowDirection.Up ? dy + 1: 1);
        Point p3 = new Point(ArrowWidth - dx, Direction == ArrowDirection.Up ? ClientSize.Height : ClientSize.Height - dy);
        Point p4 = new Point(dx, Direction == ArrowDirection.Up ? ClientSize.Height : ClientSize.Height - dy);
        Point q1 = Direction == ArrowDirection.Up ? new Point(0, ArrowHeight) : new Point(0, ClientSize.Height - ArrowHeight);
        Point q2 = Direction == ArrowDirection.Up ? new Point(dx, 0) : new Point(dx, ClientSize.Height);
        Point q3 = Direction == ArrowDirection.Up ? new Point(ArrowWidth, ArrowHeight) : new Point(ArrowWidth, ClientSize.Height - ArrowHeight);
        if (Direction == ArrowDirection.Up) gp.AddPolygon(new Point[] { p1, q1, q2, q3, p2, p3, p4 });
        else gp.AddPolygon(new Point[] {p1,p2,p3,q3,q2,q1,p4});
        Region = new Region(gp);
    }
    protected override void OnSizeChanged(EventArgs e)
    {
        UpdateRegion();
        base.OnSizeChanged(e);
    }
}
结果如下:

您可以使用
BackColor
更改箭头的颜色。如果我们只需要绘制箭头,代码会更简单,特别是借助
System.Drawing.Drawing2D.AdjustableArrowCap
和处理属性
CustomStartCap
CustomEndCap
。但是,根据您的要求,使用
R>在许多情况下,egion几乎是最佳选择

更新 如果您想要使用透明
背景
的解决方案,其中我们使用
画笔
CustomLineCap
而不是剪裁
区域
,则
垂直箭头
必须从
控件继承。以下是代码:

public class VerticalArrow : Control
{
    public VerticalArrow()
    {
        Width = 30;
        Height = 100;
        Direction = ArrowDirection.Up;
        ArrowHeight = 4;
        ArrowWidth = 4;
        TrunkWidth = 2;
        SetStyle(ControlStyles.Opaque, true);            
    }
    protected override CreateParams CreateParams
    {
        get
        {
            CreateParams cp = base.CreateParams;
            cp.ExStyle |= 0x20;
            return cp;
        }
    }
    public ArrowDirection Direction { get; set; }
    public int ArrowHeight { get; set; }
    public int ArrowWidth { get; set; }
    public int TrunkWidth { get; set; }
    Point p1, p2;
    public enum ArrowDirection
    {
        Up,
        Down,
        UpDown
    }
    protected override void OnSizeChanged(EventArgs e)
    {
        p1 = new Point(Width / 2, 0);
        p2 = new Point(Width / 2, Height);
        base.OnSizeChanged(e);
    }
    protected override void OnPaint(PaintEventArgs e)
    {
        base.OnPaint(e);
        using (Pen p = new Pen(ForeColor))
        {                
            using (AdjustableArrowCap cap = new AdjustableArrowCap(ArrowWidth, ArrowHeight))
            {
                if (Direction == ArrowDirection.Up || Direction == ArrowDirection.UpDown) p.CustomStartCap = cap;
                if (Direction == ArrowDirection.Down || Direction == ArrowDirection.UpDown) p.CustomEndCap = cap;
            }
            p.Width = TrunkWidth;
            e.Graphics.DrawLine(p, p1, p2);
        }
    }
}
截图:


要更改
箭头颜色
更改
前景色

有一种方法可以使控件的背景在
winforms中透明(彼此重叠)。但是,在运行时移动控件可能会使其闪烁。另一种选择是使用
Region
指定控件的区域,使其在理论上具有任何形状。这是我可以为您做的,只是一个演示:

public partial class VerticalArrow : UserControl
{
    public VerticalArrow()
    {
        InitializeComponent();
        Direction = ArrowDirection.Up;                       
    }
    public enum ArrowDirection
    {
        Up,
        Down
    }
    ArrowDirection dir;
    public ArrowDirection Direction
    {
        get { return dir; }
        set
        {
            if (dir != value)
            {
                dir = value;
                UpdateRegion();
            }
        }
    }
    //default values of ArrowWidth and ArrowHeight
    int arrowWidth = 14;
    int arrowHeight = 18;
    public int ArrowWidth
    {
        get { return arrowWidth; }
        set
        {
            if (arrowWidth != value)
            {
                arrowWidth = value;
                UpdateRegion();                    
            }
        }
    }
    public int ArrowHeight
    {
        get { return arrowHeight; }
        set
        {
            if (arrowHeight != value)
            {
                arrowHeight = value;
                UpdateRegion();
            }
        }
    }
    //This will keep the size of the UserControl fixed at design time.
    protected override void SetBoundsCore(int x, int y, int width, int height, BoundsSpecified specified)
    {
        base.SetBoundsCore(x, y, Math.Max(ArrowWidth, 4), height, specified);
    }        
    private void UpdateRegion()
    {            
        GraphicsPath gp = new GraphicsPath();
        int dx = ArrowWidth / 2 - 1;
        int dy = ArrowHeight / 2;
        Point p1 = new Point(dx, Direction == ArrowDirection.Up ? dy : 1);
        Point p2 = new Point(ArrowWidth - dx, Direction == ArrowDirection.Up ? dy + 1: 1);
        Point p3 = new Point(ArrowWidth - dx, Direction == ArrowDirection.Up ? ClientSize.Height : ClientSize.Height - dy);
        Point p4 = new Point(dx, Direction == ArrowDirection.Up ? ClientSize.Height : ClientSize.Height - dy);
        Point q1 = Direction == ArrowDirection.Up ? new Point(0, ArrowHeight) : new Point(0, ClientSize.Height - ArrowHeight);
        Point q2 = Direction == ArrowDirection.Up ? new Point(dx, 0) : new Point(dx, ClientSize.Height);
        Point q3 = Direction == ArrowDirection.Up ? new Point(ArrowWidth, ArrowHeight) : new Point(ArrowWidth, ClientSize.Height - ArrowHeight);
        if (Direction == ArrowDirection.Up) gp.AddPolygon(new Point[] { p1, q1, q2, q3, p2, p3, p4 });
        else gp.AddPolygon(new Point[] {p1,p2,p3,q3,q2,q1,p4});
        Region = new Region(gp);
    }
    protected override void OnSizeChanged(EventArgs e)
    {
        UpdateRegion();
        base.OnSizeChanged(e);
    }
}
结果如下:

您可以使用
BackColor
更改箭头的颜色。如果我们只需要绘制箭头,代码会更简单,特别是借助
System.Drawing.Drawing2D.AdjustableArrowCap
和处理属性
CustomStartCap
CustomEndCap
。但是,根据您的要求,使用
R>在许多情况下,egion几乎是最佳选择

更新 如果您想要使用透明
背景
的解决方案,其中我们使用
画笔
CustomLineCap
而不是剪裁
区域
,则
垂直箭头
必须从
控件继承。以下是代码:

public class VerticalArrow : Control
{
    public VerticalArrow()
    {
        Width = 30;
        Height = 100;
        Direction = ArrowDirection.Up;
        ArrowHeight = 4;
        ArrowWidth = 4;
        TrunkWidth = 2;
        SetStyle(ControlStyles.Opaque, true);            
    }
    protected override CreateParams CreateParams
    {
        get
        {
            CreateParams cp = base.CreateParams;
            cp.ExStyle |= 0x20;
            return cp;
        }
    }
    public ArrowDirection Direction { get; set; }
    public int ArrowHeight { get; set; }
    public int ArrowWidth { get; set; }
    public int TrunkWidth { get; set; }
    Point p1, p2;
    public enum ArrowDirection
    {
        Up,
        Down,
        UpDown
    }
    protected override void OnSizeChanged(EventArgs e)
    {
        p1 = new Point(Width / 2, 0);
        p2 = new Point(Width / 2, Height);
        base.OnSizeChanged(e);
    }
    protected override void OnPaint(PaintEventArgs e)
    {
        base.OnPaint(e);
        using (Pen p = new Pen(ForeColor))
        {                
            using (AdjustableArrowCap cap = new AdjustableArrowCap(ArrowWidth, ArrowHeight))
            {
                if (Direction == ArrowDirection.Up || Direction == ArrowDirection.UpDown) p.CustomStartCap = cap;
                if (Direction == ArrowDirection.Down || Direction == ArrowDirection.UpDown) p.CustomEndCap = cap;
            }
            p.Width = TrunkWidth;
            e.Graphics.DrawLine(p, p1, p2);
        }
    }
}
截图:


要更改
箭头颜色
请更改
前景色

您的线条宽度在
2
像素处最大?太细了。箭头的最大宽度如何?2或3或4…?这就是我在问题中试图解释的:哦,按enter键发表评论!问题是箭头需要用户把它画得更宽。我可以把它画得更宽,这样就可以画出箭头,线就在控制的中间,但是在线的每一边都有死区,没有画出来。如果线的每一个区域都是透明的,那就没问题。为什么你需要把每个箭头创建为<代码> UserControl ?您想在运行时移动它(通过按住鼠标并拖动…)?您的线条宽度在
2
像素处最大?太细了。箭头的最大宽度是多少?2、3或4…?这就是我在问题中试图解释的:哦,点击回车键发表评论!问题是箭头将要求用户控件更宽。我可以将其加宽,以便绘制箭头,然后lIN将保持在控件的中间,但是在行的每一边都有那些死区,没有任何东西被画出来。如果行的每一个区域都是透明的,那就没问题了。为什么需要创建每个箭头作为<代码> UserControl < /代码>?你想在运行时移动它(通过按住鼠标拖动……)?谢谢,我会尝试一下。我不需要在运行时移动它,只需要在设计时移动,所以闪烁不是问题。在这种情况下,您的建议是什么?太好了!这正是我在过去两周一直在搜索的!为什么需要以下行:e.Graphics.FillRectangle(brush.Transparent,ClientRectangle)?@JohnSaps抱歉,这不是必需的。我只是尝试了一下,但忘了删除它。谢谢,我会尝试一下。我不需要在运行时移动它,只需要在设计时,所以闪烁不是问题。那么,在这种情况下,您的建议是什么?太好了!这正是我在过去两周一直在搜索的内容!为什么会出现以下行需要:e.Graphics.FillRectangle(笔刷.Transparent,Cli