无需超类化或子类化即可实现类似 CContainedWindow 的功能

Posted

技术标签:

【中文标题】无需超类化或子类化即可实现类似 CContainedWindow 的功能【英文标题】:Achieving CContainedWindow-like functionality without superclassing or sublcassing 【发布时间】:2021-01-13 10:13:07 【问题描述】:

假设您想要一个CContainedWindow,但您想从头开始工作——即,您希望它有自己的新消息映射,没有任何超类或子类。因此,换句话说,您希望在单个类声明中方便地使用所有处理程序方法,但您不想处理预定义窗口类的任何特性。有没有一种优雅的方式来实现这一点?我唯一想出的大概是这样的(它是一个嵌套在外部CWindowImpl-派生类中的CWindowImpl-派生类):

class COuterWindow :
    public CWindowImpl<COuterWindow>

public:
    COuterWindow() : m_wndInner(this)
    
    

    BEGIN_MSG_MAP(COuterWindow)
        // ...
    END_MSG_MAP()

private:
    class CInnerWindow :
        public CWindowImpl<CInnerWindow>
    
    public:
        CInnerWindow(COuterWindow* pwndOuter) : m_pwndOuter(pwndOuter)
        
        

    private:
        COuterWindow* m_pwndOuter;

        BEGIN_MSG_MAP(CInnerWindow)
            MESSAGE_HANDLER(WM_PAINT, m_pwndOuter->OnInnerPaint)
            // ...
        END_MSG_MAP()
    ;

    LRESULT OnInnerPaint(UINT, WPARAM, LPARAM, BOOL&);

    CInnerWindow m_wndInner;
;

这可行,但我想知道在我忽略的 ATL 中是否有任何东西(如消息映射宏或其他东西)可以完成此操作或使其更整洁或允许使用单个消息映射。感谢您的指导。

【问题讨论】:

您可能正在寻找CHAIN_MSG_MAP_DYNAMICCDynamicChain。它允许您将所有(或大部分)消息从CInnerWindow 的实例重定向到COuterWindow 实例中的备用消息映射。 谢谢,但我从文档中得出的结论是否正确,您只能重定向到默认消息映射? docs.microsoft.com/en-us/cpp/atl/reference/… 不,CDynamicChain::SetChainEntry 采用 alt 消息映射的 ID。 哦,太棒了。我没看到。非常感谢。 如果您想添加您的评论作为答案,我会接受。 【参考方案1】:

根据 Igor 的评论,我认为你可以做这样的事情来实现一种“空白”CContainedWindow

class CContainedWindowBlank : public CWindowImpl<CContainedWindowBlank>
                            , public CDynamicChain

public:
    CContainedWindowBlank(CMessageMap* pObject, DWORD dwMsgMapId = 0)
    
        SetChainEntry(1, pObject, dwMsgMapId);
    

    BEGIN_MSG_MAP()
        CHAIN_MSG_MAP_DYNAMIC(1)
    END_MSG_MAP()
;

虽然我不确定如何将其转换为接受CWinTraits 之类的模板。如果有人知道如何做到这一点,我将不胜感激。谢谢。

实际上,我似乎已经实现了模板化版本,虽然我不太确定它为什么起作用或是否可以简化:

template <class TBase = CWindow, class TWinTraits = CControlWinTraits>
class CContainedWindowBlankT : public CWindowImpl<CContainedWindowBlankT<TBase, TWinTraits>, TBase, TWinTraits>
                             , public CDynamicChain

public:
    CContainedWindowBlankT(CMessageMap* pObject, DWORD dwMsgMapId = 0)
    
        SetChainEntry(1, pObject, dwMsgMapId);
    
    BEGIN_MSG_MAP()
        CHAIN_MSG_MAP_DYNAMIC(1)
    END_MSG_MAP()
;

【讨论】:

【参考方案2】:

另一种选择是简单地注册一个空白窗口类,即普通的 Win32 方式,然后将其与CContainedWindow 结合使用。头文件中可能是这样的:

#define WC_BLANK L"BLANKWINDOWCLASS"

inline const ATOM atom = []()
    WNDCLASS wc =  ;
    wc.lpszClassName = WC_BLANK;
    wc.hInstance = _AtlBaseModule.GetModuleInstance();
    wc.lpfnWndProc = DefWindowProc;
    wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
    return RegisterClass(&wc);
(); 

【讨论】:

以上是关于无需超类化或子类化即可实现类似 CContainedWindow 的功能的主要内容,如果未能解决你的问题,请参考以下文章

VC++ win32 application里如何建一个子窗口

CollectionViewController,我应该使用普通的 UIViewController 子类化或实现它的委托/数据源吗?

Python 接口:从协议到抽象基类

Delphi一共封装(超类化)了8种Windows基础控件和17种复杂控件

当子类被实例化时,超类的私有成员是不是也被实例化? [复制]

子类化 UITextView 时实现 UIScrollView 方法