【问题标题】:Big problems with MFC/WinAPIMFC/WinAPI 的大问题
【发布时间】:2010-10-20 12:19:47
【问题描述】:

我需要创建一个 SDI 表单,其中的表单视图有两个选项卡,将多个对话框封装为选项卡内容。但表单必须有彩色背景。

像这样的事情让我讨厌编程。

首先,我通过资源编辑器尝试了 CTabControl,尝试了不同的方法,但未记录的行为和没有答案的怪癖让我遇到了障碍。

找了好几个小时,发现有个控件叫property sheet,其实是我需要的。

稍后再搜索,我发现属性表甚至可以像这样嵌入到 CFormView 中:http://www.codeguru.com/Cpp/controls/propertysheet/article.php/c591

并且CPropertyPage派生的对话框类可以通过CPropertySheet的AddPage方法直接添加为页面。

太棒了!并非如此......一些控件不起作用,也没有被创建,遇到了奇怪的断言。结果发现对话框中缺少 DS_CONTROL 样式。在Link 上完全偶然发现它,在 MSDN 上没有任何消息!!!!属性页必须有:DS_3DLOOK | DS_CONTROL | WS_儿童 | WS_TABSTOP,并且可以有:DS_SHELLFONT | DS_LOCALEDIT | WS_CLIPCHILDREN 样式!不是任何其他的,默认情况下使用资源编辑器创建。适合软件开发人员的甜蜜、超级隐藏的信息!

该页面上 cmets 中的引用:“天哪。这就是该行为的来源......

事实证明,在 64 位机器上播放声音时,PlaySound API 依赖于这种行为。”据我所知,在 Microsoft 工作了 20 年的 Larry Osterman 让我大笑。

无论如何,修复了这个问题,对话框控件(CPropertyPages)现在按预期创建,这部分看起来很有希望,但下一个有颜色的部分又是死胡同了!

通常您覆盖 WM_CTLCOLOR,检查控件 ID 或 hwnd 并提供必要的画笔来设置您需要的颜色。对于 CPropertySheet,情况并非如此,整个顶行保持灰色!对于 CTabCtrl 它以某种方式工作,对于 CPropertySheet 它没有。

为什么?似乎 CPropertySheet 有点嵌入 CTabControl 之类的东西,因为如果我覆盖 WM_ERASEBKGND,只有内部部分会改变颜色。

现在似乎在 CPropertySheet 中有一个 GetTabControl() 方法,它返回 CPropertySheet 的实际 CTabCtrl*。但由于它是内部构造的,我找不到如何覆盖它的 WM_CTLCOLOR 消息处理。

似乎有一种方法可以对 windowproc 进行子类化,但经过多次尝试后,我找不到任何关于如何做到这一点的好资料。 MSDN 上的 SubclassWindow 文档说:“调用此函数时,该窗口不得已附加到 MFC 对象。”?!那是什么?

我尝试通过 MFC 向导创建一个基于 CTabCtrl 的自定义 CCustomTabCtrl 类,创建了它的一个实例,从 CCustomPropertySheet 处理程序之一调用 SubclassWindow 以覆盖内部 CTabCtrl,但没有任何效果,MFC 深处神秘崩溃。

尝试使用 GCL_HBRBACKGROUND 为内部 CTabCtrl 设置 WindowLong,没有任何改变。

最糟糕的是,我找不到任何关于该主题的有用文档或教程。

我能找到的最多的是如何绘制选项卡控件,但这在很多方面都是严重错误的,我想要一个标准的控件行为减去背景颜色,我不想支持不同的配色方案、windows 版本、IAccesible接口和所有这些东西,而且我见过的所有所有者绘制示例都无法获得所有标准控制行为的 10%。我不幻想我会创造更好的东西,我不会用手头的资源。

我偶然发现了这个帖子,我不能更同意作者的观点:http://arstechnica.com/civis/viewtopic.php?f=20&t=169886&sid=aad002424e80121e514548d428cf09c6所有者绘制控件是无证的 PITA,这是不可能正确的,MSDN 上有 NULL 信息可以帮助。

那么有什么我错过或尚未尝试的吗?如何更改 CPropertySheet 的顶部条带背景颜色?有人吗?

【问题讨论】:

  • "那么有什么我遗漏或尚未尝试的吗?" WPF?感觉你。
  • 要求是原生应用。 :(
  • 您首先要求使用 MFC 来解决这种痛苦。 MFC 非常强大,但也很难学习。如果您想创建某种 Windows 窗体,请使用 C#/.Net。您可以在 WinForms 或 WPF 中轻松执行此类操作(尽管选项卡控件仍然是 PITA)。
  • @Madman:如果你需要一个原生应用程序(我敢打赌你并不真的需要一个适当的原生应用程序)你最好使用德尔福。
  • 如果你想要 c++ 和 gui 编程的易用性,请使用 C++ builder 而不是 delphi!

标签: visual-studio winapi mfc


【解决方案1】:

您唯一的选择是所有者绘制选项卡控件。这并不难。嗯,这很令人沮丧,因为 MFC 没有告诉您如何进行必要的 Win32 调用。

在您的 CPropertySheet 派生类中,覆盖 OnInitDialog() 并添加:

GetTabControl()->ModifyStyle(0,TCS_OWNERDRAWFIXED);

这会让您的 CPropertySheet 派生类负责绘制选项卡控件。为 WM_DRAWITEM (OnDrawItem) 添加一个处理程序并更改 backgroundColor 和 textColor 以匹配您想要的任何颜色。 OnDrawItem 的代码如下:

void CPropSht::OnDrawItem(int nIDCtl, LPDRAWITEMSTRUCT lpDrawItemStruct)
{
    if (ODT_TAB != lpDrawItemStruct->CtlType)
    {
        CPropertySheet::OnDrawItem(nIDCtl, lpDrawItemStruct);
        return;
    }

    // prepare to draw the tab control
    COLORREF backgroundColor = RGB(0,255,0);
    COLORREF textColor = RGB(0,0,255);
    CTabCtrl *c_Tab = GetTabControl();

    //  Get the current tab item text.
    TCHAR buffer[256] = {0};
    TC_ITEM tcItem;
    tcItem.pszText = buffer;
    tcItem.cchTextMax = 256;
    tcItem.mask = TCIF_TEXT;

    if (!c_Tab->GetItem(c_Tab->GetCurSel(), &tcItem )) return;

    // draw it
    CDC aDC;
    aDC.Attach(lpDrawItemStruct->hDC);
    int nSavedDC = aDC.SaveDC();

    CBrush newBrush;
    newBrush.CreateSolidBrush(backgroundColor);
    aDC.SelectObject(&newBrush);
    aDC.FillRect(&lpDrawItemStruct->rcItem, &newBrush);
    aDC.SetBkMode(TRANSPARENT); 
    aDC.SetTextColor(textColor);
    aDC.DrawText(tcItem.pszText, &lpDrawItemStruct->rcItem, DT_CENTER|DT_VCENTER|DT_SINGLELINE);

    aDC.RestoreDC(nSavedDC);

    aDC.Detach();
}

【讨论】:

    【解决方案2】:

    感谢您提供此解决方案,但是...

    上述解决方案适用于一个选项卡,但是当您有多个选项卡时,它似乎重命名了错误的选项卡。我需要将 GetItem 的 if 语句更改为:

    if (!c_Tab->GetItem(lpDrawItemStruct->itemID, &tcItem )) return;
    

    需要 lpDrawItemStruct->itemID 才能正确命名选项卡

    【讨论】:

      猜你喜欢
      • 2010-11-29
      • 2011-11-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-04-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多