【问题标题】:Manually drawing gradients for buttons, toolbars, tabs etc?手动为按钮、工具栏、选项卡等绘制渐变?
【发布时间】:2010-01-18 19:10:51
【问题描述】:

我想更新一些类似工具栏的代码,我们必须对它们进行 Vista/Win7 渐变圆角。

目前,按钮具有 Windows 2000 的外观和感觉:块状、单色调。

我玩过 XP 主题,并使用过 DrawThemeBackground、DrawThemeEdge 等;但我对主题绘图机制非常不满意(按钮很大,主题将它们绘制为 2 音调,上半部分和下半部分,当按钮很小时看起来还不错 - 它给了它们一半体面的外观对它们来说是渐变或圆润的。但这些按钮虽然大,但看起来很愚蠢。

通过简单地观察在各种应用程序和控件中绘制了多少控件来进行实验,我可以看到它们中的大多数似乎都使用了渐变——控件的顶部显示为浅色,而底部则逐渐变暗颜色 - 或 - 顶部的颜色比背景颜色浅,在中间逐渐接近白色,然后在底部逐渐变暗。

我不确定从这里去哪里。 DrawThemeXXX 似乎不够用。我真的不想用一个改进了绘图的新控件替换整个控件,但需要我换掉一些代码以了解当前自定义控件的工作方式,并冒着与其他一些库出现各种问题的风险。 我宁愿有一种方法以我正在运行的当前版本的 Windows 的样式绘制任意对象。我原以为主题绘图功能会涵盖这一点。但正如我所描述的,它们的脑损伤相当严重。

有人能告诉我“现代 C++ 应用程序应该如何绘制自定义 GUI 元素,以便他们可以合理地期待在 XP、Vista 和 Windows 7 下的优雅外观?”

目前,我们在代码中使用 MFC、Gdiplus 和原始 Win32 API。

希望有人知道如何在 Windows 下使用 C++ 绘制现代 GUI!

只是为了让这不是一堵文字墙,这是当前版本的绘制处理程序,它在“热跟踪”时绘制带有蚀刻边框的按钮,以及蚀刻边框和图标 + 文本“按下” " (移动 1,1)当处于按下状态时:

void CPlacesButton::PaintButton(CDC & dc, CRect & rcClient)
{
 const int kLabelHeight = 8;

 COLORREF clrHiLt = GetSysColor(COLOR_BTNHIGHLIGHT);
 COLORREF clrShdo = GetSysColor(COLOR_BTNSHADOW);
 COLORREF clrText = GetSysColor(COLOR_BTNTEXT);
 COLORREF clrFace = GetSysColor(COLOR_BTNFACE);

 // draw the button's background & border

 if (m_bPressed || m_bDrawPressed || m_bMouseOnButton)
 {
  COLORREF clrDarkened = Darken(clrFace, -0.01f);
  dc.FillRect(rcClient, &CBrush(clrDarkened));

  //dc.Draw3dRect(rcClient, clrShdo, clrHiLt);
  //dc.RoundRect(&rcClient, CPoint(10,10));
  dc.DrawEdge(&rcClient, EDGE_ETCHED, BF_RECT|BF_FLAT);
  //dc.DrawFrameControl(&rcClient, DFC_BUTTON, DFCS_BUTTONPUSH|DFCS_PUSHED);
 }
//  else if (m_bMouseOnButton) // hot draw
//   //dc.Draw3dRect(rcClient, clrShdo, clrHiLt);
//   dc.DrawEdge(&rcClient, EDGE_ETCHED, BF_RECT);
//   //dc.RoundRect(&rcClient, CPoint(10,10));
 else
  dc.FillRect(rcClient, &CBrush(clrFace));

 // use transparent mode for everything that follows
 dc.SetBkMode(TRANSPARENT);

 // center icon
 CPoint ptIcon((rcClient.Width() - m_nIconSize) / 2, ((rcClient.Height() - m_nIconSize) / 2) - kLabelHeight);
 if (m_bPressed || m_bDrawPressed)
  ptIcon.Offset(1, 1);

 // determine the state to draw ourselves in
 const UINT nState = DST_ICON | (IsEnabled() ? DSS_NORMAL : DSS_DISABLED);

 // draw our icon
 dc.DrawState(ptIcon, CSize(m_nIconSize, m_nIconSize), m_hIcon, nState, (HBRUSH)NULL);

 // create & select the font to use for the button's label
 CFont guiFont;
 VERIFY(guiFont.CreateStockObject(DEFAULT_GUI_FONT));
 AutoSelectGDIObject select_font(dc, guiFont);

 // determine clipping rect for label
 CRect rcText(0, ptIcon.y+m_nIconSize+kLabelHeight, rcClient.Width(), ptIcon.y+m_nIconSize+kLabelHeight);
 rcText.InflateRect(0,20);
 if (m_bPressed || m_bDrawPressed)
  rcText.OffsetRect(1, 1);

 dc.SetTextColor(clrText);
 if (IsEnabled())
  dc.DrawText(m_strCaption, rcText, DT_VCENTER|DT_SINGLELINE|DT_CENTER);
 else
  dc.GrayString(NULL, NULL, (LPARAM)(LPCTSTR)m_strCaption, 0, rcText.TopLeft().x, rcText.TopLeft().y, rcText.Width(), rcText.Height());
}

我在代码中留下了一些被注释掉的变体,以表明一些关于我尝试过的其他可能性的提示。但是,它们只是一个提示,因为不存在完整的替代示例。

【问题讨论】:

标签: c++ user-interface visual-c++ mfc winapi


【解决方案1】:

实际上,复制各种 Windows 风格的外观非常困难,尤其是当您的应用程序可以在多个版本的 Windows 上运行时。

我认为他们打算在 Win2k/Win95 时代为您提供执行此操作的 api,但后来 WinXP 出现了阴影和覆盖,旧的 API 完全不够用。

因此,他们提出了主题内容,这甚至不是一个 API,而是一个 API 和一组图形原语都挤在一起。但是它们并没有遵循并允许扩展或替换图形基元集,因此主题仅在您的控件与标准集非常匹配时才有效。

所以,对于 Win9x/Win2k。你用

DrawFrameControl
DrawEdge

对于WinXP

DrawTheme

对于 WinVista/7

DrawTheme
DwmXXX functions
GradientFill ??

现在我怀疑 Windows 实际上并没有使用 GradientDraw。我怀疑它实际上使用了一些内置在窗口管理器代码中的 DX10 着色器,但我不知道该怎么做,因为我一直在使用 GradientDraw。此代码将为您提供从控件顶部到底部的线性淡入淡出。

INLINE void SetTrivertex(TRIVERTEX & vtx, int x, int y, COLORREF cr)
{
   vtx.x      = x;
   vtx.y      = y;
   vtx.Red    = (SHORT)(GetRValue(cr) * 256L);
   vtx.Green  = (SHORT)(GetGValue(cr) * 256L);
   vtx.Blue   = (SHORT)(GetBValue(cr) * 256L);
   vtx.Alpha  = (SHORT)(255 * 256L);
}

...

  // fill the interior from the top down with a gradient that starts at crTop
  // and ends with the crBottom
  TRIVERTEX vtx[2];
  SetTrivertex (vtx[0], prc->left+1, prc->top+1, crTop);
  SetTrivertex (vtx[1], prc->right-1, prc->bottom-1, crBottom);

  GRADIENT_RECT gRect = { 0, 1 };
  GradientFill(hdc, vtx, 2, &gRect, 1, GRADIENT_FILL_RECT_V); 

【讨论】:

  • 感谢以上。我可能最终会使用它,我可能不会,但我没有意识到它。 :)
【解决方案2】:

您从未提到过 MFC 功能包。你看过了吗? VS2008 的下载,包含在 VS2008 SP1 中。 CDrawingManager 有很多特殊效果。它对应用程序主题有很好的支持。

【讨论】:

  • 我需要花一些时间来真正研究一下功能包。我确实有 2008SP1,所以大概我有它。我只是不知道从哪里开始使用功能包。我们的 MFC 应用程序很大,我不知道“放入”功能包是否容易。
【解决方案3】:

单独的 MFC 并不完全适合换肤。除了使用另一个 GUI(Qt 非常适合自定义皮肤)之外,您还可以查看像 SkinCrafter 这样的解决方案。

【讨论】:

  • 我并不是真的想尝试允许为整个应用程序设置皮肤。我只想创建偶尔的“按钮式”或“菜单式”控件,它们看起来和感觉就像我们正在运行的 Windows 版本(所以 Vista 应该看起来像 Vista,而 Win7 应该看起来像任何主题用户已选择用于 Win7 等)。 MFC 确实通过实际创建“BUTTON”窗口类对象来正确创建 CButton。但是所述窗口的绘图对我来说是不透明的。过去模拟标准按钮控件很容易...我只想正确模拟一个仍然...
  • Qt 太棒了!尤其是 QStyle 和 doc.trolltech.com/4.6/stylesheet.html
  • 我的印象是 QT 更像是整个应用程序的框架,而不是一些自定义控件的插件?
  • 我真的很想在这方面帮助你,但我个人不会用棍子碰 MFC -- 我使用 Delphi 或 wxWindows 来编写 gui 应用程序:/
猜你喜欢
  • 1970-01-01
  • 2012-02-22
  • 2016-06-17
  • 2011-07-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-08-14
  • 1970-01-01
相关资源
最近更新 更多