How can I change the style of a control (CListBox)

2019-07-26 22:57发布

I'm modifying an existing project and a dialog has controls I am subclassing to theme differently in some cases (in other cases I will leave it entirely alone). By the time DDX_Control() is called during DoDataExchange(), the hwnd for the ListBox already has styles applied. Specifically, at this time even if I do SetWindowLongPtr(), the LBS_OWNERDRAWFIXED does not work. By "does not work," I mean that although the style is applied, owner draw messages are not received by the CListBox.

Conversely, if I avoid the DDX_Control() and simply do a create, the ListBox does receive the messages and can be ownerdrawn. But if I do this there are now two HWND, only one of which is returned by GetDlgItem(). I believe I can make this work if necessary, but I wondered if there is a secret to intercept the HWND creation of the controls in the dialog (actually a CPropertyPage).

Below is code that doesn't work, with more commented code that "works" but isn't the way I wanted it to work, if possible.

void CMyPropertySheet::DoDataExchange(CDataExchange* pDX)
{
    HWND hWndCtrl;
    pDX->m_pDlgWnd->GetDlgItem(IDC_LIST1, &hWndCtrl);

    if (themed) {
        DWORD style = GetWindowLongPtr(hWndCtrl, GWL_STYLE) | LBS_OWNERDRAWFIXED;
        SetWindowLongPtr(hWndCtrl, GWL_STYLE, style);
        DDX_Control(pDX, IDC_LIST1, m_listbox);
        //RECT wr;
        //::GetWindowRect(hWndCtrl, &wr);
        //m_listbox.Create(style, wr, this, IDC_LIST1);
    } else {
        DDX_Control(pDX, IDC_LIST1, m_listbox);
    }

I should probably add I tried subclassing the window, but it didn't help, and CMyPropertySheet::PreSubclassWindow wasn't soon enough, either.

标签: c++ winapi mfc
2条回答
对你真心纯属浪费
2楼-- · 2019-07-26 23:09

OK, I'm not sure I recommend this to anyone, but I did find a way to modify the template, finally. I had to use VirtualProtect to unlock the memory of the template.

for (int i = 0; i < m_pages.GetSize(); i++) {
    CPropertyPage* pPage = GetPage(i);
    PROPSHEETPAGE* tpsp = &pPage->m_psp;

    const DLGTEMPLATE* pTemplate;
    if (tpsp->dwFlags & PSP_DLGINDIRECT) {
        pTemplate = tpsp->pResource;
    } else {
        HRSRC hResource = ::FindResource(tpsp->hInstance, tpsp->pszTemplate, RT_DIALOG);
        if (hResource == NULL) return false;
        HGLOBAL hTemplate = LoadResource(tpsp->hInstance, hResource);
        if (hTemplate == NULL) return false;
        pTemplate = (LPCDLGTEMPLATE)LockResource(hTemplate);
        if (pTemplate == NULL) return false;
    }

    if (afxOccManager != NULL) {
        DLGITEMTEMPLATE *pItem = _AfxFindFirstDlgItem(pTemplate);
        DLGITEMTEMPLATE *pNextItem;
        BOOL bDialogEx = IsDialogEx(pTemplate);

        int iItem, iItems = DlgTemplateItemCount(pTemplate);

        for (iItem = 0; iItem < iItems; iItem++) {
            pNextItem = _AfxFindNextDlgItem(pItem, bDialogEx);
            DWORD dwOldProtect, tp;
            if (bDialogEx) {
                _DialogSplitHelper::DLGITEMTEMPLATEEX *pItemEx = (_DialogSplitHelper::DLGITEMTEMPLATEEX *)pItem;
                if (pItemEx->id == IDC_LIST1) {
                    if (VirtualProtect(&pItemEx->style, sizeof(pItemEx->style), PAGE_READWRITE, &dwOldProtect)) {
                        pItemEx->style |= LBS_OWNERDRAWFIXED;
                        VirtualProtect(&pItemEx->style, sizeof(pItemEx->style), dwOldProtect, &tp);
                    }
                }
            } else {
                if (pItem->id == IDC_LIST1) {
                    if (VirtualProtect(&pItem->style, sizeof(pItem->style), PAGE_READWRITE, &dwOldProtect)) {
                        pItem->style |= LBS_OWNERDRAWFIXED;
                        VirtualProtect(&pItem->style, sizeof(pItem->style), dwOldProtect, &tp);
                    }
                }
            }
            pItem = pNextItem;
        }
    }
}
return true;
查看更多
地球回转人心会变
3楼-- · 2019-07-26 23:20

Some creation flags like LBS_OWNERDRAWFIXED and LBS_SORT are cached and modifying them afterwards have no effect. You have to change the template, or just make a duplicate of the listbox. Copy the style of the old listbox, then hide that listbox, change its ID, and create a new listbox based on the old one. You then have to remove DDX_Control(pDX, IDC_LIST1, m_listbox)

The example below starts with a standard list which has its sort flag set. It duplicates the listbox and disables the sort option.

For simplicity this example avoids LBS_OWNERDRAWFIXED, it uses LBS_SORT instead.

class CMyPropertyPage : public CPropertyPage
{
public:
    CListBox m_listbox;
    int m_listbox_index;

    CMyPropertyPage(int idd) : CPropertyPage(idd)
    {
        m_listbox_index = 1;
    }   

    void DoDataExchange(CDataExchange* pDX)
    {
        //This function is automatically called before 
        //CPropertyPage::OnInitDialog is complete
        //On the first call, IDC_LIST1 will point to the template listbox
        if(m_listbox.m_hWnd)
        {
            //m_listbox is ready, 
            //IDC_LIST1 will refer to the new listbox
            DDX_LBIndex(pDX, IDC_LIST1, m_listbox_index);
        }
    }

    BOOL OnInitDialog()
    {
        CPropertyPage::OnInitDialog();

        CListBox* old_listbox = (CListBox*)GetDlgItem(IDC_LIST1);
        if(old_listbox)
        {
            DWORD style = ~LBS_SORT & GetWindowLongPtr(old_listbox->m_hWnd, GWL_STYLE);
            CRect rc;
            old_listbox->GetWindowRect(&rc);
            ScreenToClient(&rc);
            old_listbox->SetDlgCtrlID(0);//change the old ID to something unused
            old_listbox->ShowWindow(SW_HIDE); //hide the old listbox
            m_listbox.Create(style | WS_BORDER, rc, this, IDC_LIST1);
            m_listbox.SetFont(GetFont());
        }

        ASSERT(m_listbox.GetDlgCtrlID() == IDC_LIST1);
        m_listbox.AddString(L"2");
        m_listbox.AddString(L"1");
        m_listbox.AddString(L"0");

        UpdateData(FALSE);

        return TRUE;
    }
};

class CMyWinApp : public CWinApp
{
    BOOL InitInstance()
    {
        CWinApp::InitInstance();
        CPropertySheet sh;
        CMyPropertyPage page(IDD_PAGE1);
        sh.AddPage(&page);
        sh.DoModal();
        return TRUE;
    }
} myapp;
查看更多
登录 后发表回答