第二十二篇:在SOUI中使用代碼向視窗中插入子視窗

來源:互聯網
上載者:User

標籤:

使用SOUI開發用戶端UI程式,通常也推薦使用XML代碼來建立視窗,這樣建立的視窗使用方便,當視窗大小改變時,內部的子視窗也更容易協同變化。

但是最近不斷有網友諮詢如何使用代碼來建立SOUI子視窗,特此在這裡統一解答。

要回答這個問題,首先要瞭解SOUI視窗建立及布局的流程。

先從swnd.cpp裡抄一段建立子視窗的代碼:

 1     BOOL SWindow::CreateChildren(pugi::xml_node xmlNode) 2     { 3         TestMainThread(); 4         for (pugi::xml_node xmlChild=xmlNode.first_child(); xmlChild; xmlChild=xmlChild.next_sibling()) 5         { 6             if(xmlChild.type() != pugi::node_element) continue; 7  8             if(_wcsicmp(xmlChild.name(),KLabelInclude)==0) 9             {//在視窗布局中支援include標籤10                 SStringW strSrc = S_CW2T(xmlChild.attribute(L"src").value());11                 pugi::xml_document xmlDoc;12                 SStringTList strLst;13 14                 if(2 == ParseResID(strSrc,strLst))15                 {16                     LOADXML(xmlDoc,strLst[1],strLst[0]);17                 }else18                 {19                     LOADXML(xmlDoc,strLst[0],RT_LAYOUT);20                 }21                 if(xmlDoc)22                 {23                     CreateChildren(xmlDoc.child(KLabelInclude));24                 }else25                 {26                     SASSERT(FALSE);27                 }28             }else if(!xmlChild.get_userdata())//通過userdata來標記一個節點是否可以忽略29             {30                 SWindow *pChild = SApplication::getSingleton().CreateWindowByName(xmlChild.name());31                 if(pChild)32                 {33                     InsertChild(pChild);34                     pChild->InitFromXml(xmlChild);35                 }36             }37         }38         return TRUE;39     }

這個函數的功能是為this從XML中建立它的子視窗,主要注意代碼中紅色部分。

其中第30行是從SOUI的視窗類別廠根據XML結點名new出一個視窗對象。

第33行把建立出來的視窗插入到this的子視窗鏈表裡去。

而第34行的功能是從子視窗對應的XML結點初始化子視窗屬性。

很多網友以為只要完成上面步驟就應該可以顯示視窗了,但結果確大失所望。

SOUI一個重要特點就是能夠自動布局,這個過程的秘密就在於SWindow::OnRelayout方法。

 1     void SWindow::OnRelayout(const CRect &rcOld, const CRect & rcNew) 2     { 3         SWindow *pParent= GetParent(); 4         if(pParent) 5         { 6             pParent->InvalidateRect(rcOld); 7             pParent->InvalidateRect(rcNew); 8         }else 9         {10             InvalidateRect(m_rcWindow);11         }12 13         SSendMessage(WM_NCCALCSIZE);//計算非客戶區大小14 15         UpdateChildrenPosition();   //更新子視窗位置16 17         CRect rcClient;18         GetClientRect(&rcClient);19         SSendMessage(WM_SIZE,0,MAKELPARAM(rcClient.Width(),rcClient.Height()));20     }

當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>

 1 template<class T> 2 class TObjRefImpl :  public T 3 { 4 public: 5     TObjRefImpl():m_cRef(1) 6     { 7     } 8  9     virtual ~TObjRefImpl(){10     }11 12     //!添加引用13     /*!14     */15     virtual long AddRef()16     {17         return InterlockedIncrement(&m_cRef);18     }19 20     //!釋放引用21     /*!22     */23     virtual long Release()24     {25         long lRet = InterlockedDecrement(&m_cRef);26         if(lRet==0)27         {28             OnFinalRelease();29         }30         return lRet;31     }32 33     //!釋放對象34     /*!35     */36     virtual void OnFinalRelease()37     {38         delete this;39     }40 protected:41     volatile LONG m_cRef;42 };43 44 template<class T,class T2>45 class TObjRefImpl2 :  public TObjRefImpl<T>46 {47 public:48     virtual void OnFinalRelease()49     {50         delete static_cast<T2*>(this);51     }52 };

注意代碼中的OnFinalRelease,它是一個虛方法。因此對於使用new建立的視窗對象,只需要在視窗類別中抄一段代碼如下即可:

1 class myctrl : public SWindow2 {3 SOUI_CLASS_NAME(myctrl,L"myctrl")4 public:5 //...6 virtual void OnFinalRelease() {delete this;}7 //...8 };

感謝大家的支援!

 

第二十二篇:在SOUI中使用代碼向視窗中插入子視窗

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.