使用SOUI开发客户端UI程序,通常也推荐使用XML代码来创建窗口,这样创建的窗口使用方便,当窗口大小改变时,内部的子窗口也更容易协同变化。

但是最近不断有网友咨询如何使用代码来创建SOUI子窗口,特此在这里统一解答。

要回答这个问题,首先要了解SOUI窗口创建及布局的流程。

先从swnd.cpp里抄一段创建子窗口的代码:

     BOOL SWindow::CreateChildren(pugi::xml_node xmlNode)
{
TestMainThread();
for (pugi::xml_node xmlChild=xmlNode.first_child(); xmlChild; xmlChild=xmlChild.next_sibling())
{
if(xmlChild.type() != pugi::node_element) continue; if(_wcsicmp(xmlChild.name(),KLabelInclude)==)
{//在窗口布局中支持include标签
SStringW strSrc = S_CW2T(xmlChild.attribute(L"src").value());
pugi::xml_document xmlDoc;
SStringTList strLst; if( == ParseResID(strSrc,strLst))
{
LOADXML(xmlDoc,strLst[],strLst[]);
}else
{
LOADXML(xmlDoc,strLst[],RT_LAYOUT);
}
if(xmlDoc)
{
CreateChildren(xmlDoc.child(KLabelInclude));
}else
{
SASSERT(FALSE);
}
}else if(!xmlChild.get_userdata())//通过userdata来标记一个节点是否可以忽略
{
SWindow *pChild = SApplication::getSingleton().CreateWindowByName(xmlChild.name());
if(pChild)
{
InsertChild(pChild);
pChild->InitFromXml(xmlChild);
}
}
}
return TRUE;
}

这个函数的功能是为this从XML中创建它的子窗口,主要注意代码中红色部分。

其中第30行是从SOUI的窗口类厂根据XML结点名new出一个窗口对象。

第33行把创建出来的窗口插入到this的子窗口链表里去。

而第34行的功能是从子窗口对应的XML结点初始化子窗口属性。

很多网友以为只要完成上面步骤就应该可以显示窗口了,但结果确大失所望。

SOUI一个重要特点就是能够自动布局,这个过程的秘密就在于SWindow::OnRelayout方法。

     void SWindow::OnRelayout(const CRect &rcOld, const CRect & rcNew)
{
SWindow *pParent= GetParent();
if(pParent)
{
pParent->InvalidateRect(rcOld);
pParent->InvalidateRect(rcNew);
}else
{
InvalidateRect(m_rcWindow);
} SSendMessage(WM_NCCALCSIZE);//计算非客户区大小 UpdateChildrenPosition(); //更新子窗口位置 CRect rcClient;
GetClientRect(&rcClient);
SSendMessage(WM_SIZE,,MAKELPARAM(rcClient.Width(),rcClient.Height()));
}

当this窗口位置改变后都会进入OnRelayout方法。

注意函数第15行:UpdateChildrenPosition();这个调用的功能就是将this的所有子控件按照控件中定义的布局属性来自动布局。

要实现窗口的布局,除了调用父窗口的UpdateChildrenPosition()方法外,当然也可以使用SWindow::Move方法直接修改窗口位置。

看到这里大家应该已经明白是什么原因了。

当然上述方法是SOUI中使用的窗口创建及布局方法,具体到应用程序中使用代码创建控件还有一个地方需要注意。

关键的问题是在SOUI系统中编译默认使用MT(静态链接)方式来链接CRT。

MT方式编译时使用CRT分配内存是内存是属性调用的模块(DLL)的,内存的释放也因此必须在该模块内执行。

如果用户直接使用new来分配一个窗口对象,并把它插入到SOUI窗口链表中,在窗口释放的时机是在SWindow::OnFinalRelease()中(实际是基类TObjRefImpl2<IObjRef,SWindow>的方法)。

SWindow的代码段是编译在soui.dll中,因此默认执行内存释放的代码是在soui.dll中,从而导致程序崩溃。

要解决这个问题有两种方法:

对于系统控件,用户应该使用SApplication::getSingleton().CreateWindowByName(xmlChild.name());来创建窗口对象。

而对于用户自己派生实现的扩展窗口类并没有向SOUI的窗口类厂注册时,只能使用new方法来创建窗口对象。注意SWindow的基类:TObjRefImpl2<IObjRef,SWindow>

 template<class T>
class TObjRefImpl : public T
{
public:
TObjRefImpl():m_cRef()
{
} virtual ~TObjRefImpl(){
} //!添加引用
/*!
*/
virtual long AddRef()
{
return InterlockedIncrement(&m_cRef);
} //!释放引用
/*!
*/
virtual long Release()
{
long lRet = InterlockedDecrement(&m_cRef);
if(lRet==)
{
OnFinalRelease();
}
return lRet;
} //!释放对象
/*!
*/
virtual void OnFinalRelease()
{
delete this;
}
protected:
volatile LONG m_cRef;
}; template<class T,class T2>
class TObjRefImpl2 : public TObjRefImpl<T>
{
public:
virtual void OnFinalRelease()
{
delete static_cast<T2*>(this);
}
};

注意代码中的OnFinalRelease,它是一个虚方法。因此对于使用new创建的窗口对象,只需要在窗口类中抄一段代码如下即可:

 class myctrl : public SWindow
{
SOUI_CLASS_NAME(myctrl,L"myctrl")
public:
//...
virtual void OnFinalRelease() {delete this;}
//...
};

感谢大家的支持!

第二十二篇:在SOUI中使用代码向窗口中插入子窗口的更多相关文章

  1. Python开发【第二十二篇】:Web框架之Django【进阶】

    Python开发[第二十二篇]:Web框架之Django[进阶]   猛击这里:http://www.cnblogs.com/wupeiqi/articles/5246483.html 博客园 首页 ...

  2. 第二十八篇:SOUI中自定义控件开发过程

    在SOUI中已经提供了大部分常用的控件,但是内置控件不可能满足用户的所有要求,因此一个真实的应用少不得还要做一些自定义控件. 学习一个新东西,最简单的办法就是依葫芦画瓢.事实上在SOUI系统中内置控件 ...

  3. 第二十二篇:C++中的多态机制

    前言 封装性,继承性,多态性是面向对象语言的三大特性.其中封装,继承好理解,而多态的概念让许多初学者感到困惑.本文将讲述C++中多态的概念以及多态的实现机制. 什么是多态? 多态就是多种形态,就是许多 ...

  4. 【第二十二篇】从客户端中检测到有潜在危险的 Request.Form 值

    提交数据的时候  用js的方法   escape(富文本框的值)    例:escape(UM.getEditor('Content').getContent()); 取值的时候   unescape ...

  5. 第二十二篇、IO多路复用 一

    一.简介io多路复用 可以监听多个文件描述符(socket对象)(文件句柄),一旦文件句柄出现变化,就会感知到 Linux中的 select,poll,epoll(内核2.6以上) 都是IO多路复用的 ...

  6. Python之路【第二十二篇】CMDB项目

    浅谈ITIL TIL即IT基础架构库(Information Technology Infrastructure Library, ITIL,信息技术基础架构库)由英国政府部门CCTA(Central ...

  7. 第十二篇:SOUI的utilities模块为什么要用DLL编译?

    SOUI相对于DuiEngine一个重要的变化就是很多模块变成了一个单独的DLL. 然后很多情况下用户可能希望整个产品就是一个EXE,原来DuiEngine提供了LIB编译模式,此时链接LIB模式的D ...

  8. Python之路【第二十二篇】:Django之Model操作

    Django之Model操作   一.字段 AutoField(Field) - int自增列,必须填入参数 primary_key=True BigAutoField(AutoField) - bi ...

  9. Python之路(第二十二篇) 面向对象初级:概念、类属性

    一.面向对象概念 1. "面向对象(OOP)"是什么? 简单点说,“面向对象”是一种编程范式,而编程范式是按照不同的编程特点总结出来的编程方式.俗话说,条条大路通罗马,也就说我们使 ...

随机推荐

  1. python 反射

    python 反射的核心本质其实就是利用字符串的形式去对象(模块)中操作(查找/获取/删除/添加)成员,一种基于字符串的事件驱动! 反射的四个基本函数使用 hasattr,getattr,setatt ...

  2. java enum

    小谈Java Enum的多态性 博客分类: Java JavaAppleJDKJVMIDEA  Enum+多态,我没说错,不过Enum是不可以被继承的,也不可以继承自别人,只是能实现接口而已,何谈多态 ...

  3. Hive : UDFArgumentTypeException Exactly one argument is expected.

    Hive执行时Failed.. 分段执行发现排除一些聚合函数或者内置函数后可以正常执行.. 因为Hive-Sql语句比较长..有很多的case when then 排除后发现是聚合函数的用法问题.. ...

  4. Window系统性能获取帮助类

    前言: 这个是获取Windows系统的一些性能的帮助类,其中有:系统内存.硬盘.CPU.网络(个人测试还是比较准的).Ping.单个进程的内存.Cpu.网络(不准).    最初在这个的时候在各种搜索 ...

  5. linux查看端口及端口详解

    今天现场查看了TCP端口的占用情况,如下图   红色部分是IP,现场那边问我是不是我的程序占用了tcp的链接,,我远程登陆现场查看了一下,这种类型的tcp链接占用了400多个,,后边查了一下资料,说E ...

  6. 开发thinkphp的第一步就是给Application目录(不包括其下的文件)777权限, 关闭selinux

    开发thinkphp的时候, 总是会出现各种个样 的奇怪的毛病, 比如: 说什么Application目录不可写, 比如: 说什么 _STORAGE_WRITE_ERROR, 不能生成 Runtime ...

  7. Xcode 快捷键、常用技巧

    关于iOS开发中的技能快捷键 经常使用鼠标太TM的D疼了,快捷键能大大地提高我们的开发速度,使我们的手指尽情的在键盘上飞舞,优美的代码,哈哈哈,那些常规的复制.粘贴.剪切请自行度娘或者Google一下 ...

  8. Timequest静态时序分析(STA)基础

    Setup Slack Hold Slack Recovery&Removal Recovery: The minimum time an asynchronous signal must b ...

  9. Maven生命周期小记

    1.Maven生命周期是为了所有的构建过程进行抽象和统一.Maven从大量的项目和构建工具中学习和反思,总结了一套高度完善.易扩展的生命周期.这个生命周期包含了项目的清理.初始化.编译.测试.打包.集 ...

  10. Azure上的那些IP

    相信第一次接触Azure的读者都会碰到这样一个问题,就是Azure的IP地址,笔者第一次接触Azure也是被搞懵逼了,一会儿VIP,不知道的还以为是会员的意思呢,一会儿又是DIP,后来又来了个PIP, ...