设置圆形图片框边框颜色

Set CircularPictureBox Border Color

我正在创建自定义 PictureBox。

如你所见,这是一个专为个人资料照片设计的图片框

嗯,这是 CircularPictureBox

的 class
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace Hector.Framework.Controls
{
    public class CircularPictureBox : PictureBox
    {
        private Color _idlecolor = Color.White;

        public CircularPictureBox()
        {
            this.SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint | ControlStyles.DoubleBuffer, true);
            this.DoubleBuffered = true;

            this.BackColor = Color.White;
            this.SizeMode = PictureBoxSizeMode.StretchImage;
            this.Size = new Size(100, 100);
        }

        protected override void OnPaint(PaintEventArgs pe)
        {
            base.OnPaint(pe);

            using (var gpath = new GraphicsPath())
            {
                var brush = new SolidBrush(this.IdleBorderColor);
                var pen = new Pen(brush, 5);
                var outrect = new Rectangle(-1, -1, this.Width + 5, this.Height + 5);
                gpath.AddEllipse(outrect);
                pe.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
                pe.Graphics.DrawPath(pen, gpath);

                brush.Dispose();
                pen.Dispose();
                gpath.Dispose();
            }
        }

        protected override void OnResize(EventArgs e)
        {
            base.OnResize(e);

            using (var gpath = new GraphicsPath())
            {
                var rect = new Rectangle(1, 1, this.Width - 1, this.Height - 1);
                gpath.AddEllipse(rect);
                this.Region = new Region(gpath);
                gpath.Dispose();
            }
        }

        public Color IdleBorderColor
        {
            get => this._idlecolor;
            set => this._idlecolor = value;
        }
    }
}

我的问题是因为它是一个可以从设计器中使用的控件,所以我希望它具有边缘宽度或边框颜色等属性。

我开始测试颜色,但每当我改变颜色时, Visual Studio 向我显示一条错误消息,指出 属性 的值无效

我对您的代码进行了一些修改,以突出一些可用于自定义控件设计的功能。

我认为我所做的修改是self-explanatory。
但是,请看一下 OnPaint 事件。 e.Graphics.Clip 区域可让您隐藏所有不在所选区域中的图形部分。这意味着当您在设计模式下拖动控件时,图像将被剪裁并且不会在区域区域之外看到。

PixelOffsetMode.HighQuality and SmoothingMode.AntiAlias 有助于提高渲染的整体质量(有注释掉的选项在其他情况下很有用)。

Border 偏移量的计算必须参考 BorderSize 宽度,并相应地缩放。 Pen 对象从其大小的中间开始绘制。如果 Pen 的大小为 3 个像素,则在边框上绘制 1 个像素,一个在区域外,一个在区域内(很奇怪?也许吧)。

这里的透明度设置只是一个“假”。
它可能在其他情况下有效使用(应该读作“平台”)。

public class CircularPictureBox : PictureBox
{
    private Bitmap bitmap;
    private Color borderColor;
    private int penSize;
    private Color alphaColor = Color.FromArgb(0, 255,255,255);
    private bool enhancedBuffering;

    public CircularPictureBox()
    {
        InitializeComponent();
        this.SetStyle(ControlStyles.SupportsTransparentBackColor |
                      ControlStyles.ResizeRedraw |
                      ControlStyles.AllPaintingInWmPaint | 
                      ControlStyles.UserPaint | 
                      ControlStyles.OptimizedDoubleBuffer, true);
    }

    private void InitializeComponent()
    {
        this.enhancedBuffering = true;
        this.bitmap = null;
        this.borderColor = Color.Silver;
        this.penSize = 7;
        this.BackColor = alphaColor;
        this.SizeMode = PictureBoxSizeMode.StretchImage;
        this.Size = new Size(100, 100);
    }

    protected override void OnPaint(PaintEventArgs e)
    {
        e.Graphics.CompositingMode = CompositingMode.SourceOver;
        //e.Graphics.CompositingQuality = CompositingQuality.HighQuality;
        //e.Graphics.InterpolationMode = InterpolationMode.Bicubic;
        e.Graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
        e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;

        if (this.Region != null) e.Graphics.Clip = this.Region;
        var rect = this.ClientRectangle;
        if (bitmap != null) {
            e.Graphics.DrawImage(bitmap, rect);
        }

        rect.Inflate(-penSize / 2 + 1, -penSize / 2 + 1);
        using (var pen = new Pen(borderColor, penSize)) {
            e.Graphics.DrawEllipse(pen, rect);
        }
    }

    protected override void OnResize(EventArgs e)
    {
        using (var path = new GraphicsPath()) {
            path.AddEllipse(this.ClientRectangle);
            path.CloseFigure();
            using (Region region = new Region(path)) {
                this.Region = region.Clone();
            }
        }
    }

    [Description("Gets or Sets the Image displayed by the control"), Category("Appearance")]
    [EditorBrowsable(EditorBrowsableState.Always), Browsable(true)]
    public Bitmap Bitmap
    {
        get { return bitmap; }
        set { bitmap = value; Invalidate(); }
    }

    [Description("Gets or Sets the size of the Border"), Category("Behavior")]
    [EditorBrowsable(EditorBrowsableState.Always), Browsable(true)]
    public int BorderSize
    {
        get { return penSize; }
        set { penSize = value; Invalidate(); }
    }

    [Description("Gets or Sets the Color of Border drawn around the Image.")]
    [Category("Appearance")]
    [EditorBrowsable(EditorBrowsableState.Always), Browsable(true)]
    public Color BorderColor
    {
        get { return borderColor; }
        set { borderColor = value; Invalidate(); }
    }

    [Description("Enables or disables the control OptimizedDoubleBuffering feature")]
    [Category("Useful Features")] //<= "Useful feature" is a custom category
    [EditorBrowsable(EditorBrowsableState.Always), Browsable(true)]
    public bool EnhancedBuffering
    {
        get { return enhancedBuffering; }
        set { enhancedBuffering = value; 
              SetStyle(ControlStyles.OptimizedDoubleBuffer, value);  
              UpdateStyles();
        }
    }

    [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
    [EditorBrowsable(EditorBrowsableState.Never), Browsable(false)]
    public new Image ErrorImage
    {
        get { return null; }
        set { base.ErrorImage = null; }
    }

    [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
    [EditorBrowsable(EditorBrowsableState.Never), Browsable(false)]
    public new Image InitialImage
    {
        get { return null; }
        set { base.InitialImage = null; }
    }

    [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
    [EditorBrowsable(EditorBrowsableState.Never), Browsable(false)]
    public new Image BackgroundImage
    {
        get { return null; }
        set { base.BackgroundImage = null; }
    }

    [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
    [EditorBrowsable(EditorBrowsableState.Never), BrowsableAttribute(false)]
    public new Image Image {
        get { return null; }
        set { base.Image = null; } 
    }
}

一些 System.ComponentModel 有助于塑造控件的属性。

例如Description and Category属性:
(这些已插入您控件的自定义 属性 BorderColor)。

    [Description("Gets or Sets the Color of the Border drawn around the Image.")
    [Category("Appearance")]
    [EditorBrowsable(EditorBrowsableState.Always), Browsable(true)]

Description 当然是向用户解释 属性 的用途。
类别用于为属性提供 PropertyGrid 内的有机配置。您可以使用标准名称(AppearanceBehavior 等)或指定任何其他名称。
Category 一个自定义名称,当 Categorized 视图正在使用时,它将与其他名称一起列出。

自定义控件的 Image 属性 已被隐藏并替换为 Bitmap 属性:

    [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
    [EditorBrowsable(EditorBrowsableState.Never), BrowsableAttribute(false)]

EditorBrowsable Attribute 是对 Intellisense 的提示,它让您决定是否在弹出菜单中显示 属性 或方法。它可以是NeverAlwaysAdvanced(对于那些知道如何到达VS Options的人)。部署自定义控件(作为 dll)时属性和方法将被隐藏,而不是在您设计它时隐藏。

BrowsableAttribute 属性(或仅 [Browsable])允许指定 属性 是否应显示在 属性 网格中。

DesignerSerializationVisibility

With the DesignerSerializationVisibility Attribute, you can indicate whether the value for a property is Visible, and should be persisted in initialization code, Hidden, and should not be persisted in initialization code, or consists of Content, which should have initialization code generated for each public, not hidden property of the object assigned to the property.

也很有趣:
TypeConverter(typeof(System.ComponentModel.ExpandableObjectConverter))

使用此属性,您可以指示在 属性 网格中列出 Class 对象的 Public 属性。
此 Class 对象可以是一个内部 Class,它序列化一个复杂的 属性 控件。
TypeConverter Class本身就很有趣。