SOUI代码创建窗口¶
本文介绍如何在SOUI中使用代码动态创建和管理窗口。虽然SOUI推荐使用XML来创建窗口,但在某些场景下,代码创建窗口也是必要的。
窗口创建流程¶
1. 基本流程¶
- 创建窗口对象
- 插入到父窗口
- 初始化属性
- 布局更新
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);
}
}
};
注意事项¶
- 内存管理
- 使用工厂方法创建系统控件
- 自定义控件需要重写OnFinalRelease
-
及时清理不需要的窗口
-
布局更新
- 创建窗口后需要更新布局
- 改变窗口属性后可能需要重新布局
-
注意布局性能影响
-
资源释放
- 正确管理窗口生命周期
- 避免内存泄露
- 处理好父子窗口关系