Skip to content

SOUI代码创建窗口

Warning

The current page still doesn't have a translation for this language.

You can read it through google translate.

本文介绍如何在SOUI中使用代码动态创建和管理窗口。虽然SOUI推荐使用XML来创建窗口,但在某些场景下,代码创建窗口也是必要的。

窗口创建流程

1. 基本流程

  1. 创建窗口对象
  2. 插入到父窗口
  3. 初始化属性
  4. 布局更新

2. 代码实现

// 方式1:使用窗口工厂(推荐)
SWindow* pChild = SApplication::getSingleton().CreateWindowByName(L"button");
if(pChild)
{
    pParent->InsertChild(pChild);
    // 设置属性
    pChild->SetAttribute(L"pos", L"10,10,100,30");
    pChild->SetAttribute(L"text", L"按钮");
}

// 方式2:直接创建(自定义控件)
SMyWindow* pWindow = new SMyWindow();
if(pWindow)
{
    pParent->InsertChild(pWindow);
    // 设置属性
    pWindow->SetAttribute(L"pos", L"10,10,100,30");
}
  • 注意:使用new 创建自定义控件时,需要重写OnFinalRelease方法, 确保在自己当前的模板中delete this,防止跨模块内存交叉释放。

内存管理

1. 工厂创建

// 使用窗口工厂创建的窗口,内存由SOUI管理
void CreateFactoryWindow()
{
    SWindow* pWindow = SApplication::getSingleton()
        .CreateWindowByName(L"button");
    // 无需特殊处理内存释放
}

2. 自定义创建

// 自定义窗口需要处理内存释放
class SMyWindow : public SWindow
{
    SOUI_CLASS_NAME(SMyWindow, L"mywindow")
public:
    // 重写OnFinalRelease实现自己的内存管理
    virtual void OnFinalRelease() 
    {
        delete this;
    }
};

窗口布局

1. 自动布局

void UpdateLayout()
{
    // 更新子窗口位置
    UpdateChildrenPosition();

    // 获取客户区
    CRect rcClient;
    GetClientRect(&rcClient);

    // 发送大小改变消息
    SSendMessage(WM_SIZE, 0, 
        MAKELPARAM(rcClient.Width(), rcClient.Height()));
}

2. 手动布局

void ManualLayout()
{
    // 直接设置窗口位置
    CRect rc(0,0,100,30);
    pWindow->Move(rc);

    // 或使用SetAttribute设置
    pWindow->SetAttribute(L"pos", L"0,0,100,30");
}

使用示例

1. 动态创建按钮

void CreateDynamicButton(SWindow* pParent)
{
    // 创建按钮
    SWindow* pBtn = SApplication::getSingleton()
        .CreateWindowByName(L"button");
    if(pBtn)
    {
        // 插入到父窗口
        pParent->InsertChild(pBtn);

        // 设置属性
        pBtn->SetAttribute(L"pos", L"10,10,100,30");
        pBtn->SetAttribute(L"text", L"动态按钮");

        // 更新布局
        pParent->UpdateChildrenPosition();
    }
}

2. 创建自定义窗口

class SCustomWindow : public SWindow
{
    SOUI_CLASS_NAME(SCustomWindow, L"customwindow")
public:
    SCustomWindow() {}

    virtual void OnFinalRelease()
    {
        delete this;
    }

protected:
    void OnPaint(IRenderTarget* pRT)
    {
        // 自定义绘制
    }

    SOUI_MSG_MAP_BEGIN()
        MSG_WM_PAINT_EX(OnPaint)
    SOUI_MSG_MAP_END()
};

// 使用示例
void CreateCustomWindow(SWindow* pParent)
{
    SCustomWindow* pWindow = new SCustomWindow();
    if(pWindow)
    {
        pParent->InsertChild(pWindow);
        pWindow->SetAttribute(L"pos", L"10,10,200,100");
        pParent->UpdateChildrenPosition();
    }
}

3. 动态列表项

class SListGenerator
{
public:
    void GenerateItems(SListBox* pList, int nCount)
    {
        for(int i = 0; i < nCount; i++)
        {
            SWindow* pItem = SApplication::getSingleton()
                .CreateWindowByName(L"window");
            if(pItem)
            {
                pList->InsertChild(pItem);

                // 创建内容
                CreateItemContent(pItem, i);

                // 设置属性
                pItem->SetAttribute(L"height", L"30");
            }
        }

        // 更新列表
        pList->UpdateChildrenPosition();
    }

protected:
    void CreateItemContent(SWindow* pItem, int index)
    {
        // 创建文本
        SWindow* pText = SApplication::getSingleton()
            .CreateWindowByName(L"text");
        if(pText)
        {
            pItem->InsertChild(pText);
            pText->SetAttribute(L"pos", L"0,0,-0,-0");
            pText->SetAttribute(L"text", 
                SStringW().Format(L"项目 %d", index));
        }
    }
};

最佳实践

1. 窗口创建

class SWindowCreator
{
public:
    // 创建窗口的统一接口
    static SWindow* CreateWindow(SWindow* pParent,
        LPCWSTR pszName,
        const SStringW& strPos,
        const SStringW& strText = L"")
    {
        SWindow* pWindow = SApplication::getSingleton()
            .CreateWindowByName(pszName);
        if(pWindow)
        {
            pParent->InsertChild(pWindow);
            pWindow->SetAttribute(L"pos", strPos);
            if(!strText.IsEmpty())
            {
                pWindow->SetAttribute(L"text", strText);
            }
            pParent->UpdateChildrenPosition();
        }
        return pWindow;
    }
};

2. 资源管理

class SResourceManager
{
public:
    static void SafeRelease(SWindow* pWindow)
    {
        if(pWindow)
        {
            // 移除所有子窗口
            SWindow *pChild = pWindow->GetWindow(GSW_FIRSTCHILD);
            while(pChild)
            {
                SWindow *pNext = pChild->GetWindow(GSW_NEXTSIBLING);
                pChild->DestroyWindow();
                pChild = pNext;
            }

            // 销毁窗口
            pWindow->DestroyWindow();
        }
    }
};

3. 布局辅助

class SLayoutHelper
{
public:
    static void UpdateLayout(SWindow* pWindow)
    {
        if(pWindow)
        {
            // 更新子窗口位置
            pWindow->UpdateChildrenPosition();

            // 刷新窗口
            pWindow->InvalidateRect(NULL);
        }
    }

    static void SetWindowPos(SWindow* pWindow,
        int x, int y, int width, int height)
    {
        if(pWindow)
        {
            CRect rc(x, y, x + width, y + height);
            pWindow->Move(rc);
        }
    }
};

注意事项

  1. 内存管理
  2. 使用工厂方法创建系统控件
  3. 自定义控件需要重写OnFinalRelease
  4. 及时清理不需要的窗口

  5. 布局更新

  6. 创建窗口后需要更新布局
  7. 改变窗口属性后可能需要重新布局
  8. 注意布局性能影响

  9. 资源释放

  10. 正确管理窗口生命周期
  11. 避免内存泄露
  12. 处理好父子窗口关系