手动为按钮、工具栏、选项卡等绘制渐变?

Mor*_*hai 3 c++ user-interface winapi mfc visual-c++

我想更新一些类似工具栏的代码,我们必须让它们具有 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());
}
Run Code Online (Sandbox Code Playgroud)

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

Joh*_*ler 5

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

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

所以他们想出了主题的东西,它甚至不是一个真正的 API,而是一个 API 和一组图形基元都挤在一起。但它们并没有贯彻执行并允许扩展或替换图形基元集,因此主题仅在您的控件与标准集非常匹配时才有效。

所以,对于Win9x/Win2k。你用

DrawFrameControl
DrawEdge
Run Code Online (Sandbox Code Playgroud)

对于 WinXP

DrawTheme
Run Code Online (Sandbox Code Playgroud)

对于 WinVista/7

DrawTheme
DwmXXX functions
GradientFill ??
Run Code Online (Sandbox Code Playgroud)

现在我怀疑 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); 
Run Code Online (Sandbox Code Playgroud)