更改 PPC 控件的颜色






4.63/5 (10投票s)
如何更改 Windows CE 组件的颜色而不修改您的程序。
引言
本文旨在向您展示如何以简单的方式更改 Windows CE 设备组件的颜色。例如,如果您想为按钮显示红色背景,您会怎么做?很可能,您需要捕获 WM_PAINT
消息并自己完成所有工作。那么更改标题栏中显示的文本颜色呢?您将不得不重绘非客户端区域。
此外,假设您的客户希望您在系统中创建主题。也就是说,对于主题 X,我希望窗口具有粉色背景,按钮具有红色背景,静态文本为白色,菜单为浅蓝色背景。但这还不是全部:下个月,客户会想要另一个主题。
事实上,上一段描述了我不久前的处境。该怎么办?重写应用程序、捕获 WM_PAINT
并为每个控件重绘都不是一个可行的选择。幸运的是,经过一番研究,我找到了一个解决方案。找到它之后,我决定创建一个允许我轻松更改系统颜色的应用程序。
因此,本文的目的是展示如何更改系统颜色,并展示该应用程序如何工作以及如何使用它。
背景
Windows CE 有许多可配置项。我们感兴趣的系统颜色位于注册表中,路径如下:
HKEY_LOCAL_MACHINE\System\GWE\SysColor
此注册表项是一个 116 字节的二进制缓冲区。每种系统颜色使用四个字节:一个字节用于红色,一个字节用于绿色,一个字节用于蓝色;第四个字节始终为 0,尽管有人认为对于 Windows CE 的后续版本,该字节将用作 alpha 颜色。
因此,有二十九种系统组件的颜色可以更改(116/4 = 29)。以下是这些组件的列表。
注册表顺序 | 系统颜色 | 描述 |
---|---|---|
0 | COLOR_SCROLLBAR |
滚动条灰色区域的颜色。 |
1 | COLOR_BACKGROUND |
桌面窗口的背景颜色。 |
2 | COLOR_ACTIVECAPTION |
活动窗口标题栏的颜色。 |
3 | COLOR_INACTIVECAPTION |
非活动窗口标题栏的颜色。 |
4 | COLOR_MENU |
菜单背景颜色。 |
5 | COLOR_WINDOW |
窗口背景颜色。 |
6 | COLOR_WINDOWFRAME |
窗口边框的颜色。 |
7 | COLOR_MENUTEXT |
菜单中文本的颜色。 |
8 | COLOR_WINDOWTEXT |
窗口中文本的颜色。 |
9 | COLOR_CAPTIONTEXT |
标题栏、尺寸框和滚动条箭头框中文本的颜色。 |
10 | COLOR_ACTIVEBORDER |
活动窗口边框的颜色。 |
11 | COLOR_INACTIVEBORDER |
非活动窗口边框的颜色。 |
12 | COLOR_APPWORKSPACE |
多文档界面 (MDI) 应用程序的背景颜色。 |
13 | COLOR_HIGHLIGHT |
控件中选定项的颜色。 |
14 | COLOR_HIGHLIGHTTEXT |
控件中选定项文本的颜色。 |
15 | COLOR_BTNFACE |
按钮面的颜色。 |
16 | COLOR_BTNSHADOW |
按钮的阴影颜色,用于远离光源的边缘。 |
17 | COLOR_GRAYTEXT |
灰色文本的颜色。如果当前显示驱动程序不支持纯灰色,则此颜色设置为 0。 |
18 | COLOR_BTNTEXT |
按钮文本的颜色。 |
19 | COLOR_INACTIVECAPTIONTEXT |
非活动窗口标题栏中文本的颜色。 |
20 | COLOR_BTNHIGHLIGHT |
按钮的突出显示颜色,用于朝向光源的边缘。 |
21 | COLOR_3DDKSHADOW |
三维显示元素的深色阴影。 |
22 | COLOR_3DLIGHT |
三维显示元素的突出显示颜色,用于朝向光源的边缘。 |
23 | COLOR_INFOTEXT |
工具提示控件文本的颜色。 |
24 | COLOR_INFOBK |
工具提示控件的背景颜色。 |
25 | COLOR_STATIC |
静态控件和对话框的背景颜色。支持 Windows CE 2.0 及更高版本。 |
26 | COLOR_STATICTEXT |
静态控件文本的颜色。支持 Windows CE 2.0 及更高版本。 |
27 | COLOR_GRADIENTACTIVECAPTION |
用颜色渐变填充的活动窗口标题栏的颜色。 |
28 | COLOR_GRADIENTINACTIVECAPTION |
用颜色渐变填充的非活动窗口标题栏的颜色。 |
因此,要定位需要更改的字节,只需将注册表顺序乘以四。例如,要将菜单的背景颜色修改为红色,您需要将第 16 个字节更改为 FF,第 17 个字节更改为 00,第 18 个字节也更改为 00。更新注册表并执行软重置。注意:需要软重置(也称为温启动),因为这些属性仅在系统启动时读取。
如您所见,原理有些简单。因此,程序需要做的就是获取缓冲区,修改它,更新注册表,然后软重置设备。或者,您可以像我一样,修改注册表并创建一个更新注册表的 CAB 文件。无论哪种方式,您都需要一种方法来修改注册表,所以我制作了一个在移动设备上运行并更新注册表的小应用程序。
PPC 颜色配置器
这是我制作的应用程序;您将在本文开头找到源代码。我在 Symbol MC50、iPaq 和 Intermec 730 机器的 Windows CE .NET (4.2) 上对其进行了测试。
该应用程序是基于对话框的。这是主对话框类的声明
class CPPCColorConfigDlg : public CDialog { public: CPPCColorConfigDlg(CWnd* pParent = NULL); // standard constructor enum { IDD = IDD_PPCCOLORCONFIG_DIALOG }; protected: HICON m_hIcon; virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support virtual BOOL OnInitDialog(); virtual void OnSelectOption(); virtual void OnColorChange(); virtual void OnPaint(); virtual void OnUpdateSettings(); private: void FillColorOptions(); void InitVectors(); void DisplayDescription(); void UpdateRegistry(); void ResetRegistry(); void ParseItemColors(int* pRed, int* pGreen, int* pBlue); void SetColorsInBoxes(int iRed, int iGreen, int iBlue); int m_iSelected; int m_iRed; int m_iGreen; int m_iBlue; BYTE m_pNewBuffer<BUFFER_SIZE>; BYTE m_pOldBuffer<BUFFER_SIZE>; DECLARE_MESSAGE_MAP() };
成员变量 m_iSelected
存储所选属性(例如,菜单背景颜色)的索引,并在 OnSelectOption
方法中更改。属性 m_iRed
、m_iGreen
和 m_iBlue
存储红色、绿色和蓝色文本框的值,它们的值将决定组件的颜色。最后,属性 m_pNewBuffer
和 m_pOldBuffer
存储将从注册表中获取的内存缓冲区,其值最终也将被更新到注册表中。宏 BUFFER_SIZE
定义为 116。
InitVectors
方法初始化内存缓冲区,从注册表中获取值。这是代码
void CPPCColorConfigDlg::InitVectors() { HKEY hKey; DWORD dwType; DWORD dwSize; dwType = 0; dwSize = BUFFER_SIZE; memset(m_pNewBuffer, 0, BUFFER_SIZE); memset(m_pOldBuffer, 0, BUFFER_SIZE); ::RegOpenKeyEx(HKEY_LOCAL_MACHINE, _T("SYSTEM\\GWE"), NULL, NULL, &hKey); ::RegQueryValueEx(hKey, _T("SysColor"), NULL, &dwType, m_pOldBuffer, &dwSize); ::RegQueryValueEx(hKey, _T("SysColor"), NULL, &dwType, m_pNewBuffer, &dwSize); ::RegCloseKey(hKey); ASSERT(!memcmp(m_pOldBuffer, m_pNewBuffer, BUFFER_SIZE)); }
我创建了一个全局变量 g_vtrShowStrings
,它是一个二维矩阵,包含组合框中显示的字符串及其描述。矩阵中的位置将决定要修改其字节的缓冲区中的位置。宏 OPTION_VECTOR_SIZE
定义为 29。
CString g_vtrShowStrings[OPTION_VECTOR_SIZE][2] = { { CString("Scrollbar"), CString("Color of the gray area of a scroll bar") }, { CString("Background"), CString("Background color of the desktop window") }, { CString("Active Caption"), CString("Color of the title bar of an active window") }, ... };
为了记录,我将展示消息映射。如您从图片中注意到的, there are some controls: a combo box that holds the components of the system that can be updated, four text boxes whose purpose is to allow the user to change the color of the component (the first one is for red, the second is for green, and the third is for the blue color; the fourth is unused, and it will be for the alpha, when WinCE admits that parameter). There is also a button, that -when clicked- will update the registry through the memory buffer. Here is the message map
BEGIN_MESSAGE_MAP(CPPCColorConfigDlg, CDialog) ON_WM_PAINT() ON_CBN_SELCHANGE(IDC_CMB_COMPONENTS, OnSelectOption) ON_EN_CHANGE(IDC_TXT_RED, OnColorChange) ON_EN_CHANGE(IDC_TXT_GREEN, OnColorChange) ON_EN_CHANGE(IDC_TXT_BLUE, OnColorChange) ON_BN_CLICKED(IDC_CMD_UPDATE, OnUpdateSettings) END_MESSAGE_MAP()
接下来是 OnInitDialog
。在此对话框中,我们执行两项任务:根据 g_vtrShowStrings
矩阵初始化组合框(组合框的“已排序”属性在对话框资源编辑器中未选中),并选择组合框的第一个项目。该方法如下所示
BOOL CPPCColorConfigDlg::OnInitDialog() { CDialog::OnInitDialog(); SetIcon(m_hIcon, TRUE); SetIcon(m_hIcon, FALSE); CenterWindow(GetDesktopWindow()); FillColorOptions(); SetDlgItemText(IDC_TXT_OTHER, _T("0")); SetDlgItemText(IDC_TXT_RED, _T("0")); SetDlgItemText(IDC_TXT_GREEN, _T("0")); SetDlgItemText(IDC_TXT_BLUE, _T("0")); OnColorChange(); CComboBox* pColorOptions; pColorOptions = reinterpret_cast<CComboBox*>(GetDlgItem(IDC_CMB_COMPONENTS)); pColorOptions->SetCurSel(0); return TRUE; }
FillColorOptions
方法将填充组合框。它将遍历全局矩阵的第一维。这是代码
void CPPCColorConfigDlg::FillColorOptions() { CComboBox* pColorOptions; pColorOptions = reinterpret_cast<CComboBox*>(GetDlgItem(IDC_CMB_COMPONENTS)); pColorOptions->Clear(); for (int i = 0; i < OPTION_VECTOR_SIZE; i++) { pColorOptions->AddString(g_vtrShowStrings[i][0]); } }
如前所述,m_iSelected
存储将要更改的矩阵的位置。当更改组合框的索引时,此成员将被更新。这是响应此类事件的代码
void CPPCColorConfigDlg::OnSelectOption() { CComboBox* pColorOptions; int iRed, iGreen, iBlue; pColorOptions = reinterpret_cast<CComboBox*>(GetDlgItem(IDC_CMB_COMPONENTS)); m_iSelected = pColorOptions->GetCurSel(); // fill the current text boxes with the current color iRed = iGreen = iBlue = 0; ParseItemColors(&iRed, &iGreen, &iBlue); SetColorsInBoxes(iRed, iGreen, iBlue); DisplayDescription(); }
此方法执行两个操作。首先,更新 m_iSelected
成员。其次,它还将根据缓冲区中的值更新文本框的值。ParseItemColors
方法从内存缓冲区获取三个颜色。SetColorsInBoxes
方法更新文本框的值。这是代码
void CPPCColorConfigDlg::ParseItemColors(int* pRed, int* pGreen, int* pBlue) { int iPosition; iPosition = m_iSelected * 4; memcpy(pRed, m_pNewBuffer + iPosition++, 1); memcpy(pGreen, m_pNewBuffer + iPosition++, 1); memcpy(pBlue, m_pNewBuffer + iPosition++, 1); } void CPPCColorConfigDlg::SetColorsInBoxes(int iRed, int iGreen, int iBlue) { CString strRed, strGreen, strBlue; strRed.Format(_T("%d"), iRed); strGreen.Format(_T("%d"), iGreen); strBlue.Format(_T("%d"), iBlue); SetDlgItemText(IDC_TXT_RED, strRed); SetDlgItemText(IDC_TXT_GREEN, strGreen); SetDlgItemText(IDC_TXT_BLUE, strBlue); }
请注意,在 ParseItemColors
中,我们使用 m_iSelected
来确定内存缓冲区中的位置。
当从组合框中选择一个项目时,它还将调用 DisplayDescription
方法,该方法将显示有关系统组件的简要描述。它从全局矩阵中获取该值。
void CPPCColorConfigDlg::DisplayDescription() { CStatic* pDescription; pDescription = reinterpret_cast<CStatic*>(GetDlgItem(IDC_LBL_DESCRPT)); pDescription->SetWindowText(g_vtrShowStrings[m_iSelected][1]); }
当我们更改三个可用文本框之一中的值时,将调用 OnColorChange
方法。此方法获取这些值并更新本地值。此外,它还会执行一些验证,以确保值始终在 0 到 255 之间。
void CPPCColorConfigDlg::OnColorChange() { CEdit* pRed, * pGreen, * pBlue; int iRed, iGreen, iBlue; CString strRed, strGreen, strBlue; pRed = reinterpret_cast<CEdit*>(GetDlgItem(IDC_TXT_RED)); pGreen = reinterpret_cast<CEdit*>(GetDlgItem(IDC_TXT_GREEN)); pBlue = reinterpret_cast<CEdit*>(GetDlgItem(IDC_TXT_BLUE)); pRed->GetWindowText(strRed); pGreen->GetWindowText(strGreen); pBlue->GetWindowText(strBlue); iRed = _ttoi(strRed.GetBuffer(0)); iGreen = _ttoi(strGreen.GetBuffer(0)); iBlue = _ttoi(strBlue.GetBuffer(0)); if (iRed < 0 || iRed > 255) { MessageBox(_T("Red tone must be between 0 and 255"), NULL, MB_ICONEXCLAMATION); pRed->SetWindowText(_T("0")); } else if (iGreen < 0 || iGreen > 255) { MessageBox(_T("Green tone must be between 0 and 255"), NULL, MB_ICONEXCLAMATION); pGreen->SetWindowText(_T("0")); } else if (iBlue < 0 || iBlue > 255) { MessageBox(_T("Blue tone must be between 0 and 255"), NULL, MB_ICONEXCLAMATION); pBlue->SetWindowText(_T("0")); } else { m_iRed = iRed; m_iGreen = iGreen; m_iBlue = iBlue; InvalidateRect(NULL); } }
请注意,最后,如果一切正常,我们调用 InvalidateRect
。此函数将调用 WM_PAINT
消息,以便重绘窗口。然后我们在 OnPaint
方法中捕获该消息,然后绘制一个简单的矩形,其颜色是用户选择的颜色。这将帮助用户预览所选颜色。该方法很简单。
void CPPCColorConfigDlg::OnPaint() { CDialog::OnPaint(); CClientDC dc(this); CBrush brush; brush.CreateSolidBrush(RGB(m_iRed, m_iGreen, m_iBlue)); dc.SelectObject(&brush); dc.Rectangle(12, 90, 215, 150); }
最后,当我们选择系统组件并确定其颜色时,我们必须按下“更新设置”按钮。这将调用 OnUpdateSettings
方法,该方法将更新内存缓冲区,并最终更新注册表。
void CPPCColorConfigDlg::OnUpdateSettings() { int iPosition; iPosition = m_iSelected * 4; memcpy(m_pNewBuffer + iPosition++, &m_iRed, 1); memcpy(m_pNewBuffer + iPosition++, &m_iGreen, 1); memcpy(m_pNewBuffer + iPosition++, &m_iBlue, 1); UpdateRegistry(); SetDlgItemText(IDC_LBL_DESCRPT, _T("A soft reset must be made before changes take effect.")); }
UpdateRegistry
成员……猜猜怎么着?更新注册表……
void CPPCColorConfigDlg::UpdateRegistry() { HKEY hKey; DWORD dwType; DWORD dwSize; dwType = 0; dwSize = BUFFER_SIZE; ::RegOpenKeyEx(HKEY_LOCAL_MACHINE, _T("SYSTEM\\GWE"), NULL, NULL, &hKey); ::RegQueryValueEx(hKey, _T("SysColor"), NULL, &dwType, m_pOldBuffer, &dwSize); ::RegSetValueEx(hKey, _T("SysColor"), NULL, dwType, m_pNewBuffer, BUFFER_SIZE); ::RegCloseKey(hKey); }
结论
好了,就这些了。您可以为您的 Pocket PC 创建各种主题。此外,您还可以将此代码的部分内容包含在您的应用程序中,以便自定义其显示,并在应用程序结束时恢复原始设置(这些设置存储在HKEY_LOCAL_MACHINE\System\GWE\DefSysColor)。您甚至可以扩展此功能,因为您还可以更改HKEY_LOCAL_MACHINE\System\GWE 下的许多其他键。
请记住,您需要软重置设备才能使更改生效。
处理 Intermec 设备
如果您像文章中所述的那样更改注册表以调整任何颜色,当您对 Intermec 机器进行软重置时,它会将您的注册表“重置”为其原始值。因此,您将无法看到更改。为此,您有两种选择。
- 删除“\Flash File Store”目录下的“registry”文件。
- 更新注册表后,在软重置之前,运行 RegFlush2.exe 程序。此程序将您的注册表“拍照”并将其保存在“\Flash File Store”目录下,以便下次启动时,它将采用该配置。
有关更多信息,请参阅 Intermec 知识库中的 700c WM 2003 如何保存注册表 文章。
历史
- [2006 年 3 月 3 日] 文章主要发布。