DHTML设计VC界面《五》- 容器

前文所描述的是 标签栏的UI实现,但是实际上我们还要给程序加上容器的概念,才能让不仅仅标签切换而且功能也要切换。

切换的原理是简单的,就是给每个功能模块创建一个子Dialog,切换到一个标签,隐藏其他Dilaog显示当前标签对应的Dialog。

这点一般程序员都很容易做到。

本文在这里抛砖引玉用模板Template的概念书写一个TabManager类,用更加好看的代码(动态类创建)来实现这个技术。

 

 

     一般来说创建一个子Dialog,常规的做法是 CDialog * pDlg = new CMyDialog(); 如果是10个Dialog那么就需要预先创建10个  Dialog,如何在程序初始化的时候只是存储10个子Dialog的类名,到想要创建的时候自动创建呢?VC一般来说是不允许动态创建类的,在Doc/View结构下有一个RUNTIME_CLASS宏可以做到,但是这只是在doc/view里面才能这么用,作者这里书写了一个所有类都能使用的动态类管理容器。笔者只所以在这里用简单的应用来诠释复杂的技术,是因为如果读者有机会接触到插件Plugin编程的时候就会遇到类似的问题。

 

  1. //Runtime Class Fectory
  2. class RuntimeClassFactory
  3. {
  4. private:
  5.     typedef void* (* RUNTIME_Create_REF)();
  6.     typedef void(* RUNTIME_Delete_REF)(void*) ;
  7. protected:
  8.     struct func_ref
  9.     {
  10.         RUNTIME_Create_REF procCreate;
  11.         RUNTIME_Delete_REF procDelete;
  12.     };
  13.     typedef hash_map< std::string, RuntimeClassFactory::func_ref> class_map;
  14. public:
  15.     static class_map * s_map;
  16. public:
  17.     static void register_class( std::string name, RUNTIME_Create_REF p,RUNTIME_Delete_REF p2 )
  18.     {
  19.         if(!s_map)
  20.             s_map = new class_map;
  21.         func_ref ref;
  22.         ref.procCreate = p;
  23.         ref.procDelete = p2;
  24.         (*s_map)[name] = ref;
  25.     }
  26.     static void Clear()
  27.     {
  28.         if(s_map)
  29.             delete s_map;
  30.         s_map = 0;
  31.     }
  32.     static void* Create( std::string name )
  33.     {
  34.         if(s_map)
  35.         {
  36.             class_map::iterator item = s_map->find(name);
  37.             if(item!=s_map->end())
  38.                 return item->second.procCreate();
  39.             else
  40.                 return 0;
  41.         }
  42.         else
  43.             return 0;
  44.     }
  45.     static void Delete(std::string name,void *p)
  46.     {
  47.         if(s_map)
  48.         {
  49.             class_map::iterator item = s_map->find(name);
  50.             if(item!=s_map->end())
  51.             {
  52.                 item->second.procDelete(p);
  53.             }
  54.         }
  55.     }
  56. };
  57. template<typename T>
  58. struct RegisterClassHelper
  59. {
  60.     RegisterClassHelper(const std::string class_name)
  61.     { 
  62.         RuntimeClassFactory::register_class( class_name, &RegisterClassHelper<T>::create_object,&RegisterClassHelper::delete_object ); 
  63.     }
  64.     static void* create_object(){ return new T; }
  65.     static void delete_object(void* p){delete (T*)p;}
  66. };  
  67. #define REGISTER_CLASS(X) /
  68.     {/
  69.     RegisterClassHelper<X> __class_help(_T(#X));/
  70.     }
  71. #define REGISTER_CLASS_BYKEY(K,X)/
  72.     {/
  73.     RegisterClassHelper<X> __class_help(_T(K));/
  74.     }
  75. #define CLASSNAME(classname)/
  76.     #classname

假设在程序中有2个子类  Class1, Class2;

怎么去动态创建呢?

    //注册类

     REGISTER_CLASS(Class1);

     REGISTER_CLASS(Class2);

    //创建类

    RuntimeClassFactory::Create(CLASS_NAME(Class1));

    //删除类

    RuntimeClassFactory::Delete(CLASS_NAME(Class2));

 

    好的,基本技术介绍完毕了,接下来就是封装的TabManager,用来管理标签的切换

 

    

  1.     class TabManager
  2.     {
  3.     public:
  4.         struct tab_elem
  5.         {
  6.             tab_elem()
  7.             {
  8.                 dialog = 0;
  9.             }
  10.             CDialog * dialog;
  11.             UINT idd;
  12.             string classname;
  13.         };
  14.     public:
  15.         TabManager(CDHtmlUIDemoDlg* parent)
  16.         {
  17.             m_current = -1;
  18.             this->m_parent = parent;
  19.         }
  20.         ~TabManager()
  21.         {
  22.             hash_map<int,tab_elem>::iterator item;
  23.             for(item=this->m_list.begin();item!=this->m_list.end();)
  24.             {
  25.                 if(item->second.dialog)
  26.                 {
  27.                     item->second.dialog->DestroyWindow();
  28.                     RuntimeClassFactory::Delete(item->second.classname,item->second.dialog);
  29.                 }
  30.                 item = m_list.erase(item);
  31.             }
  32.         }
  33.     protected:
  34.         hash_map<int,tab_elem> m_list;
  35.         CDHtmlUIDemoDlg* m_parent;
  36.         int m_current;
  37.         void GetWorkRect(LPRECT rc)
  38.         {
  39.             long l,t,w,h;
  40.             this->m_parent->GetElem("tbOnline").mi_Elem->get_offsetLeft(&l);
  41.             this->m_parent->GetElem("tbOnline").mi_Elem->get_offsetTop(&t);
  42.             this->m_parent->GetElem("tbOnline").mi_Elem->get_offsetWidth(&w);
  43.             this->m_parent->GetElem("tbOnline").mi_Elem->get_offsetHeight(&h);
  44.             long l_s,t_s,w_s,h_s;
  45.             this->m_parent->GetElem("statusBar").mi_Elem->get_offsetLeft(&l_s);
  46.             this->m_parent->GetElem("statusBar").mi_Elem->get_offsetTop(&t_s);
  47.             this->m_parent->GetElem("statusBar").mi_Elem->get_offsetWidth(&w_s);
  48.             this->m_parent->GetElem("statusBar").mi_Elem->get_offsetHeight(&h_s);
  49.             CRect rcClient;
  50.             this->m_parent->GetClientRect(rcClient);
  51.             rc->left = l+2;
  52.             rc->top = t+h;
  53.             rc->right = l+w-2;
  54.             rc->bottom = t_s;
  55.         }
  56.         void CreatePlugin(tab_elem & elem)
  57.         {
  58.             if(elem.dialog)
  59.                 return;
  60.             elem.dialog = (CDialog*)RuntimeClassFactory::Create(elem.classname);
  61.             elem.dialog->Create(elem.idd,this->m_parent);
  62.             CRect rcWork;
  63.             GetWorkRect(rcWork);
  64.             elem.dialog->SetWindowPos(0,rcWork.left,rcWork.top,rcWork.Width(),rcWork.Height(),SWP_HIDEWINDOW);
  65.         }
  66.     public:
  67.         CDialog * GetCurrentTab()
  68.         {
  69.             if(this->m_current<0)
  70.                 return 0;
  71.             return this->m_list[this->m_current].dialog;
  72.         }
  73.         void Add(const char * title,const char * classname,int IDD,bool autoCreate)
  74.         {
  75.             tab_elem elem;
  76.             try
  77.             {
  78.                 elem.idd = IDD;
  79.                 elem.classname = classname;
  80.                 int index = m_parent->GetTabElemCount();
  81.                 m_parent->AddTabElem(title,title,index);
  82.                 if(autoCreate)
  83.                     CreatePlugin(elem);
  84.                 this->m_list[index] = elem;
  85.             }
  86.             catch(string e)
  87.             {
  88.                 TRACE("add plugin [%s] failed:%s/n",classname);
  89.             } 
  90.         }
  91.         void Switch(int index)
  92.         {
  93.             if(index==m_current)
  94.                 return;
  95.             hash_map<int,tab_elem>::iterator item;
  96.             for(item = this->m_list.begin();item!=this->m_list.end();item++)
  97.             {
  98.                 if(item->first!=index)
  99.                 {
  100.                     if(item->second.dialog)
  101.                     {
  102.                         item->second.dialog->ShowWindow(SW_HIDE);
  103.                     }
  104.                 }
  105.                 else
  106.                 {
  107.                     tab_elem & elem = item->second;
  108.                     CreatePlugin(elem);
  109.                     elem.dialog->ShowWindow(SW_SHOW);
  110.                 }
  111.             }
  112.             m_current = index;
  113.             this->m_parent->SwitchTabElem(m_current);
  114.         }
  115.         void resize()
  116.         {
  117.             CRect rcWork;
  118.             this->GetWorkRect(rcWork);
  119.             hash_map<int,tab_elem>::iterator item;
  120.             for(item = this->m_list.begin();item!=this->m_list.end();item++)
  121.             {
  122.                 tab_elem & elem = item->second;
  123.                 if(elem.dialog==0)
  124.                     continue;
  125.                 if(item->first==this->m_current)
  126.                     elem.dialog->SetWindowPos(0,rcWork.left,rcWork.top,rcWork.Width(),rcWork.Height(),SWP_SHOWWINDOW);
  127.                 else
  128.                     elem.dialog->SetWindowPos(0,rcWork.left,rcWork.top,rcWork.Width(),rcWork.Height(),SWP_HIDEWINDOW);
  129.             }
  130.         }
  131.     };

代码并不难懂,其中有一个函数GetWorkRect是用来得到工作区域的位置,这里采用了Html的Div定位技术

看看DhtmlUIDemo.html的框架可以发现

Body可视部分由3部分组成

Table( tbOnline)

Div(content)

Div(statusBar)

网页设计让tbOnline居上,statusBar居下,content的位置则在程序中经过计算得出

 

 

程序中TabManager的使用

 

假设CTab1,CTab2为创建的2个子Dialog

  1.  TabManager * m_tab;
  2.  REGISTER_CLASS(CTab1);
  3.  REGISTER_CLASS(CTab2);
  4.  m_tab = new TabManager(this);
  5.  m_tab->Add("标签1",CLASSNAME(CTab1),CTab1::IDD,true);
  6.  m_tab->Add("标签2",CLASSNAME(CTab2),CTab2::IDD,false);
  7.  m_tab->Switch(0);

用户点击了标签

  1. HRESULT CDHtmlUIDemoDlg::OnClickTab(IHTMLElement *phtmlElement)
  2. {
  3.     BSTR bstr;
  4.     phtmlElement->get_id(&bstr);
  5.     string sid = (LPCTSTR)CString(bstr);
  6.     int pos = sid.find('_');
  7.     int id = atoi(sid.substr(pos+1,sid.length()-pos-1).c_str());
  8.     this->m_tab->Switch(id);
  9.     return S_OK;
  10. }

 

 

 

总体介绍完了,Demo下载 http://download.csdn.net/source/598661 ,唉,CSDN改版怎么默认下载设置需要1个资源分了,改都改不了

Demo工程是VS2008下的,但是由于demo比较简单,其他都是外部资源文件,如果是VS2003环境,移植也很简单。

 

本文旨在帮助那些深受自绘控件烦恼的程序员找一个新的捷径,建议读者下载demo以后不要使用“拿来主义”,直接拿到自己的工程里面用,建议从第一篇文章开始,Step by Step去领悟Dhtml UI设计的思路。

祝大家早日打通这个“任督二脉”。

当然DHtml VC7开始就有了,不是什么新鲜玩意,所以没学过的也别觉得困难,其实也很简单,就是操作HTML元素,UI技巧基本都移植到CSS里面去。

 

文章只是介绍了Toolbar,TabCtrl的模拟情况,关于ListCtrl等等,作者并没有继续模拟,现在比较成熟的JS+CSS实现DataGrid,ProgressCtrl都有,大家可以去网上找一找,我这里推荐一个国外优秀的开源JS库http://extjs.com/,基本WindowUI都实现了,非常完美,缺点就是这个库主要是针对网站Ajax的,但是只要哪位仁兄有这兴趣,可以考虑把它移植到客户端上做界面。

 

datagrid:

 

 

 

你可能感兴趣的:(object,datagrid,delete,Class,dialog,div)