所见即所得的控件

来源:互联网 发布:sql服务器名称填什么 编辑:程序博客网 时间:2024/04/29 11:36

这些天做UI控件,碰到个棘手的问题: 怎么样让用户看到实际效果了再应用设置.说详细点就是一个UI控件如何让用户能够对他所做的设置有个直观的认识,并根据效果判断是否应用设置.

想想要解决这样一个问题,无非就是弄个窗口出来,里面能够根据用户的设置绘制我的控件,用户觉得配置满意了,Apply一下,实际操作的控件也就跟着变样了.要实现的东西就多了, 首先得让控件能够将他的样式(这里都是属性)导出,然后由Demo控件中导入, 接着你得实现一个对话框, 并用他做为Demo控件的容器,在打开属性页的时候把他show出来.

对话框很好做,在Resource Editor中随便加一个框框,然后把编译好的你自己的控件拖上去.接着从CAxDialogImpl<T>派生一个对话框类,加入一个CAxWindow类对象,运行时将其与对话框上的控件绑定,重载CAxDialogImpl类的OnInitDialog. 通过一个静态的指针来在各个属性页中维护同一个窗口.

CMyCtlPrevDlg * CMyCtlPrevDlg ::CreateUniqueInstance(
                                    HWND hParent /*= NULL*/,
                                    LPSIZEL pszExt /*= NULL*/ )
{
   if ( NULL == m_pUniqueInstance )
   {
      SIZEL szTemp = {0};
      if ( pszExt != NULL )
      {
         szTemp = *pszExt;
      }
      m_pUniqueInstance = new CMyCtlPrevDlg ( hParent, szTemp );
      if ( NULL == m_pUniqueInstance )
      {
         ATLTRACE( "Out of memory! ===CMyCtlPrevDlg ::CreateUInst===/n" );
         return NULL;
      }

      HWND hWndThis = m_pUniqueInstance ->Create( hParent );
      if ( NULL == hWndThis )
      {
         ATLTRACE( "Failed to create! ===CMyCtlPrevDlg ::CreateUInst===/n" );
         m_pUniqueInstance ->Release();
         return NULL;
      }
   }
  
   m_pUniqueInstance ->AddRef();
   return m_pUniqueInstance ;
}

LRESULT CAdvCtlPrevDlg::OnInitDialog(
   UINT uMsg, 
   WPARAM wParam,
   LPARAM lParam,
   BOOL& bHandled )
{
   if ( m_hWnd == NULL )
   {
      return E_UNEXPECTED;
   }

   // Get interface
   m_axMyCtrl.Attach( GetDlgItem( IDC_PREDLG_MYCTRL ) );
   m_axMyCtrl.QueryControl( IID_IMyControl, (void**)&m_spMyCtrl );
   if ( m_spMyCtrl == NULL )
   {
      return E_UNEXPECTED;
   }

   // Restore the container's size
   if ( m_hParent != NULL )
   {
      ::GetWindowRect(
         m_hParent,
         &m_rcPtWnd );

      ::GetClientRect(
         m_hParent,
         &m_rcPtClt );
   }
   // Init the size
   UpdateExtent( m_szExtent );

   bHandled = TRUE;
   return 0L;
}

这样做出来的Preview Window将整合到属性页中, 当然也可以把他做出来,不过这么做可得小心,后面会提到.

再回到控件的属性页中

LRESULT CMyControlPP::OnInitDialog(
                         UINT uMsg,
                         WPARAM wParam,
                         LPARAM lParam,
                         BOOL& bHandled )
{

   .......

   // Initialize preview window
   CComQIPtr<IMyControl, &IID_IMyControl> pCtrl = m_ppUnk[ 0 ];
   ATLASSERT( pCtrl != NULL );
  
   // Init
   CComQIPtr<IOleObject, &IID_IOleObject> pOleObj = pCtrl ;
   SIZEL szPos = {0};
   if ( pOleObj != NULL )
   {
      pOleObj->GetExtent( DVASPECT_CONTENT, &szPos );
   }
  
   // Create
   m_pwndPreview = CAdvCtlPrevDlg::CreateUnitInstance( m_hWnd, &szPos );
   if ( m_pwndPreview != NULL )
   {
      // Get the demo control
      m_pwndPreview->GetTheControl(
         IID_IMyControl,
         (void**)&m_pDemoCtrl );
     
      // Import style
      CComQIPtr<IUnknown, &IID_IUnknown> pUnk = pCtrl ;
      m_pwndPreview->ImportStyleIn( pUnk );
     
      // Show the Preview Window
      m_pwndPreview->ShowWindow( SW_SHOW );
   }

   .......

}

之后修改所有属性页中的代码,让所有的操作直接应用到m_pDemoCtrl上,当用户点击Apply时,从m_pDemoCtrl上导出样式,再一个个Objects轮流导入就ok了

这无形中还解决了另一个问题,反正这是我头一遭碰到,那就是:

前提:你的控件在操作过程中需要动态生成或移除"子"对象,就好比踢场足球你可能要换人,也可能有人被罚出场

如果是这样,当客户程序中存在多个同类的控件,且其内部的成员组成不一样(曼联上了11个人,阿森纳只上了7个),当用户将他们都框起来,然后打开属性页,一旦访问到有歧义的对象时(第8个队员),异常就蹦出来了,然后客户程序就崩溃了.

通过上面说的方法能够避免这种异常,并且允许用户对多个同类的控件做统一配置.(只闻上帝说道:"费厄泼赖", 曼联和阿森纳霎时间只留下两个守门员大眼瞪小眼了)

 

前面提到如果要把Preview Window做成浮动的必须小心,原因就是:

杀千刀的美国国家仪器(这里就不提英文名了,免得被找到)把这项技术注册为专利了......

 

原创粉丝点击