Try using Graphics.MeasureCharacter to find the position of a character and then draw the character in each color.
public class DualcoloredButton : Button
{
private int index = -1;
private string textDefault;
private string coloredText;
private Color coloredTextColor = Color.Red;
public DualcoloredButton()
{
base.Text = "";
}
public override string Text
{
get => "";
set => textDefault = value;
}
public string TextDefault
{
get => textDefault;
set
{
textDefault = value;
UpdateColoredText();
OnTextChanged(EventArgs.Empty);
}
}
public string ColoredText
{
get { return coloredText; }
set
{
coloredText = value;
UpdateColoredText();
}
}
public Color ColoredTextColor
{
get { return coloredTextColor; }
set
{
coloredTextColor = value;
UpdateColoredText();
}
}
private void UpdateColoredText()
{
if (!string.IsNullOrEmpty(coloredText) && !string.IsNullOrEmpty(textDefault))
{
string displayText = this.TextDefault.Replace("&", "");
string coloredText = this.coloredText.Replace("&", "");
index = displayText.IndexOf(coloredText);
}
else
{
index = -1;
}
Invalidate();
}
protected override void OnPaint(PaintEventArgs pevent)
{
StringFormat sf = new StringFormat();
sf.Alignment = StringAlignment.Center;
sf.LineAlignment = StringAlignment.Center;
sf.Trimming = StringTrimming.None;
sf.FormatFlags = StringFormatFlags.NoWrap | StringFormatFlags.NoClip;
base.OnPaint(pevent);
using (Brush brushColor = new SolidBrush(coloredTextColor))
using (Brush brushDefault = new SolidBrush(this.ForeColor))
{
string displayText = this.TextDefault?.Replace("&", "") ?? "";
string coloredText = this.coloredText?.Replace("&", "") ?? "";
if (string.IsNullOrWhiteSpace(displayText))
{
return;
}
int index = displayText.IndexOf(coloredText);
Brush[] brushes = new Brush[] { brushDefault, brushColor, brushDefault };
Part partA = new Part(brushDefault, 0, index);
Part partB = new Part(brushColor, index, coloredText.Length);
Part partC = new Part(brushDefault, index + coloredText.Length, displayText.Length - index - coloredText.Length);
Part[] parts = { partA, partB, partC };
CharacterRange[] cranges = parts.Select(_ => _.ToCharacterRange()).ToArray();
sf.SetMeasurableCharacterRanges(cranges);
var ranges = pevent.Graphics.MeasureCharacterRanges(displayText, this.Font, this.ClientRectangle, sf);
for (int i = 0; i < ranges.Length; i++)
{
RectangleF rect = ranges[i].GetBounds(pevent.Graphics);
if (rect.Width > 0 && rect.Height > 0)
{
Part part = parts[i];
string text = part.SubString(displayText);
pevent.Graphics.DrawString(text, Font, part.Brush, rect, sf);
}
}
int indexAmp = this.textDefault.IndexOf("&");
if (indexAmp < 0 || indexAmp == this.textDefault.Length - 1)
{
return;
}
if (Control.ModifierKeys == Keys.Alt || IsShowMnemonicUnderLine)
{
IsShowMnemonicUnderLine = true;
cranges = new[] { new CharacterRange(indexAmp, 1) };
sf.SetMeasurableCharacterRanges(cranges);
ranges = pevent.Graphics.MeasureCharacterRanges(displayText, this.Font, this.ClientRectangle, sf);
for (int i = 0; i < ranges.Length; i++)
{
RectangleF rect = ranges[i].GetBounds(pevent.Graphics);
if (rect.Width > 0 && rect.Height > 0)
{
pevent.Graphics.DrawLine(Pens.Black, rect.X, rect.Bottom, rect.Right, rect.Bottom);
}
}
}
}
}
public bool IsShowMnemonicUnderLine { get; set; }
protected override bool ProcessMnemonic(char charCode)
{
if (textDefault != null)
{
int index = textDefault.IndexOf("&");
if (0 <= index && index < textDefault.Length - 1)
{
if (char.ToLower(charCode) == char.ToLower(textDefault[index + 1]))
{
PerformClick();
return true;
}
}
}
return false;
}
class Part
{
public int Start;
public int Length;
public Brush Brush;
public Part(Brush brush, int start, int length)
{
Brush = brush;
Start = start;
Length = length;
}
public CharacterRange ToCharacterRange()
{
return new CharacterRange(Start, Length);
}
public string SubString(string text)
{
return text.Substring(Start, Length);
}
}
}
Edit: Add Mnemonic logic
Edit: Fix misalignment when TextDefault has Ampersand and coloredText has no Ampersand.
Edit: Add draw underline logic