Probably a more flexible (and less hacky) solution will be recreating the ColorEditor
as mentioned by Jimi. I also may take a totally different approach for supporting theme colors, for example creating a theme extender provider component, or creating derived controls or other possible solutions.
Anyways, to support my previous post, specially where I mentioned 'You even can add another tab to the editor.', here I post a sample code to add another tab to the color editor, then you can see the custom tab (theme) with the custom colors that you need, like this:
It's a real screenshot ;) not exactly what you asked, but good enough as a sample code. For example if you need to use custom names for the colors, then you need more customization in DrawItem
(like what I did in the example), or rewriting the control from scratch.
And here's the code:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Design;
using System.Linq;
using System.Reflection;
using System.Windows.Forms;
public class CustomColorEditor : ColorEditor
{
public override object EditValue(ITypeDescriptorContext
context, System.IServiceProvider provider, object value)
{
//Get required types and methods
var ColorUIType = typeof(ColorEditor).GetNestedType("ColorUI",
BindingFlags.NonPublic | BindingFlags.Instance);
var ColorUiConstructor = ColorUIType.GetConstructors()[0];
var ColorEditorListBoxType = ColorUIType.GetNestedType("ColorEditorListBox",
BindingFlags.NonPublic | BindingFlags.Instance);
var ColorUiField = typeof(ColorEditor).GetField("colorUI",
BindingFlags.NonPublic | BindingFlags.Instance);
var OnListClickMethod = ColorUIType.GetMethod("OnListClick",
BindingFlags.NonPublic | BindingFlags.Instance);
var OnListDrawItemMethod = ColorUIType.GetMethod("OnListDrawItem",
BindingFlags.NonPublic | BindingFlags.Instance);
var OnListKeyDownMethod = ColorUIType.GetMethod("OnListKeyDown",
BindingFlags.NonPublic | BindingFlags.Instance);
//Color UI Control
var colorUi = (Control)ColorUiConstructor.Invoke(new[] { this });
ColorUiField.SetValue(this, colorUi);
//Custom colors ListBox
var listBox = (ListBox)Activator.CreateInstance(ColorEditorListBoxType);
//Colors
listBox.Items.AddRange(new object[] { Color.Red, Color.Green, Color.Blue });
listBox.DrawMode = DrawMode.OwnerDrawFixed;
listBox.BorderStyle = BorderStyle.FixedSingle;
listBox.IntegralHeight = false;
listBox.Sorted = false;
listBox.Click += (sender, e) =>
OnListClickMethod.Invoke(colorUi, new[] { sender, e });
//Custom paint
listBox.DrawItem += OnListDrawItem;
//Original paint
//listBox.DrawItem +=(sender, e) =>
// OnListDrawItemMethod.Invoke(colorUi, new[] { sender, e });
listBox.DrawItem += OnListDrawItem;
listBox.KeyDown += (sender, e) =>
OnListKeyDownMethod.Invoke(colorUi, new[] { sender, e });
listBox.Dock = DockStyle.Fill;
//Add the custom tab page, including the custome colors
var tabControl = colorUi.Controls.OfType<TabControl>().First();
var customTabPage = new TabPage();
customTabPage.Text = "Theme";
customTabPage.Controls.Add(listBox);
tabControl.TabPages.Add(customTabPage);
return base.EditValue(context, provider, value);
}
private void OnListDrawItem(object sender, DrawItemEventArgs e)
{
var colorNames = new Dictionary<int, string>
{
{Color.Red.ToArgb(), "Blood"},
{Color.Green.ToArgb(), "Life potion"},
{Color.Blue.ToArgb(), "Water"},
};
ListBox lb = (ListBox)sender;
object value = lb.Items[e.Index];
e.DrawBackground();
this.PaintValue(value, e.Graphics,
new Rectangle(e.Bounds.X + 2, e.Bounds.Y + 2, 22, e.Bounds.Height - 4));
e.Graphics.DrawRectangle(SystemPens.WindowText,
new Rectangle(e.Bounds.X + 2, e.Bounds.Y + 2, 22 - 1, e.Bounds.Height - 4 - 1));
var color = (Color)value;
var name = colorNames.ContainsKey(color.ToArgb()) ? colorNames[color.ToArgb()] : color.Name;
using (var foreBrush = new SolidBrush(e.ForeColor))
e.Graphics.DrawString(name, lb.Font, foreBrush, e.Bounds.X + 26, e.Bounds.Y);
}
}
public class MyControl : Control
{
[Editor(typeof(CustomColorEditor), typeof(UITypeEditor))]
public Color MyColor { get; set; }
}
Note: It's relying on implementation details of the color editor, for example, in .NET 5+, the member field name is "_colorUI".
dlg-4300.dlg
, retrieved from resources withAssembly.GetManifestResourceStream()
) -- You'd need to recreate the entire super-class and build your own ColorUI, there's no Service you can call for this. Probably better generate your own custom editor – StallingscustomColors
Field with a custom one and injecting in the Constructor of the ColorUI class a custom ColorEditor (let's say, the super-class, or the handler). Don't mind the clever approach to add an Editor Type to VS using a Package, you don't need that, because what you want to do is to parent a TabPage, to add it to the TabControl ofColorUI
, then handle this added interface. Not saying that it's impossible (what is?), but the amount of debugging (don't forget the COM dialog and the Hooking thing) is most probably not worth the effort – StallingsColorEditor
UITypeEditor entirely. Then, since you have your ownColorUI
Control to manage, you can add to it whatever you want. After that, register your UITypeEditor as usual. If you don't have theColorEditor
class (since it appears it's not been published in the .NET source code directly), take it from this PasteBin (if you're interested, let me know. Note that I'll delete this comment in a while) – Stallings