如何在调用 DDX_Control 之前更改使用 DDX_Control 初始化的控件 (CListBox) 的样式 [英] How can I change the style of a control (CListBox) initialized with DDX_Control, before DDX_Control is called
问题描述
我正在修改一个现有项目,并且一个对话框中的控件在某些情况下我以不同的方式子类化为主题(在其他情况下,我将完全不理会它).在 DoDataExchange()
期间调用 DDX_Control()
时,ListBox
的 hwnd 已经应用了样式.具体来说,此时即使我执行SetWindowLongPtr()
,LBS_OWNERDRAWFIXED
也不起作用.不起作用"是指虽然应用了样式,但 CListBox 不会接收到所有者绘制消息.
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.
相反,如果我避免使用 DDX_Control()
并简单地进行创建,则 ListBox 确实会接收消息并且可以被所有者绘制.但是如果我这样做,现在有两个 HWND,GetDlgItem()
只返回其中一个.我相信我可以在必要时完成这项工作,但我想知道是否有秘密可以拦截对话框中控件的 HWND 创建(实际上是一个 CPropertyPage).
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);
}
我可能应该补充一点,我尝试对窗口进行子类化,但没有帮助,而且 CMyPropertySheet::PreSubclassWindow
也不够快.
I should probably add I tried subclassing the window, but it didn't help, and CMyPropertySheet::PreSubclassWindow
wasn't soon enough, either.
推荐答案
一些创建标志如 LBS_OWNERDRAWFIXED
和 LBS_SORT
被缓存,之后修改它们没有任何效果.您必须更改模板,或者只是制作列表框的副本.复制旧列表框的样式,然后隐藏该列表框,更改其 ID,并基于旧列表框创建一个新列表框.然后你必须删除 DDX_Control(pDX, IDC_LIST1, m_listbox)
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.
为简单起见,此示例避免使用 LBS_OWNERDRAWFIXED
,而是使用 LBS_SORT
.
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;
这篇关于如何在调用 DDX_Control 之前更改使用 DDX_Control 初始化的控件 (CListBox) 的样式的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!