跳转至

在SOUI中使用真窗口

在DirectUI系统中,有时需要在DUI窗口中嵌入带有窗口句柄的传统Windows控件(真窗口)。本文将介绍如何在SOUI中优雅地管理和使用真窗口。

概念介绍

什么是真窗口?

  • 具有窗口句柄(HWND)的Windows控件
  • 传统Win32控件
  • MFC/ATL控件等

为什么需要真窗口?

  1. 使用特定的第三方控件
  2. 集成现有的Windows控件
  3. 特殊功能需要使用原生窗口

SRealWnd控件

SOUI提供了SRealWnd控件来管理真窗口:

  • 继承自SWindow
  • 支持SOUI的布局系统
  • 管理真窗口的生命周期
  • 处理窗口状态同步

实现IRealWndHandler接口

接口定义

struct IRealWndHandler : public IObjRef {
    // 创建真窗口
    virtual HWND OnRealWndCreate(SRealWnd *pRealWnd) = NULL;

    // 销毁真窗口
    virtual void OnRealWndDestroy(SRealWnd *pRealWnd) = NULL;

    // 初始化窗口
    virtual BOOL OnRealWndInit(SRealWnd *pRealWnd) = NULL;

    // 调整窗口大小
    virtual BOOL OnRealWndSize(SRealWnd *pRealWnd) = NULL;
};

实现示例

class CSouiRealWndHandler : public TObjRefImpl2<IRealWndHandler, CSouiRealWndHandler> {
public:
    // 创建真窗口
    virtual HWND OnRealWndCreate(SRealWnd *pRealWnd) {
        const SRealWndParam &param = pRealWnd->GetRealWndParam();

        if(param.m_strClassName == _T("button")) {
            CButton *pbtn = new CButton;
            pbtn->Create(param.m_strWindowName,
                        WS_CHILD|WS_VISIBLE|BS_PUSHBUTTON,
                        CRect(0,0,0,0),
                        CWnd::FromHandle(pRealWnd->GetContainer()->GetHostHwnd()),
                        pRealWnd->GetID());
            pRealWnd->SetData(pbtn);
            return pbtn->m_hWnd;
        }
        return 0;
    }

    // 销毁真窗口
    virtual void OnRealWndDestroy(SRealWnd *pRealWnd) {
        const SRealWndParam &param = pRealWnd->GetRealWndParam();
        if(param.m_strClassName == _T("button")) {
            CButton *pbtn = (CButton*)pRealWnd->GetData();
            if(pbtn) {
                pbtn->DestroyWindow();
                delete pbtn;
            }
        }
    }

    // 初始化和大小调整(默认由SOUI处理)
    virtual BOOL OnRealWndInit(SRealWnd *pRealWnd) { return FALSE; }
    virtual BOOL OnRealWndSize(SRealWnd *pRealWnd) { return FALSE; }
};

XML布局配置

<SOUI title="SOUI-DEMO" width="600" height="400" appwin="1" ncRect="5,5,5,5" resize="1">
    <root skin="skin.bkframe" cache="1">
        <caption pos="0,0,-0,29">
            <text pos="11,9">带真窗口的示例</text>
        </caption>
        <window pos="0,29,-0,-0">
            <!-- 定义真窗口 -->
            <realwnd pos="10,10,-10,-10" 
                    name="mfcbtn" 
                    wndclass="button" 
                    id="100" 
                    wndname="MFC Button"/>
        </window>
    </root>
</SOUI>

realwnd标签属性

  • pos:位置和大小
  • name:控件名称
  • wndclass:真窗口的类名
  • id:控件ID
  • wndname:窗口标题

消息处理

在主窗口中处理真窗口消息

class CMainDlg : public SOUI::SHostDialog {
protected:
    // 处理按钮点击
    void OnBtnClick(UINT uNotifyCode, int nID, HWND wndCtl) {
        if(uNotifyCode == BN_CLICKED && nID == 100) {
            SMessageBox(m_hWnd, 
                       _T("真窗口按钮被点击!"), 
                       _T("提示"), 
                       MB_OK|MB_ICONINFORMATION);
        }
    }

    // 消息映射
    BEGIN_MSG_MAP_EX(CMainDlg)
        MSG_WM_COMMAND(OnBtnClick)
        CHAIN_MSG_MAP(SOUI::SHostDialog)
        REFLECT_NOTIFICATIONS_EX()
    END_MSG_MAP()
};

使用注意事项

1. 半透明限制

  • 包含真窗口的SOUI窗口不能设置translucent="1"
  • 真窗口在半透明窗口上无法正常显示
  • 此限制也适用于IE控件等

2. 生命周期管理

  • 正确实现创建和销毁接口
  • 注意内存泄漏
  • 保持窗口状态同步

3. 性能考虑

  • 适度使用真窗口
  • 注意窗口重绘效率
  • 合理处理消息

最佳实践

  1. 真窗口的选择
  2. 优先使用SOUI原生控件
  3. 必要时才使用真窗口
  4. 注意控件的兼容性

  5. 布局管理

  6. 合理设置位置和大小
  7. 考虑窗口重绘影响
  8. 正确处理Z序

  9. 事件处理

  10. 使用合适的消息映射
  11. 注意消息传递顺序
  12. 处理各类窗口状态

调试技巧

  1. 窗口层级检查

    // 在创建时验证父窗口
    HWND hParent = pRealWnd->GetContainer()->GetHostHwnd();
    ASSERT(::IsWindow(hParent));
    

  2. 位置验证

    // 检查窗口位置
    CRect rcReal;
    ::GetWindowRect(hRealWnd, &rcReal);
    ASSERT(!rcReal.IsRectEmpty());
    

  3. 消息跟踪

    // 添加消息跟踪
    TRACE(_T("Real window message: %d\n"), message);
    

总结

通过SOUI的SRealWnd控件,我们可以: - 方便地集成传统Windows控件 - 保持SOUI的布局特性 - 统一管理窗口生命周期 - 实现更丰富的界面功能