第二十一篇:SOUI中的控件注册机制

Win32编程中,用户需要一个新控件时,需要向系统注册一个新的控件类型。注册以后,调用::CreateWindow时才能根据标识控件类型的字符串创建出一个新的控件窗口对象。

为了能够从XML描述的字符串中创建出需要的控件对象,和Win32类似,在SOUI中要创建一个新的控件也同样需要向SOUI系统注册新的控件类。

从demo.cpp的main中我们可以看到类似如下的控件注册控件的代码:

        //向SApplication系统中注册由外部扩展的控件及SkinObj类
        SWkeLoader wkeLoader;
        if(wkeLoader.Init(_T("wke.dll")))
        {
            theApp->RegisterWndFactory(TplSWindowFactory<SWkeWebkit>());//注册WKE浏览器
        }
        theApp->RegisterWndFactory(TplSWindowFactory<SGifPlayer>());//注册GIFPlayer
        theApp->RegisterSkinFactory(TplSkinFactory<SSkinGif>());//注册SkinGif
        theApp->RegisterSkinFactory(TplSkinFactory<SSkinAPNG>());//注册SSkinAPNG
        theApp->RegisterSkinFactory(TplSkinFactory<SSkinVScrollbar>());//注册纵向滚动条皮肤

        theApp->RegisterWndFactory(TplSWindowFactory<SIPAddressCtrl>());//注册IP控件
        theApp->RegisterWndFactory(TplSWindowFactory<SPropertyGrid>());//注册属性表控件
        theApp->RegisterWndFactory(TplSWindowFactory<SChromeTabCtrl>());//注册ChromeTabCtrl
        theApp->RegisterWndFactory(TplSWindowFactory<SIECtrl>());//注册IECtrl
        theApp->RegisterWndFactory(TplSWindowFactory<SChatEdit>());//注册ChatEdit
        theApp->RegisterWndFactory(TplSWindowFactory<SScrollText>());//注册SScrollText

        if(SUCCEEDED(CUiAnimation::Init()))
        {
            theApp->RegisterWndFactory(TplSWindowFactory<SUiAnimationWnd>());//注册动画控件
        }
        theApp->RegisterWndFactory(TplSWindowFactory<SFlyWnd>());//注册飞行动画控件
        theApp->RegisterWndFactory(TplSWindowFactory<SFadeFrame>());//注册渐显隐动画控件
        theApp->RegisterWndFactory(TplSWindowFactory<SRadioBox2>());//注册渐显隐动画控件

上面代码中即有新皮肤对象的注册也有窗口控件的注册,这种注册控件方式看起来有点怪异,但使用起来也还算是简单,只需要一行代码(实际上是从著名的游戏GUI CEGUI借鉴过来的)。

以注册窗口控件为例,下面我来解释一下为什么会有这样一种控件注册方式。

先看看TplSWindowFactory模板类的实现:

    class SWindowFactory
    {
    public:
        virtual ~SWindowFactory() {}
        virtual SWindow* NewWindow() = 0;
        virtual LPCWSTR SWindowBaseName()=0;

        virtual const SStringW & getWindowType()=0;

        virtual SWindowFactory* Clone() const =0;
    };

    template <typename T>
    class TplSWindowFactory : public SWindowFactory
    {
    public:
        //! Default constructor.
        TplSWindowFactory():m_strTypeName(T::GetClassName())
        {
        }

        LPCWSTR SWindowBaseName(){return T::BaseClassName();}

        // Implement WindowFactory interface
        virtual SWindow* NewWindow()
        {
            return new T;
        }

        virtual const SStringW & getWindowType()
        {
            return m_strTypeName;
        }

        virtual SWindowFactory* Clone() const
        {
            return new TplSWindowFactory();
        }
    protected:
        SStringW m_strTypeName;
    };
TplSWindowFactory从SWindowFactory派生,自动为模板参数中指定的类型生成一个SWindowFactory对象。
SWindowFactory有什么用呢?SWindowFactory最核心的用途就在于它的方法:SWindow * SWindowFactory::NewWindow();该方法说来很简单,不过是根据不同的字符串从堆上分配一个SWindow对象,但是在C++中我们需要满足一条基本原则:内存在哪个模块中分配(从哪个模块的堆上分配)就必须被哪个模块释放。在SOUI中,通常情况下控件都是由SOUI.DLL这个模块来分配内存的,也就是说SWindow * SWindowFactory::NewWindow()只会在SOUI模块中调用。

那么生成的对象在哪里释放呢?这就需要看一下SWindow或者SSkinObjBase这两个类的实现了。
    class SOUI_EXP SWindow : public SObject
        , public SMsgHandleState
        , public TObjRefImpl2<IObjRef,SWindow>
    {
        SOUI_CLASS_NAME(SWindow, L"window")
        //....
    };

    class SOUI_EXP SSkinObjBase : public TObjRefImpl<ISkinObj>
    {
       //......
    };
SWindow和SSkinObjBase实际上都派生自TObjRefImpl这个模板类。下面看一下这个类的实现:
template<class T>
class TObjRefImpl :  public T
{
public:
    TObjRefImpl():m_cRef(1)
    {
    }

    virtual ~TObjRefImpl(){
    }

    //!添加引用
    /*!
    */
    virtual void AddRef()
    {
        InterlockedIncrement(&m_cRef);
    }

    //!释放引用
    /*!
    */
    virtual void Release()
    {
        InterlockedDecrement(&m_cRef);
        if(m_cRef==0)
        {
            OnFinalRelease();
        }
    }

    //!释放对象
    /*!
    */
    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);
    }
};
TObjRefImpl一个重要的虚函数是void OnFinalRelease(){delete this;}

由于SWindow及SSkinObjBase是在SOUI模块中实现的,因此派生自这两个类的新的控件类及皮肤类最后都将在SOUI模块中被释放,从而保证了对象内存的分配、释放在一个模块。这也就是说不管是SOUI模块内实现的控件还是在应用层扩展的控件一般情况下都应该向SOUI系统注册并经由SOUI内核来实现对象的分配与释放。

那么问题来了,是不是所有新控件都必须向系统注册呢?当然不是的,注意OnFinalRelease是一个虚函数,只需要在新的控件类中增加一个
void OnFinalRelease(){delete this;}就可以把控件内存的释放转移到应用层的模块了。如此你就可以在自己的模块中直接使用new来创建新控件了。(可以参考属性表控件中部分子控件的实现)
时间: 2024-08-03 10:51:36

第二十一篇:SOUI中的控件注册机制的相关文章

第二十九篇:使用SOUI的SMCListView控件

列表控件是客户端应用最常用的控件之一.列表控件通常只负责显示数据,最多通知一下APP列表行的选中状态变化. 现在的UI经常要求程序猿在列表控件里不光显示内容,还要能和用户交互,显示动画等等,传统的列表控件对于这样的需求基本是无能为力了. Android开发中很多界面都直接采用ListView实现,ListView中每一个Item中都可以容纳其它控件,这样的设计使得在表项中的交互和在主面板上交互一样简单. 虽然在列表项中容纳其它控件并不是什么新的思想,考虑到列表中的数据量是不确定的,如果给每一个表

学习IOS开问题篇--视图的模型控件属性写在私有分类中的原因

在说原型模式之前,我们先来看java里面的深复制和浅复制: 1. 浅复制:被复制的对象的所有变量都持有和原来对象的变量相同的值,而所有的对其他对象的引用都指向原来的对象. 2. 深复制:被复制对象的所有变量都含有与原来对象相同的值,除去那些引用其他变量的对象.那些引用其他对象的变量将指向被复制过来的新对象,而不是原来那些被引用的对象.深复制需要把要复制的对象的所有引用都复制一遍. 这两者的区别就是关于引用对象的处理,浅复制是不考虑引用对象,而深复制需要考虑引用对象的问题. 对java中的clon

Android自定义控件系列 十:利用添加自定义布局来搞定触摸事件的分发,解决组合界面中特定控件响应特定方向的事件

这个例子是比较有用的,基本上可以说,写完这一次,以后很多情况下,直接拿过来addView一下,然后再addInterceptorView一下,就可以轻轻松松的达到组合界面中特定控件来响应特定方向的触摸事件了. 请尊重原创劳动成果,转载请注明出处:http://blog.csdn.net/cyp331203/article/details/45198549,非允许请勿用于商业或盈利用途,违者必究. 在写Android应用的过程之中,经常会遇到这样的情况:界面包含了多个控件,我们希望触摸在界面上的不

Android中常用控件及属性

在之前的博客为大家带来了很多关于Android和jsp的介绍,本篇将为大家带来,关于Andriod中常用控件及属性的使用方法,目的方便大家遗忘时,及时复习参考.好了废话不多讲,现在开始我们本篇内容的介绍. 1.控制应用显示的方向: setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);//竖直显示效果. setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LA

iOS开发UI篇—自定义瀑布流控件(蘑菇街数据刷新操作)

iOS开发UI篇—自定义瀑布流控件(蘑菇街数据刷新操作) 一.简单说明 使用数据刷新框架: 该框架提供了两种刷新的方法,一个是使用block回调(存在循环引用问题,_ _weak),一个是使用调用. 问题:在进行下拉刷新之前,应该要清空之前的所有数据(在刷新数据这个方法中). 移除正在显示的cell: (1)把字典中的所有的值,都从屏幕上移除 (2)清除字典中的所有元素 (3)清除cell的frame,每个位置的cell的frame都要重新计算 (4)清除可复用的缓存池. 该部分的代码如下: 1

Python开发【第二十一篇】:Web框架之Django【基础】

Python开发[第二十一篇]:Web框架之Django[基础] 猛击这里:http://www.cnblogs.com/wupeiqi/articles/5237704.html Python之路[第十六篇]:Django[基础篇] Python的WEB框架有Django.Tornado.Flask 等多种,Django相较与其他WEB框架其优势为:大而全,框架本身集成了ORM.模型绑定.模板引擎.缓存.Session等诸多功能. 基本配置 一.创建django程序 终端命令:django-a

WPF中Ribbon控件的使用

这篇博客将分享如何在WPF程序中使用Ribbon控件.Ribbon可以很大的提高软件的便捷性. 上面截图使Outlook 2010的界面,在Home标签页中,将所属的Menu都平铺的布局,非常容易的可以找到想要的Menu.在Outlook 2003时代,将Home下面的Menu都垂直的排列下来,操作的便捷程度降低了很多.Ribbon的布局会随着窗体的变化动态的调整. 上面的图片中标注了Ribbon的4个区块. 下面我们就在WPF中使用Ribbon控件来实现一个简单的界面. 1. 添加System

.NET中TextBox控件设置ReadOnly=true后台取不到值三种解决方法

.NET中TextBox控件设置ReadOnly=true后台取不到值三种解决方法 当TextBox设置了ReadOnly=true后要是在前台为控件添加了值,后台是取不到的,值为空,多么郁闷的一个问题经过尝试,发现可以通过如下的方式解决这个问题.感兴趣的朋友可以了解下 当TextBox设置了ReadOnly="true" 后,要是在前台为控件添加了值,后台是取不到的,值为“空” 原理没想通,说不清楚微软是出于什么考虑的,不过有时是要我们能通过前台脚本来填充值,并不希望用户修改其控件内

iOS开发UI篇—DatePicker和UIToolBar控件简单介绍

iOS开发UI篇—DatePicker和UIToolBar控件简单介绍 一.Date Picker控件 1.简单介绍: Date Picker显示时间的控件 有默认宽高,不用设置数据源和代理 如何改成中文的? (1)查看当前系统是否为中文的,把模拟器改成是中文的 (2)属性,locale选择地区 如果默认显示不符合需求.时间有四种模式可以设置,在model中进行设置 时间可以自定义(custom). 设置最小时间和最大时间,超过就会自动回到最小时间. 最大的用途在于自定义键盘:弹出一个日期选择器