CDialog:为什么 SetParent(this) 改变视觉风格?
CDialog: Why SetParent(this) change the visual Style?
我在 CView
派生的 class m_wndTestDlg.Create(CTestDlg::IDD, this);
和 Show/Hide 中创建了一个非模态 CDialog
并使用以下代码移动对话框
h 文件:
class CTestView : public CView
{
:
CTestDlg m_wndTestDlg;
:
}
cpp 文件:
void CTestView::OnInitialUpdate()
{
CView::OnInitialUpdate();
m_wndTestDlg.Create(CTestDlg::IDD, this);
}
void CTestView::OnDialog1()
{
BOOL b = m_wndTestDlg.IsWindowVisible();
if (b)
m_wndTestDlg.ShowWindow(SW_HIDE);
else {
m_wndTestDlg.ShowWindow(SW_SHOW);
// if (!m_wndTestDlg.IsWindowVisible()) { // still not viewable, outside ParentScreen, move to top of CView
m_wndTestDlg.SetParent(this);
m_wndTestDlg.SetWindowPos(&CWnd::wndTop, 0, 0, 0, 0, SWP_NOSIZE | SWP_SHOWWINDOW);
}
}
我不明白为什么 SetParent
将视觉风格更改为旧的 WinXP 风格?
如果 SetParent
别有用心的目的是将对话框保持在视图前面,那么以下方法可能有效(也可能无效,具体取决于 UI 具体情况)。但是,如果原因是将对话框剪辑到 parent 视图,那么这将无济于事,尽管它仍然试图解释原因。
这里的主要问题是 Windows 如何将视觉样式应用于 child windows(的后代),或者更确切地说 不是 去做。该问题并不局限于 VC++ 或 MFC,并且已被注意到 [1], [2], [3], [4] (the last link is an open case on dotnet/winforms but also references MFC)。由于这个问题,re-parenting 作为视图 child 的对话框“丢失”了主题。
这里的另一个复杂问题是所讨论的 window 是一个对话框,而对话框是 owned windows. Just calling SetParent
changes the parent of the dialog, but leaves the owner in place. This goes against the rule that A window can have a parent or an owner but not both. Also, SetParent
需要更新样式位以删除 WS_POPUP
并添加 WS_CHILD
代替。但是,一旦对话框成为视图的 child,清除所有者和修复样式位都不会改变样式行为。
另一种方法是更改对话框的 owner,而不是其 parent,在这种情况下,视觉样式实际上会得到应用。这使对话框在 Z 顺序中保持在其所有者前面,但不会将其剪辑到所有者区域。但是,需要注意的是,所有者必须是弹出窗口或重叠 window,因此不能将其设置为视图本身,即 child window,而是与其最接近的popup/overlapped 祖先。在带有单个 top-level window 的 UI 中,这通常意味着唯一的主要 window.
示例代码如下,内联了一些注释,#if 0
部分不 工作。
void CTestView::OnInitialUpdate()
{
CView::OnInitialUpdate();
// dialog gets created with parent = desktop window
// owner = app main window
m_wndTestDlg.Create(IDD_ABOUTBOX, this);
// visual styles *are* applied at this point
m_wndTestDlg.ShowWindow(SW_SHOW);
}
void CTestView::OnDialog1()
{
if (m_wndTestDlg.IsWindowVisible())
{ m_wndTestDlg.ShowWindow(SW_HIDE); }
else
{
#if 0 // visual styles *not* applied to test dialog
// the following make re-parenting consistent with the rules
// but visual styles are still not applied
// ::SetWindowLongPtr(m_wndTestDlg.m_hWnd, GWLP_HWNDPARENT, NULL);
// m_wndTestDlg.ModifyStyle(WS_POPUP | WS_CHILD, WS_CHILD);
m_wndTestDlg.SetParent(this);
#else // visual styles *are* applied to test dialog
// for an owned window, the following sets the owner, not the parent
// the new owner must be ws_popup or ws_overlapped, thus ga_root
::SetWindowLongPtr(m_wndTestDlg.m_hWnd, GWLP_HWNDPARENT, (LONG_PTR)::GetAncestor(m_hWnd, GA_ROOT));
#endif
m_wndTestDlg.SetWindowPos(&CWnd::wndTop, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_SHOWWINDOW);
}
}
我在 CView
派生的 class m_wndTestDlg.Create(CTestDlg::IDD, this);
和 Show/Hide 中创建了一个非模态 CDialog
并使用以下代码移动对话框
h 文件:
class CTestView : public CView
{
:
CTestDlg m_wndTestDlg;
:
}
cpp 文件:
void CTestView::OnInitialUpdate()
{
CView::OnInitialUpdate();
m_wndTestDlg.Create(CTestDlg::IDD, this);
}
void CTestView::OnDialog1()
{
BOOL b = m_wndTestDlg.IsWindowVisible();
if (b)
m_wndTestDlg.ShowWindow(SW_HIDE);
else {
m_wndTestDlg.ShowWindow(SW_SHOW);
// if (!m_wndTestDlg.IsWindowVisible()) { // still not viewable, outside ParentScreen, move to top of CView
m_wndTestDlg.SetParent(this);
m_wndTestDlg.SetWindowPos(&CWnd::wndTop, 0, 0, 0, 0, SWP_NOSIZE | SWP_SHOWWINDOW);
}
}
我不明白为什么 SetParent
将视觉风格更改为旧的 WinXP 风格?
如果 SetParent
别有用心的目的是将对话框保持在视图前面,那么以下方法可能有效(也可能无效,具体取决于 UI 具体情况)。但是,如果原因是将对话框剪辑到 parent 视图,那么这将无济于事,尽管它仍然试图解释原因。
这里的主要问题是 Windows 如何将视觉样式应用于 child windows(的后代),或者更确切地说 不是 去做。该问题并不局限于 VC++ 或 MFC,并且已被注意到 [1], [2], [3], [4] (the last link is an open case on dotnet/winforms but also references MFC)。由于这个问题,re-parenting 作为视图 child 的对话框“丢失”了主题。
这里的另一个复杂问题是所讨论的 window 是一个对话框,而对话框是 owned windows. Just calling SetParent
changes the parent of the dialog, but leaves the owner in place. This goes against the rule that A window can have a parent or an owner but not both. Also, SetParent
需要更新样式位以删除 WS_POPUP
并添加 WS_CHILD
代替。但是,一旦对话框成为视图的 child,清除所有者和修复样式位都不会改变样式行为。
另一种方法是更改对话框的 owner,而不是其 parent,在这种情况下,视觉样式实际上会得到应用。这使对话框在 Z 顺序中保持在其所有者前面,但不会将其剪辑到所有者区域。但是,需要注意的是,所有者必须是弹出窗口或重叠 window,因此不能将其设置为视图本身,即 child window,而是与其最接近的popup/overlapped 祖先。在带有单个 top-level window 的 UI 中,这通常意味着唯一的主要 window.
示例代码如下,内联了一些注释,#if 0
部分不 工作。
void CTestView::OnInitialUpdate()
{
CView::OnInitialUpdate();
// dialog gets created with parent = desktop window
// owner = app main window
m_wndTestDlg.Create(IDD_ABOUTBOX, this);
// visual styles *are* applied at this point
m_wndTestDlg.ShowWindow(SW_SHOW);
}
void CTestView::OnDialog1()
{
if (m_wndTestDlg.IsWindowVisible())
{ m_wndTestDlg.ShowWindow(SW_HIDE); }
else
{
#if 0 // visual styles *not* applied to test dialog
// the following make re-parenting consistent with the rules
// but visual styles are still not applied
// ::SetWindowLongPtr(m_wndTestDlg.m_hWnd, GWLP_HWNDPARENT, NULL);
// m_wndTestDlg.ModifyStyle(WS_POPUP | WS_CHILD, WS_CHILD);
m_wndTestDlg.SetParent(this);
#else // visual styles *are* applied to test dialog
// for an owned window, the following sets the owner, not the parent
// the new owner must be ws_popup or ws_overlapped, thus ga_root
::SetWindowLongPtr(m_wndTestDlg.m_hWnd, GWLP_HWNDPARENT, (LONG_PTR)::GetAncestor(m_hWnd, GA_ROOT));
#endif
m_wndTestDlg.SetWindowPos(&CWnd::wndTop, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_SHOWWINDOW);
}
}