在自定义控件的中心绘制字符 – Font Awesome Glyph

我正在尝试构建自定义用户控件,它将在winforms Button中显示Font Awesome Glyphs。
我找到了类似控件的GitHub repo,但我想用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); } } 

我尝试使用这个问题的解决方案,但没有任何运气。

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

这是我控制的代码:

 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。

这是来自MSDN :

MeasureString方法设计用于单个字符串,并在字符串之前和之后包含少量额外空格以允许悬垂字形。 此外,DrawString方法调整字形点以优化显示质量,并且可能显示比MeasureString报告的字符串更窄的字符串。 要获取适合布局中相邻字符串的度量(例如,在实现格式化文本时),请使用MeasureCharacterRanges方法或采用StringFormat并传递GenericTypographic的MeasureString方法之一。 还要确保Graphics的TextRenderingHint是AntiAlias。

因此,要使MeasureString省略GenericTypographic中的所有额外空间传递,如下所示:

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

我觉得这很有帮助,虽然我没有像你那样精确地做到这一点。

我使用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());