最新消息:想得多,做的少。一天到晚瞎鸡巴搞。

duilib源码剖析 day3 WM_CREATE干了什么

曲径通幽 阿虚 514浏览 0评论

.      在WM_CREATE事件中一共12行代码,先看前三行干啥了。

//初始化
m_pm.Init(m_hWnd);
//构造了一个新的类
CDialogBuilder builder;
//加载XML资源
CControlUI* pRoot = builder.Create(_T("test1.xml"), (UINT)0, NULL, &m_pm);

.      第一行是调用的CPaintManagerUI初始化功能,这个函数负责Remove一些内部东西和设置参数传递进去的窗口句柄,以及创建一个DC。移除什么东西看名字大概猜为:字体、图片、默认属性列表、窗口自定义属性、选项组、定时器。这些东西怎么定义的先不管。先过,用到了在仔细琢磨。

.      第二行是构造了一个对话框生成器(CDialogBuilder)类。这个类并没有继承任何基类。这个类看头文件挺简单的,只有二个Create()、GetMarkup()、GetLastErrorMessage()、GetLastErrorLocation()、_Parse()。属性有CMarkup m_xml、IDialogBuilderCallback m_pCallback、LPCTSTR m_pstrtype。

.      第三行参是调用了CDialogBuilder类的Create()函数参数分别test.xml字符串,这个xml就是这个工程UI所定义的XML名称,参数二和参数三都是0,参数四是成员函数CPaintManagerUI类的地址。这个类传递了UI的XML再次跟进看看对XML做了什么。

第一个CDialogBuilder::Create()做了什么

.      通过CDialogBuilder头文件可以看Create有两个重载函数.

CControlUI* Create(STRINGorID xml, LPCTSTR type = NULL, 
        IDialogBuilderCallback* pCallback = NULL,
        CPaintManagerUI* pManager = NULL, CControlUI* pParent = NULL);
CControlUI* Create(IDialogBuilderCallback* pCallback = NULL,                
        CPaintManagerUI* pManager = NULL, CControlUI* pParent = NULL);

.      通过判断函数传入参数可以确定进入的位第一个Create(),函数内部逻辑有三种条件逻辑。

.      1.判断xml是否存在字符串,如果存在字符串并且字符串第一个字符为‘<’则认为传递进来的就是XMLBUF。将buf拷贝拷贝到new的堆中由成员变量m_pstrXML指向堆首地址。

.      2.如果字符串首地址不为‘<’那么则将字符串传递给m_xml.LoadFromFile()由这个函数处理。

.      3. xml.m_lpstr没有字符串那么则通过CPaintManagerUI::GetResourceDll()静态函数返回的路径搜索是否存在资源DLL,如果没有返回关闭程序。如果有则将资源DLL加载内存中将资源DLL内存buf首地址交给m_xml.LoadFromMem()函数处理。

.      上述三条步骤都是为了获取到程序需要加载的UIXML文件,得到XML文件并且将他加载到内存中。最终正真对XML节点解析的是在第二个Create()来做进一步处理。

CMarkup类做了什么

.      CMarkup类是一款开源的XML解析库,将XML文件加载到内存中并且解析出所有的KEY/VAL。官方网站为http://www.firstobject.com/dn_markup.htm。当前解析XML完毕后将调用第二个Create()函数。

第二个CDialogBuilder::Create()做了什么

.      函数一开始开始做了两件事给IDialogBuilderCallback* m_pCallback;回调函数指针赋值,但是在调试时在WM_CTEATE这个消息中此值为空。看名字猜测应该是和对话框构造时可以提供一个回调函数处理点什么其他的事情。而后通过来CMarkupNode root = m_xml.GetRoot();返回一个XML解析后的root节点判断是否正确解析了XML文件,如果root节点获取错误直接返回NULL。

CMarkupNode root = m_xml.GetRoot();
    if( !root.IsValid() ) 
        return NULL;

.      接下来一个判断逻辑CPaintManagerUI* pManager是否指针为空,当然这个 CPaintManagerUI本身就是继承CWindowWnd的窗口子类CFrameWindowWnd的成员函数。指针为空表示失败则退出返回退出程序。接下来是一个for循环遍历所有XML节点。

for( CMarkupNode node = root.GetChild() 
    ; node.IsValid()
    ; node = node.GetSibling() )

.      这个for循环可以看出有处理4条分支:Image、Font、Default、MultiLanguage这四个分别对应test1.xml里面的属性类型(图片、默认字体、默认属性列表、多语言)。4个分支全部做的一件事情那么就是获取需要设置的属性和属性值。获取属性后分别调用CPaintManagerUI类成员函AddImage、SetDefaultFont、AddDefaultAttributeList、AddMultiLanguageString数存储起来。

.      出了循环函数后获取Root节点的信息判断是否为WIndow如果是那么则取属性SetWindowAttribute存起来。最后CDialogBuilder::_Parse()调用来做解析。

CDialogBuilder::_Parse()函数做了什么

.      这个解析(_Parse())函数做的事情是除了第二个Create一开始解析的(Image、Font、Default、MultiLanguage)属性外所有的控件信息都在这里面解析。Duilib所有支持的控件有Include、TreeNode、Edit、List、Text、Tree、HBox、VBox、Combo、  Label、Button、Option、Slider、Control、ActiveX、GifAnim、Progress、RichEdit、CheckBox、ComboBox、DateTime、TreeView、TreeNode、Container、TabLayout、ScrollBar、ListHeader、TileLayout、WebBrowser、ChildLayout、VerticalLayout、ListHeaderItem、ListTextElement、ListHBoxElement、HorizontalLayout、ListLabelElement、ListContainerElement这么多。在这个函数你可以找到所有控件的类,并且可以看出所有控件都继承与CControlUI类。

.      函数大概需要分几段来说,第一段除了撇开的那么应该是特殊处理Include和TreeNode两个属性。Include寓意包含另一个XML文件,那么对于XML就需要使用CDialogBuilder:: Create()来构建和解析XML文件。也就是目前正在分析的类。TreeNode则需要处理获取节点属性,解析TreeNode的子节点,将节点添加到CTreeViewUI中等等。控件这方面我没有细看后续针对控件再说。

.      第二段则是上一节说的解析控件名称new控件。

.      第三段有个注释// User-supplied control factory说用户提供控件工程我这里还真没弄明白什么意思,先不管。

.      第四段就是解析子节点了。如果存在子节点那么则会循环递归_Parse()函数。

.      第五段如果存在父控件那么则将子控件添加到父控件中。

.      第六步设置默认属性。

.      第七步设置用户自配置的属性。

.      上述七步就是CDialogBuilder::_Parse()所做的事情。

CDialogBuilder总结

.      CDialogBuilder类负责解析XML和按照XML文档所定义的组装控件需要使用的属性和组装控件。组装后的信息全部存储在CPaintManagerUI中。

后续

.      从builder.Create()出来之后,下面几行做的是m_pm.AttachDialog()返回的Root节点,由成员函数CControlUI* m_pRoot;保存,除了保存Root节点,还对Root节点内所有的控件进行初始化操作。m_pm.AddNotifier(this);将当前窗体添加到消息通知中。由CDuiPtrArray m_aNotifiers;保存通知窗体列表。需要接受通知需要继承INotifyUI接口并且实现纯虚函数Notify()。

//附加根节点
m_pm.AttachDialog(pRoot);
//添加通知
m_pm.AddNotifier(this);

.      CWndShadow目前对UI其实还特别不了解并,CWndShadow* m_pWndShadow;并没有在其他地方多次使用目前先放着。但是m_pWndShadow->Create(m_hWnd);中却修改了CFrameWindowWnd设置的消息循环回调函数,先指向了CWndShadow的ParentProc()在通过函数指针调用CWindowWnd::__WndProc。

.      CWndShadow:: ParentProc()处理了WM_MOVE、WM_SIZE、WM_PAINT、WM_EXITSIZEMOVE、WM_SHOWWINDOW、WM_DESTROY以及WM_NCDESTROY消息。但是直接将CWndShadow的内容注释掉也并不影响窗体的使用。

m_pWndShadow = new CWndShadow;
m_pWndShadow->Create(m_hWnd);
RECT rcCorner = { 3, 3, 4, 4 };
RECT rcHoleOffset = { 0, 0, 0, 0 };
m_pWndShadow->SetImage(_T("LeftWithFill.png"), rcCorner, rcHoleOffset);

.      SetWindowAttribute()函数是CDwm类的成员函数,CDwm类通过查询MSDN得知这个是Vitas新增加的函数。主要是对窗体开启毛玻璃透明度等效果。但是将基类CDwm给注释掉后还是能正常设置窗体透明度。

//窗口的特殊属性,将非客户区的属性延伸到客户区
DWMNCRENDERINGPOLICY ncrp = DWMNCRP_ENABLED;
SetWindowAttribute(
m_hWnd, DWMWA_TRANSITIONS_FORCEDISABLED, &ncrp, sizeof(ncrp));

.      最后Init();其实是个空函数。

转载请注明:虚无 » duilib源码剖析 day3 WM_CREATE干了什么

发表我的评论
取消评论

表情

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址