c# - 通用选择颜色更改 WinForms 控件
问题描述
概述
我目前正在为我的应用程序开发一个主题系统,允许用户选择明暗主题(类似于视觉工作室)。到目前为止,整个过程非常简单。但是有一些事情我仍然有几个问题(嗯,更多的是想知道是否有更好的方法来做到这一点)。在开发过程中,我对更改支持它的控件的选择颜色(ListView
、ComboBox
、ListBox
、TextBox
、RichTextBox
等)进行了大量研究。我发现完成此任务的最直接方法(尤其是列表样式控件)是通过利用每个控件可用的事件来执行项目的自定义绘制。我知道它RichTextBox
具有SelectionBackColor
允许您更改它的属性选择颜色,但是该TextBox
控件不提供相同的属性,也没有我发现的任何类似属性。
使用该ListBox
控件,它不包含任何类型的选择颜色属性以供进一步自定义,这意味着我必须使用OwnerDraw
/DrawItem
事件DrawSubItem
来自定义绘制背景颜色,同时将HideSelection
属性设置为false
帮助在失去焦点时进行更改。
ComboBox
和ListBox
控件非常简单,可以通过使用事件并将其DrawItem
设置为类似控件来进行更改。OwnerDraw
ListBox
众所周知,有许多不同的控件支持用户选择,我可以继续进行我的研究,但我认为我提供的内容足以帮助描述我为什么在这里。
问题
由于通过单个类操作所有这些的代码非常繁琐,并且对未来的开发人员有限制(特别是如果他们正在执行这些控件中的项目的自定义绘制)。我想知道是否有一种简化的方法来操纵选择颜色。我已经在 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 中绘制项目所需的任务。
附加链接
解决方案
推荐阅读
- reactjs - 表单输入不允许在 React 表单上更改值
- sqlite - 在 SQLite 中查询分组平均值的平均值
- c# - 无法使用 C# 在 Jupyter Notebook 中绘制内联绘图图表
- c++ - C++ 类型转换/类型约定
- javascript - [Vue 警告]:无效的道具:道具“值”的类型检查失败。预期的数组,得到值为 1 的数字
- php - 使用斩波器发送请求时出现此错误:XMLHttpRequest 错误
- java - java 11 rmi NoClassDefFoundError
- python - 使用 Sympy 在 python 中求解方程
- html - 如何将一个屏幕分成多个相同大小的盒子,它们都朝外?
- html - 滚动后保持悬停状态