【问题标题】:Set CircularPictureBox Border Color设置圆形 PictureBox 边框颜色
【发布时间】:2018-09-05 04:21:23
【问题描述】:

我正在创建一个自定义图片框。

如您所见,它是专为个人资料照片设计的 PictureBox

嗯,这是 CircularPictureBox 的类

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 向我显示一条错误消息,指出 属性的值无效

【问题讨论】:

  • 嗯,你输入的哪些值是无效的?
  • @ForeverZer0 在 Visual Studio 设计器中,由我的 IdleColor 创建的属性通过更改颜色向我显示此消息
  • 我可以看到一个讨厌的错误,不要在绘制事件中分配 Region 属性。副作用太多,它会导致油漆再次燃烧,你会看到 VS 燃烧 100% 核心。它属于 OnResize()。这也会炸毁你的代码,因为你忘记在笔和画笔上调用 Dispose(),显示在 5000 次绘画后结束,反过来导致 VS 变得非常不安。
  • 在调用 method_0 之前检查设计模式。 (LicenseManager.UsageMode == LicenseUsageMode.Designtime)
  • 首选不设置Region。如果将区域设置为圆形区域,您将看到图片框周围出现锯齿状边框,而您可以使用SetClip 到圆形路径来强制控件将自身绘制成圆形。然后对于透明效果,只需将颜色设置为Color.Transparent。要查看这两种情况的示例,请查看this post

标签: c# winforms graphics picturebox


【解决方案1】:

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

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

PixelOffsetMode.HighQualitySmoothingMode.AntiAlias 有助于渲染的整体质量(有注释掉的选项在其他情况下可能有用)。

Border offset 的计算必须参考 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 可以帮助塑造控件的属性。

例如,DescriptionCategory 属性:
(这些已被插入到您控件的自定义属性BorderColor 中)。

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

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

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

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

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

BrowsableAttribute 属性(或只是[Browsable])允许指定该属性是否应显示在 PropertyGrid 中。

DesignerSerializationVisibility

使用DesignerSerializationVisibility 属性,您可以指示 属性的值是否为Visible,并且应该被持久化 在初始化代码中,Hidden,不应持久化 初始化代码,或者由 Content 组成,应该有 为每个公共而不是隐藏属性生成的初始化代码 分配给属性的对象。

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

使用此属性,您可以指示在 PropertyGrid 中列出类对象的公共属性。
该类对象可以是序列化控件的复杂属性的内部类。
TypeConverter Class 本身就很有趣。

【讨论】:

  • 好吧,我复制了你的代码,但是在分配图像时,它根本没有显示
  • 如何分配图像?您是否注意到 Image 属性已禁用?您必须使用新的Bitmap 属性将图像分配给控件。将其放在窗体上并查看 PropertyGrid。
  • 哦,太好了,原来我是通过代码使用Image属性的,不过是的,是Bitmap,谢谢
  • 我修改它只是为了展示如何抑制默认属性并用自定义属性替换。我虽然你可以把这个类用作playground。您当然可以将其恢复为标准行为。不会影响结果。
  • 好吧,如果我确实知道通过使用关键字 new 可以隐藏成员来创建自定义成员,但我认为这对于我想做的事情没有必要
猜你喜欢
  • 1970-01-01
  • 2013-04-30
  • 2014-06-16
  • 1970-01-01
  • 2017-11-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多