将静态控件子类化到对话框 window

Subclassing a static control to the dialog window

让我从目标开始:我要将静态控件的背景设置为对话框,而静态控件内容不是固定字符串,并且在 switch case 之后声明,变化。换句话说,我要将具有动态内容的静态控件的背景设置为具有静态双图图像的对话框 window。

为了达到这个目标,三个处理消息定义如下: OnEraseBkgnd(), OnDestroy(), OnCtlColor()

为了显示文本,在每个 Switch case 语句之后我设置了一个 SetWindowText 函数:

::SetWindowText(GetDlgItem(IDC_STATIC_FORM)->m_hWnd, ArrayName);

这里唯一的问题是 overlapping。预览事件或案例中的所有文本都保留在 SetWindowText。为了在每个 SetWindowText 定义一个 InvalidateRect(Null) 之后解决这个问题,这个问题也解决了,但是在这个过程中我收到了一个总是闪烁的对话框,如果我从技术上说,闪烁。我认为问题只是因为 Invalidate 应用于整个对话框而不仅仅是静态控件。 OnCtlColor 被对话框而不是控件覆盖。因为我只是想让控件失效,所以我必须子 class CStatic 控件,只覆盖它的 OnCtlColor(不是对话框的),并且只调用它的 Invalidate。

我的问题:

假设我已经在主代码中声明了一个用于静态控制的变量并在构造函数中初始化它我定义了一个名为 CSTATICCTRL 的 class 基类 class CDialogEX 然后消息处理程序如下:

变量成员定义为:

CSTATICCTRL m_STATIC_FORM;

BOOL CSTATICCTRL::OnEraseBkgnd(CDC* pDC)
{
    // TODO: Add your message handler code here and/or call default
    CDC dcMemory;
    dcMemory.CreateCompatibleDC(pDC);
    CBitmap* pOldbitmap = dcMemory.SelectObject(&Background);
    CRect rcClient;

    const CSize& sbitmap = bitmapSize;
    pDC->BitBlt(0, 0, sbitmap.cx, sbitmap.cy, &dcMemory, 0, 0, SRCCOPY);
    /*The BitBlt function performs a bit-block transfer of the color data corresponding to a rectangle of pixels
    from the specified source device context into a destination device context.*/
    dcMemory.SelectObject(pOldbitmap);

    return 1;
    //return CDialogEx::OnEraseBkgnd(pDC);
}

HBRUSH CSTATICCTRL::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
{
    //HBRUSH hbr = CDialogEx::OnCtlColor(pDC, pWnd, nCtlColor);

    if (pWnd->GetDlgCtrlID() == IDC_STATIC_FORM)
    {
        pDC->SetBkMode(TRANSPARENT);
        pDC->SetTextColor(RGB(255, 255, 255));
        Invalidate();

    }
    return (HBRUSH)GetStockObject(NULL_BRUSH);

}
void CSTATICCTRL::OnDestroy()  //free the resources created for the bitmap,
{
    CDialog::OnDestroy();
    Background.DeleteObject(); // Delete Background bitmap
    BrushHol.DeleteObject();
    // Delete Brush
}

我也在主代码上留下了这些信息。然后我尝试以这种方式在每个 switch case 之后使静态控件无效:

 m_STATIC_FORM.Invalidate();

另外一个 PreSubclassWindow() 句柄添加在新 class 的末尾,如下所示。

void CSTATICCTRL::PreSubclassWindow()
{
    // TODO: Add your specialized code here and/or call the base class

    CDialogEx::PreSubclassWindow();
    ModifyStyle(0, BS_OWNERDRAW);   // make the button owner drawn

} 

什么都没发生!我不知道我的代码有什么问题!!在这种新方式中,静态控件内容发生了变化,但它不是透明的,重叠仍然存在。

更新:

其实刚才我在注释了一些行后才意识到新的Class对静态控件和对话框没有任何影响!!我想首先我应该激活 CSTATICCTRL 然后我可以处理它的句柄消息。

对话代码使用了这三个句柄消息:

BOOL CMainDlg::OnEraseBkgnd(CDC* pDC)
{
    // TODO: Add your message handler code here and/or call default
    //************************************************************************************
    CDC dcMemory;
    dcMemory.CreateCompatibleDC(pDC);
    CBitmap* pOldbitmap = dcMemory.SelectObject(&Background);
    CRect rcClient;
    GetClientRect(&rcClient);
    const CSize& sbitmap = bitmapSize;
    pDC->BitBlt(0, 0, sbitmap.cx, sbitmap.cy, &dcMemory, 0, 0, SRCCOPY);
    dcMemory.SelectObject(pOldbitmap);
    return TRUE;
    //return CDialog::OnEraseBkgnd(pDC);
}




void CMainDlg::OnDestroy()  //free the resources created for the bitmap,
{
    CDialog::OnDestroy();

    // Delete Brush
}

HBRUSH CMainDlg::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
{

    // TODO:  Change any attributes of the DC here

    return (HBRUSH)GetStockObject(NULL_BRUSH);
}

我还没有尝试过,但我认为它会起作用。

在您的对话框中覆盖 OnCtlColor。当它检测到正在绘制静态控件时,return 一个空画笔。这样可以防止擦除背景,而擦除正是导致闪烁的原因。

在静态控件上设置 WS_EX_TRANSPARENT 样式。这将导致在绘制控件之前重新绘制控件下方的对话框部分,从而擦除旧文本。

编辑:根据 an article by Raymond Chen the WS_EX_TRANSPARENT style won't help in this case, because the dialog isn't a sibling of the static control, it's the parent. You can make it redraw the portion underneath your static control with RedrawWindow.

您不需要自己的 class,CStatic 可以很好地处理这些修改。如果你决定你需要自己的class,你不应该从CDialogEx派生它,但应该为对话windows保留。

pStatic->SetWindowText(str);
CRect rcChild;
pStatic->GetClientRect(&rcChild);
pStatic->MapWindowPoints(this, &rcChild);
RedrawWindow(rcChild, NULL, RDW_INVALIDATE | RDW_UPDATENOW);
pStatic->InvalidateRect(NULL);