6

我知道我通常应该避免弄乱这样的系统设置,但是我的应用程序确实已经使用了非标准颜色,我对此没有影响。我希望能够在某些地方添加标准的 .NET 控件,但它们的颜色不匹配。我想要一个 hack 来替换这个应用程序的系统颜色。需要注意的另一件重要的事情是它是一个 .NET 应用程序。

到目前为止,我的(不完整的)想法是:

  • 用替换的 GetSysColor 创建一个代理 User32.dll 库,但这会非常繁琐(要重定向 731 个函数,要替换 1 个函数),而且我不知道如何强制我的应用程序使用该特定副本。
  • 以某种方式拦截对 GetSysColors 的调用(不幸的是,我认为它位于 CLR 中的某个位置)。
  • 以某种方式修改 .NET 类 SystemColors(在内存中?可能吗?)。

你有什么想法,实现这一目标的最佳(和完整)方法是什么?

4

3 回答 3

4

我希望能够在某些地方添加标准的 .NET 控件,但它们的颜色不匹配。我想要一个 hack 来替换这个应用程序的系统颜色。

这就像用大锤敲钉子一样。

您可以做的是从您要使用的每个库存控件中继承一个新控件,而不是在系统本身内弄脏颜色。因此,您可以从普通的 TextBox 控件继承来创建自己的ThemedTextBox. 您将这个新控件设置为默认使用您的应用程序的配色方案,并且因为就继承结构而言它仍然是一个文本框,您可以在任何使用普通文本框的地方使用它,包括在 winforms 设计器中。

于 2009-07-07T12:49:08.220 回答
2

在我早期,我开发了一个程序,它注册了一个全局消息挂钩来所有者绘制窗口边框——我可以为所有窗口设置主题。这对于单个应用程序也应该是可能的。然而,这不是一个简单的任务。

否则我认为这是不可能的。使用Krypton Toolkit等可主题化的第三方控件怎么样?

于 2009-07-07T12:26:52.940 回答
-1

System.Drawing.SystemColor值缓存在KnownColorsTable类中的私有数组colorTable中。随着使用 Win32 API 请求的系统颜色更改,数组的内容会被填充和更新。

要更改应用程序范围内的系统颜色,请colorTable根据您想要的值更改存储的颜色值。

然后你还需要为SystemBrushesSystemPens类清空缓存,因为钢笔和画笔不会自己重新读取 RBG 值。

using System.Drawing;
using System.Reflection;

namespace Example
{
    public class SystemColorsUtility
    {
        public SystemColorsUtility()
        {
            // force init color table
            byte unused = SystemColors.Window.R;

            var colorTableField = typeof(Color).Assembly.GetType("System.Drawing.KnownColorTable")
                .GetField("colorTable", BindingFlags.Static | BindingFlags.NonPublic);

            _colorTable = (int[]) colorTableField.GetValue(null);

            _threadDataProperty = systemDrawingAssembly.GetType("System.Drawing.SafeNativeMethods")
                .GetNestedType("Gdip", BindingFlags.NonPublic)
                .GetProperty("ThreadData", BindingFlags.Static | BindingFlags.NonPublic);

            SystemBrushesKey = typeof(SystemBrushes)
                .GetField("SystemBrushesKey", BindingFlags.Static | BindingFlags.NonPublic)
                .GetValue(null);

            SystemPensKey = typeof(SystemPens)
                .GetField("SystemPensKey", BindingFlags.Static | BindingFlags.NonPublic)
                .GetValue(null);
        }

        public void SetColor(KnownColor knownColor, Color value)
        {
            _colorTable[(int) knownColor] = value.ToArgb();
            ThreadData[SystemBrushesKey] = null;
            ThreadData[SystemPensKey] = null;
        }

        private int[] _colorTable;
        private object SystemBrushesKey { get; }
        private object SystemPensKey { get; }
    }
}

一个更完整的例子,说明我如何为我的爱好项目做到这一点。

应在加载应用程序之前更改系统颜色,以确保控件在初始化时看到更改的值。您将需要使用 Win32 API 直接呈现自身的控件的其他技巧,例如ListViewTreeView.

您可以在此处阅读详细信息。这是俄语的机器翻译。

于 2018-10-02T17:46:57.343 回答