代码之家  ›  专栏  ›  技术社区  ›  Mordachai

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

  •  3
  • Mordachai  · 技术社区  · 15 年前

    我想更新一些类似工具栏的代码,我们必须有一个vista/win7渐变圆到他们。

    目前,这些按钮具有Windows2000的外观:块状、单色。

    我玩过xp的主题,使用drawthemebackground、drawthemeedge等;但我对主题绘制机制非常不满(按钮很大,主题将它们绘制为2色调、上半部分和下半部分,当按钮很小的时候看起来没问题——它给了他们一个半正派的渐变或者圆形的外观。但这些钮扣虽然大,但看起来很蠢。

    通过简单地观察在各种应用程序和控件中绘制了多少控件,我可以看到它们中的大多数似乎都使用渐变-控件顶部显示为浅色,底部淡入较深的颜色-或者-颜色比背景浅在顶部,在中间增加到接近白色,然后在底部变回较暗的颜色。

    我真的不知道从这里到哪里去。drawthemexx似乎不够。我真的不想用一个改进了绘图的新控件替换整个控件,但需要我交换一些代码来了解当前自定义控件的工作方式,并冒着与其他库发生各种问题的风险。 我宁愿有一个方法来绘制任意对象的风格,当前版本的窗口,我正在运行。 我本以为主题绘制功能会涵盖这一点。但正如我所描述的,他们的大脑相当受损。

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

    目前,我们在代码中使用mfc、gdiplus和原始win32api。

    这是希望有人知道很多关于在Windows下绘制现代GUI的C++!

    为了使这不是一堵文字墙,下面是当前版本的绘制处理程序,它在“热跟踪”时绘制带有蚀刻边框的按钮,在按下状态下同时绘制蚀刻边框和图标+文字“按下”(移动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());
    }
    

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

    3 回复  |  直到 15 年前
        1
  •  4
  •   John Knoeller    15 年前

    实际上,复制各种口味的Windows的外观是非常困难的,尤其是如果你的应用程序可以运行在一个以上的Windows版本。

    我认为他们打算给你API在Wi2K/Wi95天返回,但是WiXP伴随着阴影和覆盖,旧的API完全不足。

    所以他们提出了主题的东西,其实并不是一个api,而是一个api和一组图形原语挤在一起。但是它们并没有完全遵循并允许扩展或替换图形基元集,因此主题只在控件与标准集非常匹配时才起作用。

    所以,对于win9x/win2k,您可以使用

    DrawFrameControl
    DrawEdge
    

    为了纪念

    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
  •  4
  •   Hans Passant    15 年前

    你从没提过MFC功能包。你看了吗?VS2008的下载,包括在VS2008 SP1中。CDrawingManager有很多特效。它非常支持应用程序主题。

        3
  •  1
  •   Kornel Kisielewicz    15 年前

    单靠mfc并不完全是皮肤友好的。除了使用另一个gui( Qt 对于自定义皮肤非常有用)你可以查看类似的解决方案 SkinCrafter .