【问题标题】:Draw character in center of custom control - Font Awesome Glyph在自定义控件中心绘制字符 - Font Awesome Glyph
【发布时间】:2014-11-11 13:08:18
【问题描述】:

我正在尝试构建自定义用户控件,该控件将在 winforms 按钮中显示 Font Awesome Glyphs。
我找到了具有类似控件的 GitHub 存储库,但我想使用 Button 作为控件的基础。
我可以显示字形,但无法正确对齐:

绿色虚线表示按钮大小,蓝线表示控制中心,红线表示graphics.MeasureString正在返回的矩形。

我的OnPaint 方法如下所示:

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

        var graphics = e.Graphics;
        // Set best quality
        graphics.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAliasGridFit;
        graphics.InterpolationMode = InterpolationMode.HighQualityBilinear;

        if(!DesignMode)
        {
            graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
            graphics.SmoothingMode = SmoothingMode.HighQuality;
        }

        var letter = char.ConvertFromUtf32((int)_icon);

        Brush b;
        if (!Enabled)
        {
            b = Brushes.LightGray;
        }
        else if (_mouseDown)
        {
            b = new SolidBrush(_clickColor);
        }
        else if (_mouseOver)
        {
            b = new SolidBrush(_hoverColor);
        }
        else
        {
            b = new SolidBrush(ForeColor);
        }

        SizeF s = graphics.MeasureString(letter, new Font(Fonts.Families[0], _iconSize, GraphicsUnit.Point), Width);

        float w = s.Width;
        float h = s.Height;

        // center icon
        float left = Padding.Left + (Width - w)/2;
        float top = Padding.Top + (Height - h)/2;

        if (DesignMode)
        {
            graphics.DrawRectangle(Pens.Red, top, left, w, h);
        }

        graphics.DrawString(letter, new Font(Fonts.Families[0], _iconSize, GraphicsUnit.Point), b, new PointF(left, top));

        if (DesignMode)
        {
            var pen = new Pen(_hoverColor, 1) { DashStyle = DashStyle.Dash, Alignment = PenAlignment.Inset };
            graphics.DrawRectangle(pen, Padding.Left, Padding.Top, Width - Padding.Left - Padding.Right - 1, Height - Padding.Top - Padding.Bottom - 1);
            graphics.DrawLine(Pens.Blue, Padding.Left, Padding.Top, Width - Padding.Left, Height - Padding.Top);
            graphics.DrawLine(Pens.Blue, Width - Padding.Left, Padding.Top, Padding.Left, Height - Padding.Top);
        }
    }

我尝试使用this question 的解决方案,但没有任何运气。

如何在我的控件精确中心绘制单个字符(字形)(控制中心和字形中心应对齐)

这是我的控件的代码:

using System;
using System.ComponentModel;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Text;
using System.Runtime.InteropServices;
using System.Windows.Forms;

namespace MyControls
{
    class FontButton:Button
    {
        #region Public

        public FontButton()
        {
            base.FlatStyle = FlatStyle.Flat;
            base.FlatAppearance.BorderSize = 0;
            base.FlatAppearance.MouseOverBackColor = Color.Transparent;
            base.FlatAppearance.MouseDownBackColor = Color.Transparent;
            base.Text = "";
            base.MinimumSize = new Size(32,32);
            Size = new Size(32,32);

            _hoverColor = Color.FromArgb(144, 188, 0);
            _clickColor = Color.Green;
            _icon=IconType.Android;
            _iconSize = 40;
        }

        private int _iconSize;

        [DefaultValue(typeof(int), "40"), Category("Appearance"), Description("Icon size in points")]
        public int IconSize
        {
            get { return _iconSize; }
            set
            {
                _iconSize = value;
                Invalidate();
            }
        }


        private Color _hoverColor;

        [DefaultValue(typeof(Color), "144, 188, 0"), Category("Appearance"), Description("Color when mouse over")]
        public Color HoverColor
        {
            get { return _hoverColor; }
            set
            {
                _hoverColor = value;
                Invalidate();
            }
        }

        private Color _clickColor;

        [DefaultValue(typeof(Color), "Green"), Category("Appearance"), Description("Color when mouse click")]
        public Color ClickColor
        {
            get { return _clickColor; }
            set
            {
                _clickColor = value;
                Invalidate();
            }
        }

        private IconType _icon;

        [DefaultValue(typeof(IconType), "Android"), Category("Appearance"), Description("Icon")]
        public IconType Icon
        {
            get { return _icon; }
            set
            {
                _icon = value;
                Invalidate();
            }
        }

        #endregion

        #region Static

        static FontButton()
        {
            InitialiseFont();
        }

        #region NATIVE

        [DllImport("gdi32.dll")]
        private static extern IntPtr AddFontMemResourceEx(byte[] pbFont, int cbFont, IntPtr pdv, out uint pcFonts);

        #endregion

        private static readonly PrivateFontCollection Fonts = new PrivateFontCollection();

        private static void InitialiseFont()
        {
            try
            {
                byte[] fontdata = ImageButtonTest.Properties.Resources.fontawesome_webfont;
                IntPtr ptrFont = Marshal.AllocCoTaskMem(fontdata.Length);
                uint cFonts;
                AddFontMemResourceEx(fontdata, fontdata.Length, IntPtr.Zero, out cFonts);
                Marshal.Copy(fontdata, 0, ptrFont, fontdata.Length);
                Fonts.AddMemoryFont(ptrFont, fontdata.Length);
                Marshal.FreeCoTaskMem(ptrFont);
            }
            catch (Exception)
            {
                Debug.WriteLine("Error");
                throw;
            }
        }

        #endregion

        #region Overrides

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

            var graphics = e.Graphics;
            // Set best quality
            graphics.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAliasGridFit;
            graphics.InterpolationMode = InterpolationMode.HighQualityBilinear;


            if(!DesignMode)
            {
                graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
                graphics.SmoothingMode = SmoothingMode.HighQuality;
            }

            var letter = char.ConvertFromUtf32((int)_icon);//char.ConvertFromUtf32(0xf190);

            Brush b;
            if (!Enabled)
            {
                b = Brushes.LightGray;
            }
            else if (_mouseDown)
            {
                b = new SolidBrush(_clickColor);
            }
            else if (_mouseOver)
            {
                b = new SolidBrush(_hoverColor);
            }
            else
            {
                b = new SolidBrush(ForeColor);
            }

            SizeF s = graphics.MeasureString(letter, new Font(Fonts.Families[0], _iconSize, GraphicsUnit.Point), Width);

            //SizeF s = TextRenderer.MeasureText(letter, new Font(Fonts.Families[0], _iconSize, GraphicsUnit.Point), new Size(20, 20), TextFormatFlags.NoPadding);

            //Debug.WriteLine(stringSize);
            //Debug.WriteLine(s);

            float w = s.Width;
            float h = s.Height;

            // center icon
            float left = Padding.Left + (Width - w)/2;
            float top = Padding.Top + (Height - h)/2;

            if (DesignMode)
            {
                graphics.DrawRectangle(Pens.Red, top, left, w, h);
            }

            graphics.DrawString(letter, new Font(Fonts.Families[0], _iconSize, GraphicsUnit.Point), b, new PointF(left, top));

            if (DesignMode)//Process.GetCurrentProcess().ProcessName == "devenv")
            {
                var pen = new Pen(_hoverColor, 1) { DashStyle = DashStyle.Dash, Alignment = PenAlignment.Inset };
                graphics.DrawRectangle(pen, Padding.Left, Padding.Top, Width - Padding.Left - Padding.Right - 1, Height - Padding.Top - Padding.Bottom - 1);
                graphics.DrawLine(Pens.Blue, Padding.Left, Padding.Top, Width - Padding.Left, Height - Padding.Top);
                graphics.DrawLine(Pens.Blue, Width - Padding.Left, Padding.Top, Padding.Left, Height - Padding.Top);
            }
        }

        private bool _mouseOver;

        protected override void OnMouseEnter(EventArgs e)
        {
            _mouseOver = true;
            base.OnMouseEnter(e);

        }

        protected override void OnMouseLeave(EventArgs e)
        {
            _mouseOver = false;
            base.OnMouseLeave(e);
        }

        private bool _mouseDown;

        protected override void OnMouseDown(MouseEventArgs e)
        {
            _mouseDown = true;
            base.OnMouseDown(e);
        }

        protected override void OnMouseUp(MouseEventArgs e)
        {
            _mouseDown = false;
            base.OnMouseUp(e);
        }


        [EditorBrowsable(EditorBrowsableState.Never), Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public new FlatStyle FlatStyle { get; set; }

        [EditorBrowsable(EditorBrowsableState.Never), Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public new FlatButtonAppearance FlatAppearance { get; set; }

        [EditorBrowsable(EditorBrowsableState.Never), Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public new string Text { get; set; }

        [EditorBrowsable(EditorBrowsableState.Never), Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public new Size MinimumSize { get; set; }

        #endregion
    }
}

IconType 枚举:

namespace MyControls
{
    public enum IconType
    {
        Adjust = 0xf042,
        Adn = 0xf170,
        AlignCenter = 0xf037,
        AlignJustify = 0xf039,
        AlignLeft = 0xf036,
        AlignRight = 0xf038,
        Ambulance = 0xf0f9,
        Anchor = 0xf13d,
        Android = 0xf17b,
        ArrowCircleDown = 0xf0ab,
        ArrowCircleLeft = 0xf0a8,
        ArrowCircleODown = 0xf01a,
        ArrowCircleOLeft = 0xf190,
        ArrowCircleORight = 0xf18e,
        ArrowCircleOUp = 0xf01b,
        ArrowCircleRight = 0xf0a9,
        ArrowCircleUp = 0xf0aa,
        ArrowDown = 0xf063,
        ArrowLeft = 0xf060,
        ArrowRight = 0xf061,
        ArrowUp = 0xf062,
        Arrows = 0xf047,
        ArrowsAlt = 0xf0b2,
        ArrowsH = 0xf07e,
        ArrowsV = 0xf07d,
        User = 0xf007,
        UserMd = 0xf0f0,
        Users = 0xf0c0,
        Stop = 0xf04d
    }
}

它只需要在资源中包含 fontawesome_webfont.ttf。

【问题讨论】:

    标签: c# winforms user-controls


    【解决方案1】:

    我使用GraphicsPath 得到了最好的结果:

    var path = new GraphicsPath()
    path.AddString(_letter, font.FontFamily, (int) font.Style, font.Size, new Point(0, 0), StringFormat.GenericTypographic);
    Rectangle area = Rectangle.Round(path.GetBounds());
    

    【讨论】:

      【解决方案2】:

      这是来自MSDN

      MeasureString 方法设计用于单个字符串 并在字符串前后包含少量额外空格 允许悬垂的字形。此外,DrawString 方法调整 字形点以优化显示质量并可能显示字符串 比 MeasureString 报告的要窄。获取合适的指标 对于布局中的相邻字符串(例如,在实现 格式化文本),使用 MeasureCharacterRanges 方法或其中一种 MeasureString 方法采用 StringFormat 并通过 通用排版。还要确保图形的 TextRenderingHint 是抗锯齿。

      所以要使MeasureString 省略GenericTypographic 中的所有额外空间,如下所示:

      SizeF s = graphics.MeasureString(letter, new Font(Fonts.Families[0], 
                _iconSize, GraphicsUnit.Point), Width, StringFormat.GenericTypographic);
      

      我发现这很有帮助,虽然我不必像你那样精确地做到这一点..

      【讨论】:

      • 我可以将 StringFormat 作为最后一个参数传递 - msdn.microsoft.com/en-us/library/ms142108(v=vs.110).aspx 我正在使用 WinForms 和 .NET 4.5,如果这有什么不同的话。
      • 哎呀,错字了。是的,应该是StringFormat.GenericTypographic
      • 这并没有太大帮助。我设法使用这个答案获得了最佳结果:stackoverflow.com/a/8436484/965722,我现在必须做的是动态更改字体大小以适应按钮内的字形
      • 哇,这是一个非常酷的主意!很高兴现在把这个技巧放在我的包里 - GraphicsPath!感谢您向我指出这个答案!
      猜你喜欢
      • 2014-11-22
      • 2015-08-27
      • 1970-01-01
      • 1970-01-01
      • 2022-12-15
      • 2016-06-06
      • 2017-04-09
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多