首页 > 解决方案 > 通用选择颜色更改 WinForms 控件

问题描述

概述

我目前正在为我的应用程序开发一个主题系统,允许用户选择明暗主题(类似于视觉工作室)。到目前为止,整个过程非常简单。但是有一些事情我仍然有几个问题(嗯,更多的是想知道是否有更好的方法来做到这一点)。在开发过程中,我对更改支持它的控件的选择颜色(ListViewComboBoxListBoxTextBoxRichTextBox等)进行了大量研究。我发现完成此任务的最直接方法(尤其是列表样式控件)是通过利用每个控件可用的事件来执行项目的自定义绘制。我知道它RichTextBox具有SelectionBackColor允许您更改它的属性选择颜色,但是该TextBox控件不提供相同的属性,也没有我发现的任何类似属性。

使用该ListBox控件,它不包含任何类型的选择颜色属性以供进一步自定义,这意味着我必须使用OwnerDraw/DrawItem事件DrawSubItem来自定义绘制背景颜色,同时将HideSelection 属性设置为false帮助在失去焦点时进行更改。

ComboBoxListBox控件非常简单,可以通过使用事件并将其DrawItem设置为类似控件来进行更改。OwnerDrawListBox

众所周知,有许多不同的控件支持用户选择,我可以继续进行我的研究,但我认为我提供的内容足以帮助描述我为什么在这里。

问题

由于通过单个类操作所有这些的代码非常繁琐,并且对未来的开发人员有限制(特别是如果他们正在执行这些控件中的项目的自定义绘制)。我想知道是否有一种简化的方法来操纵选择颜色。我已经在 StackOverflow 上找到了一篇文章,DllImport用于操纵系统突出显示颜色;但是,对于如此简单的事情,在操作系统级别进行更改似乎并不明智,也不安全。我就像在 MIPS 汇编中盲目地操纵内核寄存器一样不愿意操纵系统代码。

我在 StackOverflow 上找到的帖子使用了:

[DllImport("user32.dll")]
static extern bool SetSysColors(int cElements, int[] lpaElements, uint[] lpaRgbValues);


void ChangeSelectColour(Color color) {
    const int COLOR_HIGHLIGHT = 13;
    const int COLOR_HIGHLIGHTTEXT = 14;
    // You will have to set the HighlightText colour if you want to change that as well.

    //array of elements to change
    int[] elements = { COLOR_HIGHLIGHT };

    List<uint> colours = new List<uint>();
    colours.Add((uint)ColorTranslator.ToWin32(color));

    //set the desktop color using p/invoke
    SetSysColors(elements.Length, elements, colours.ToArray());
}

这是这样做的唯一方法吗?如果是这样,我可以放弃在整个应用程序中更改选择颜色的想法,因为这似乎不太安全(除非有人可以证明不是这样)。

我当前的实现(概述部分中提到的硬方法)是这样的(只是一个小示例):

public static void ApplyStyles(Control c) {
    if (c is Button) {
        Button b = (Button)c;
        b.FlatStyle = FlatStyle.Flat;
        b.FlatAppearance.BorderSize = 0;
    }
    if (c is RoundedPanel || c is PictureBox) {
        // Do nothing.
    } else {
        if (c is Label) {
            if (c.Parent is RoundedPanel) {
                // Do nothing.
            } else {
                c.BackColor = BackColor;
                c.ForeColor = ForeColor;
            }
        } else {
            c.BackColor = BackColor;
            c.ForeColor = ForeColor;
        }

        if (c is ListView) {
            ListView lv = (ListView)c;

            // PSEUDO BEGIN
            lv.OwnerDraw = true;
            lv.DrawItem += DrawListViewItem;
            lv.DrawSubItem += DrawListViewSubItem;
            // PSEUDO END

            if (Style = Themes.Dark)
                lv.GridLines = false;

            foreach (ListViewItem lvi in lv.Items) {
                lvi.BackColor = BackColor;
                lvi.ForeColor = ForeColor;
            }
        }
    }
}

现在,如果您可以想象必须对支持用户选择的每种类型都这样做,那么您最终会在泛型类型上获得几个条件,然后创建自定义事件来绘制控件中包含的项目,这在您开始时会变得更加困难查看对图像、复选框等的支持。如果没有任何通用方法来更改选择颜色,则此代码可以迅速扩展,并且在我的情况下不值得付出努力(除非我将其设为非常低的优先级并偶尔进行处理这些年来)。

另外,请记住当您提出建议时;我在政府环境中工作,因此所有第三方产品都必须获得批准,就我而言,必须提供 100% 可靠的理由说明为什么我们需要该产品以供批准,因此第三方主题产品不会真正有帮助就我而言。

感谢大家花时间阅读本文,如果有人有任何想法,请随时告诉我!

笔记

如果您觉得我遗漏了任何需要的信息,或者如果我应该对某些事情更清楚,请随时发表评论并让我知道,以便我可以为未来的读者更新帖子的清晰度。

参考


RichTextBox.SelectionBackColor

用于SelectionBackColor获取或设置RichTextBox. 如果当前未选择任何文本,则该SelectionBackColor属性适用于插入符号的当前位置。从该位置输入的字符具有指定的SelectionBackColor.

组合框.DrawItem

此事件由 owner-drawn 使用ComboBox。您可以使用此事件来执行绘制项目所需的任务ComboBox

ListBox.DrawItem

此事件由所有者绘制的 ListBox 使用。该事件仅在 DrawMode 属性设置为 DrawMode.OwnerDrawFixed 或 DrawMode.OwnerDrawVariable 时引发。您可以使用此事件来执行在 ListBox 中绘制项目所需的任务。


附加链接


ComboBox.DrawItem 事件

ListBox.DrawItem 事件

标签: c#winformscontrolsthemesselection

解决方案


推荐阅读