社区应用 最新帖子 精华区 社区服务 会员列表 统计排行 银行

  • 61935阅读
  • 138回复

MFC技术资料大全与汇总

级别: 管理员
发帖
8532
金币
2762
威望
3231
贡献值
0
元宝
0

其他同类贴子:

VC,MFC开发技巧收集 http://www.usidcbbs.com/read.php?tid=1566

Windows平台VC(MFC)多线程编程 http://www.usidcbbs.com/read.php?tid=3367



树形控件可以用于树形的结构,其中有一个根接点(Root)然后下面有许多子结点,而每个子结点上有允许有一个或多个或没有子结点。MFC中使用CTreeCtrl类来封装树形控件的各种操作。通过调用
BOOL Create( DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID );创建一个窗口,dwStyle中可以使用以下一些树形控件的专用风格:

TVS_HASLINES 在父/子结点之间绘制连线
TVS_LINESATROOT 在根/子结点之间绘制连线
TVS_HASBUTTONS 在每一个结点前添加一个按钮,用于表示当前结点是否已被展开
TVS_EDITLABELS 结点的显示字符可以被编辑
TVS_SHOWSELALWAYS 在失去焦点时也显示当前选中的结点
TVS_DISABLEDRAGDROP 不允许Drag/Drop
TVS_NOTOOLTIPS 不使用ToolTip显示结点的显示字符
在树形控件中每一个结点都有一个句柄(HTREEITEM),同时添加结点时必须提供的参数是该结点的父结点句柄,(其中根Root结点只有一个,既不可以添加也不可以删除)利用
HTREEITEM InsertItem( LPCTSTR lpszItem, HTREEITEM hParent = TVI_ROOT, HTREEITEM hInsertAfter = TVI_LAST );可以添加一个结点,pszItem为显示的字符,hParent代表父结点的句柄,当前添加的结点会排在hInsertAfter表示的结点的后面,返回值为当前创建的结点的句柄。

下面的代码会建立一个如下形式的树形结构:
+--- Parent1
     +--- Child1_1
     +--- Child1_2
     +--- Child1_3
+--- Parent2
+--- Parent3


HTREEITEM hItem,hSubItem;
hItem = m_tree.InsertItem("Parent1",TVI_ROOT);在根结点上添加Parent1
hSubItem = m_tree.InsertItem("Child1_1",hItem);//在Parent1上添加一个子结点
hSubItem = m_tree.InsertItem("Child1_2",hItem,hSubItem);//在Parent1上添加一个子结点,排在Child1_1后面
hSubItem = m_tree.InsertItem("Child1_3",hItem,hSubItem);

hItem = m_tree.InsertItem("Parent2",TVI_ROOT,hItem);   
hItem = m_tree.InsertItem("Parent3",TVI_ROOT,hItem);   

如果你希望在每个结点前添加一个小图标,就必需先调用CImageList* SetImageList( CImageList * pImageList, int nImageListType );
CImageList指向一个CImageList对象,如果这个值为空,则CTreeCtrl中的Image将被移除。
nImageListType有两种,TVSIL_NORMAL-包含选择和被选择两个状态的Image,TVSIL_STATE-用户定义状态的Image。
在调用完成后控件中使用图片以设置的ImageList中图片为准。然后调用
HTREEITEM InsertItem( LPCTSTR lpszItem, int nImage, int nSelectedImage, HTREEITEM hParent = TVI_ROOT, HTREEITEM hInsertAfter = TVI_LAST);添加结点,
nImage为结点没被选中时所使用图片序号,nSelectedImage为结点被选中时所使用图片序号。
下面的代码演示了ImageList的设置。

m_list.Create(IDB_TREE,16,4,RGB(0,0,0));
m_tree.SetImageList(&m_list,TVSIL_NORMAL);
m_tree.InsertItem("Parent1",0,1);//添加,选中时显示图标1,未选中时显示图标0


Example2:

CImageList imglist;
CBitmap     bitmap;
imglist.Create(16, 16, ILC_MASK, 1, 1);
bitmap.LoadBitmap( IDB_COMPUTER );
imglist.Add(&bitmap, (COLORREF)0xFFFFFF);
bitmap.DeleteObject();
treectrl.SetImageList(&m_imgList, TVSIL_NORMAL);


此外CTreeCtrl还提供了一些函数用于得到/修改控件的状态。
HTREEITEM GetSelectedItem( );将返回当前选中的结点的句柄。BOOL SelectItem( HTREEITEM hItem );将选中指明结点。
BOOL GetItemImage( HTREEITEM hItem, int& nImage, int& nSelectedImage ) / BOOL SetItemImage( HTREEITEM hItem, int nImage, int nSelectedImage )用于得到/修改某结点所使用图标索引。
CString GetItemText( HTREEITEM hItem ) /BOOL SetItemText( HTREEITEM hItem, LPCTSTR lpszItem );用于得到/修改某一结点的显示字符。
BOOL DeleteItem( HTREEITEM hItem );用于删除某一结点,BOOL DeleteAllItems( );将删除所有结点。

此外如果想遍历树可以使用下面的函数:
HTREEITEM GetRootItem( );得到根结点。
HTREEITEM GetChildItem( HTREEITEM hItem );得到子结点。
HTREEITEM GetPrevSiblingItem/GetNextSiblingItem( HTREEITEM hItem );得到指明结点的上/下一个兄弟结点。
HTREEITEM GetParentItem( HTREEITEM hItem );得到父结点。

树形控件的消息映射使用ON_NOTIFY宏,形式如同:ON_NOTIFY( wNotifyCode, id, memberFxn ),wNotifyCode为通知代码,id为产生该消息的窗口ID,memberFxn为处理函数,函数的原型如同void OnXXXTree(NMHDR* pNMHDR, LRESULT* pResult),其中pNMHDR为一数据结构,在具体使用时需要转换成其他类型的结构。对于树形控件可能取值和对应的数据结构为:

TVN_SELCHANGED 在所选中的结点发生改变后发送,所用结构:NMTREEVIEW
TVN_ITEMEXPANDED 在某结点被展开后发送,所用结构:NMTREEVIEW
TVN_BEGINLABELEDIT 在开始编辑结点字符时发送,所用结构:NMTVDISPINFO
TVN_ENDLABELEDIT 在结束编辑结点字符时发送,所用结构:NMTVDISPINFO
TVN_GETDISPINFO 在需要得到某结点信息时发送,(如得到结点的显示字符)所用结构:NMTVDISPINFO
关于ON_NOTIFY有很多内容,将在以后的内容中进行详细讲解。

关于动态提供结点所显示的字符:首先你在添加结点时需要指明lpszItem参数为:LPSTR_TEXTCALLBACK。在控件显示该结点时会通过发送TVN_GETDISPINFO来取得所需要的字符,在处理该消息时先将参数pNMHDR转换为LPNMTVDISPINFO,然后填充其中item.pszText。但是我们通过什么来知道该结点所对应的信息呢,我的做法是在添加结点后设置其lParam参数,然后在提供信息时利用该参数来查找所对应的信息。下面的代码说明了这种方法:

char szOut[8][3]={"No.1","No.2","No.3"};

//添加结点
HTREEITEM hItem = m_tree.InsertItem(LPSTR_TEXTCALLBACK,...)
m_tree.SetItemData(hItem, 0 );
hItem = m_tree.InsertItem(LPSTR_TEXTCALLBACK,...)
m_tree.SetItemData(hItem, 1 );
//处理消息
void CParentWnd::OnGetDispInfoTree(NMHDR* pNMHDR, LRESULT* pResult)
{
TV_DISPINFO* pTVDI = (TV_DISPINFO*)pNMHDR;
pTVDI->item.pszText=szOut[pTVDI->item.lParam];//通过lParam得到需要显示的字符在数组中的位置
*pResult = 0;
}


关于编辑结点的显示字符:首先需要设置树形控件的TVS_EDITLABELS风格,在开始编辑时该控件将会发送TVN_BEGINLABELEDIT,你可以通过在处理函数中返回TRUE来取消接下来的编辑,在编辑完成后会发送TVN_ENDLABELEDIT,在处理该消息时需要将参数pNMHDR转换为LPNMTVDISPINFO,然后通过其中的item.pszText得到编辑后的字符,并重置显示字符。如果编辑在中途中取消该变量为NULL。下面的代码说明如何处理这些消息:

//处理消息 TVN_BEGINLABELEDIT
void CParentWnd::OnBeginEditTree(NMHDR* pNMHDR, LRESULT* pResult)
{
TV_DISPINFO* pTVDI = (TV_DISPINFO*)pNMHDR;
if(pTVDI->item.lParam==0);//判断是否取消该操作
   *pResult = 1;
else
   *pResult = 0;
}
//处理消息 TVN_BEGINLABELEDIT
void CParentWnd::OnBeginEditTree(NMHDR* pNMHDR, LRESULT* pResult)
{
TV_DISPINFO* pTVDI = (TV_DISPINFO*)pNMHDR;
if(pTVDI->item.pszText==NULL);//判断是否已经取消取消编辑
   m_tree.SetItemText(pTVDI->item.hItem,pTVDI->pszText);//重置显示字符
*pResult = 0;
}

上面讲述的方法所进行的消息映射必须在父窗口中进行(同样WM_NOTIFY的所有消息都需要在父窗口中处理)。

文章转自:http://blog.csdn.net/dongle2001/articles/429704.aspx


1.树视图风格:
TVS_HASBUTTONS;    //在父项旁边显示(+)和(-)
TVS_HASLINES;     //使用线条显示各项之间的层次
TVS_LINESATROOT;//使用线条链接树视图控件根部各项

2.处理单击事件:TVN_SELCHANGED
void CTreeCtrlDlg::OnTvnSelchangedTree1(NMHDR *pNMHDR, LRESULT *pResult)
{
      LPNMTREEVIEW pNMTreeView = reinterpret_cast(pNMHDR);
      // TODO: 在此添加控件通知处理程序代码
      HTREEITEM ht=m_treeCtrl.GetSelectedItem();
      CString strSelect=m_treeCtrl.GetItemText(ht);
      m_strTreeVal=strSelect;
      UpdateData(FALSE);
      *pResult = 0;
}

3.同时让自己派生的CMyTreeCtrl类和对话框处理TVN_SELCHANGED消息:
只须在CMyTreeCtrl中处理以下消息,并返回FALSE就OK了ON_NOTIFY_REFLECT_EX(TVN_SELCHANGED, OnTvnSelchanged) OnTvnSelchanged的签名如下
BOOL CMyTreeCtrl::OnTvnSelchanged(NMHDR *pNMHDR,LRESULT *pResult)

4.编辑标签:要允许编辑树视图控件中的文本,可以设置以下三个步骤
      (1).设置树视图的TVS_EDITLABELS风格
TVS_EDITLABE可以通过资源编辑框内部修改(Edit labels),也可以用代码的方式修改,如下
long lStyle=::GetWindowLong(m_treeCtrl.GetSafeHwnd(),GWL_STYLE);
      lStyle|=TVS_EDITLABELS;
      ::SetWindowLong(m_treeCtrl.GetSafeHwnd(),GWL_STYLE,lStyle)
      (2).处理TVN_BEGINLABELEDIT通知消息
       //设置相关限制,如限制编辑框字符串长度
      CEdit*pEdit=GetEditControl();     //获取当前选中结点编辑框
      ASSERT(pEdit);
      if (pEdit)
      {
          pEdit->LimitText(15);//设置编辑框文本长度为15个字符串
          *pResult = 0;      
      }
      (3).处理TVN_ENDLABLEEDIT通知消息
      HTREEITEM hParent=GetParentItem(pTVDispInfo->item.hItem); //获取父结点 
      HTREEITEM hChild=GetChildItem(hParent?hParent:TVI_ROOT); //获取第一个根结点
      hChild=GetNextSiblingItem(hChild);            //获取下一个兄弟节点
      if (pTVDispInfo->item.hItem!=hChild)          //判断是否与当前节点相同
      pTVDispInfo->item.pszText                     //获取当前节点的字符串
      CString strText=GetItemText(hChild);          //获取节点的字符串

5.让树视图处理Esc和Enter键
重载PreTranslateMessage函数
BOOL bHandleMsg=FALSE;
      switch(pMsg->message) {
      case VK_ESCAPE:
      case VK_RETURN:
          if (::GetKeyState(VK_CONTROL)&0x8000)
          {
              break;
          }
          if (GetEditControl())
          {
              ::TranslateMessage(pMsg);
              ::DispatchMessage(pMsg);
              bHandleMsg=TRUE;
          }
          break;
      }

4.实现上下文菜单
在WM_RBUTTONDOWN消息处理函数上实现上下文菜单

5.展开和收起树视图结点:
HTREEITEM hItem=GetRootItem();                //获取根结点,可能会有多个根结点
ItemHasChildren(hParent)                      //判断结点是否有孩子结点
hItem=GetChildItem(hParent);                  //获取第一个子结点
hItem=GetNextSiblingItem(hItem));             //获取下一个兄弟结点结点
Expand(hItem,bExpand?TVE_EXPAND:TVE_COLLAPSE);//展开/叠起结点
Select(hItem,TVGN_FIRSTVISIBLE);                  //设置选中结点
CString str=GetItemText(hChild);              //获取结点字符串信息
HTREEITEM hCurrSel = GetSelectedItem();       //获取当前选中结点
SelectItem(hNewSel);
HTREEITEM hNewSel = HitTest(pt, &nFlags);         //判断坐标是否在当前结点范围内
HTREEITEM hItem=InsertItem(dlg.m_strItemText,hItemParent);    //插入结点


#pragma once
//定义文件MyTreeCtrl.h
// CMyTreeCtrl
class CMyTreeCtrl : public CTreeCtrl
{
      DECLARE_DYNAMIC(CMyTreeCtrl)
public:
      CMyTreeCtrl();
      virtual ~CMyTreeCtrl();
protected:
      DECLARE_MESSAGE_MAP()
      void ExpandBranch(HTREEITEM hItem,BOOL bExpand =TRUE);
public:
      void ExpandAllBranches(BOOL bExpand =TRUE);
      BOOL DoesItemExist(HTREEITEM hItemParent, CString const& strItem);
      afx_msg void OnRButtonDown(UINT nFlags, CPoint point);
      afx_msg void OnAddItem();
      virtual BOOL PreTranslateMessage(MSG* pMsg);
      afx_msg void OnTvnBeginlabeledit(NMHDR *pNMHDR, LRESULT *pResult);
      afx_msg void OnTvnEndlabeledit(NMHDR *pNMHDR, LRESULT *pResult);
   
      afx_msg BOOL OnTvnSelchanged(NMHDR *pNMHDR, LRESULT *pResult);
};
// MyTreeCtrl.cpp : 实现文件
#include "stdafx.h"
#include "TreeCtrl.h"
#include "MyTreeCtrl.h"
#include ".mytreectrl.h"
#include "AddItemDlg.h"
// CMyTreeCtrl
IMPLEMENT_DYNAMIC(CMyTreeCtrl, CTreeCtrl)
CMyTreeCtrl::CMyTreeCtrl()
{
}
CMyTreeCtrl::~CMyTreeCtrl()
{
}
BEGIN_MESSAGE_MAP(CMyTreeCtrl, CTreeCtrl)
      ON_WM_RBUTTONDOWN()
      ON_COMMAND(IDR_ADD_ITEM, OnAddItem)
      ON_NOTIFY_REFLECT(TVN_ENDLABELEDIT, OnTvnEndlabeledit)
      ON_NOTIFY_REFLECT(TVN_BEGINLABELEDIT, OnTvnBeginlabeledit)
      ON_NOTIFY_REFLECT_EX(TVN_SELCHANGED, OnTvnSelchanged)
END_MESSAGE_MAP()

// CMyTreeCtrl 消息处理程序
void CMyTreeCtrl::ExpandBranch(HTREEITEM hItem,BOOL bExpand )
//展开
{
      if (ItemHasChildren(hItem))    //判断是否有孩子结点
      {
          Expand(hItem,bExpand?TVE_EXPAND:TVE_COLLAPSE);
          //展开/叠起结点
          hItem=GetChildItem(hItem);//获取第一个子结点
          do{
              ExpandBranch(hItem);
          } while(hItem=GetNextSiblingItem(hItem));//获取兄弟结点
      }
}
void CMyTreeCtrl::ExpandAllBranches(BOOL bExpand )
{
      HTREEITEM hItem=GetRootItem();//获取根结点,可能会有多个根结点
      do{
          ExpandBranch(hItem,bExpand);
      } while(hItem=GetNextSiblingItem(hItem));
      Select(hItem,TVGN_FIRSTVISIBLE);//设置选中结点
}
BOOL CMyTreeCtrl::DoesItemExist(HTREEITEM hItemParent,
                                  CString const& strItem)
{
      BOOL bDoesItemExist=FALSE;
      ASSERT(strItem.GetLength());
      HTREEITEM hChild=GetChildItem(hItemParent?hItemParent:TVI_ROOT);
      while (NULL!=hChild&&!bDoesItemExist)
      {
          CString str=GetItemText(hChild);//获取结点字符串信息
          if (0==str.CompareNoCase(strItem))
          {
              bDoesItemExist=TRUE;
          }
          else
          {
              hChild=GetNextSiblingItem(hChild);
          }
      }
      return bDoesItemExist;
}
void CMyTreeCtrl::OnRButtonDown(UINT nFlags, CPoint point)
{
      // TODO: 在此添加消息处理程序代码和/或调用默认值   
      // set focus to the tree control
      SetFocus();

      // map the point that is passed to the
      // function from client coordinates
      // to screen coordinates
      ClientToScreen(&point);
      // Get the currently selected item
      HTREEITEM hCurrSel = GetSelectedItem();//获取当前选中结点
      // Figure out which item was right clicked
      CPoint pt(0, 0);
      ::GetCursorPos(&pt);
      ScreenToClient (&pt);
      HTREEITEM hNewSel = HitTest(pt, &nFlags);
      // Set the selection to the item that the
      // mouse was over when the user right
      // clicked
      if (NULL == hNewSel)
      {
          SelectItem(NULL);
      }
      else if (hCurrSel != hNewSel)
      {
          SelectItem(hNewSel);
          SetFocus();
      }

      // Load the context menu
      CMenu Menu;
      if (Menu.LoadMenu(IDM_CONTEXT_MENU))
      {
          CMenu* pSubMenu = Menu.GetSubMenu(0);
          if (pSubMenu!=NULL)
          {
              // Display the context menu
              pSubMenu->TrackPopupMenu(
                  TPM_LEFTALIGN | TPM_RIGHTBUTTON,
                  point.x, point.y, this);
          }
      }  
}
void CMyTreeCtrl::OnAddItem()
//添加上下文菜单
{
      // TODO: 在此添加命令处理程序代码
      HTREEITEM hItemParent=GetSelectedItem();
      //获取当前选中结点
      CAddItemDlg dlg;
      if (dlg.DoModal()==IDOK)
      {
          if (!DoesItemExist(hItemParent,dlg.m_strItemText))
          {
              HTREEITEM hItem=InsertItem(dlg.m_strItemText,hItemParent);
              //插入结点
              SelectItem(hItem);
          }
          else
          {
              AfxMessageBox("已存在相同结点");
          }
      }
  
}
BOOL CMyTreeCtrl::PreTranslateMessage(MSG* pMsg)
{
      // TODO: 在此添加专用代码和/或调用基类
      BOOL bHandledMsg = FALSE;

      switch (pMsg->message)
      {
          case WM_KEYDOWN:
          {
              switch (pMsg->wParam)
              {
              case VK_ESCAPE:
              case VK_RETURN:   
                  if (::GetKeyState(VK_CONTROL) & 0x8000)
                  {
                      break;
                  }
                  if (GetEditControl())
                  {
                      ::TranslateMessage(pMsg);
                      ::DispatchMessage(pMsg);
                      bHandledMsg = TRUE;
                  }
                  break;
              default: break;
              } // switch (pMsg->wParam)
          } // WM_KEYDOWN
          break;
      default: break;
      } // switch (pMsg->message)                  
      // continue normal translation and dispatching             
      return (bHandledMsg ?TRUE : CTreeCtrl::PreTranslateMessage(pMsg));

}
void CMyTreeCtrl::OnTvnBeginlabeledit(NMHDR *pNMHDR, LRESULT *pResult)
{
      LPNMTVDISPINFO pTVDispInfo = reinterpret_cast(pNMHDR);
      // TODO: 在此添加控件通知处理程序代码
      *pResult=1;
      CEdit*pEdit=GetEditControl();
      ASSERT(pEdit);
      if (pEdit)
      {
          pEdit->LimitText(15);
          *pResult=0;
      }  
}
void CMyTreeCtrl::OnTvnEndlabeledit(NMHDR *pNMHDR, LRESULT *pResult)
{
      LPNMTVDISPINFO pTVDispInfo = reinterpret_cast(pNMHDR);
      // TODO: 在此添加控件通知处理程序代码

      BOOL bValidItem=FALSE;
      CString strItem=pTVDispInfo->item.pszText;
      if (0      {
          HTREEITEM hParent=GetParentItem(pTVDispInfo->item.hItem);
          bValidItem=!DoesItemExist(hParent,strItem);    
      }
      *pResult = bValidItem;
}
BOOL CMyTreeCtrl::OnTvnSelchanged(NMHDR *pNMHDR, LRESULT *pResult)
{
      LPNMTREEVIEW pNMTreeView = reinterpret_cast(pNMHDR);
      // TODO: 在此添加控件通知处理程序代码
      TRACE(GetItemText(pNMTreeView->itemNew.hItem));
      TRACE(" ");
      *pResult = 0;
      return FALSE;         //返回FALSE可以让父窗口进行进一步的处理
}

关键词: MFC C++ 编程
QQ: 378890364 微信:wwtree(省短信费) 紧急事宜发短信到0061432027638  本站微博:http://t.qq.com/wwtree QQ群:122538123
级别: 管理员
发帖
8532
金币
2762
威望
3231
贡献值
0
元宝
0
只看该作者 沙发  发表于: 2010-07-18
CTreeCtrl例子

CTreeCtrl m_TripleTree;
CImageList m_imgList;
CImageList m_imgState; 
/*
CImageList ImgTree;
 ImgTree.Create (16,16,ILC_COLOR|ILC_MASK,3,3);//创建图标列表  图标为单个图标
 ImgTree.Add (AfxGetApp()->LoadIcon (IDI_CK));
 ImgTree.Add (AfxGetApp()->LoadIcon (IDI_BASE_INFO));
 ImgTree.Add (AfxGetApp()->LoadIcon (IDI_INPUT));
*/
 m_imgList.Create(IDB_BITMAP_LIST,16, 1, RGB(255,255,255));//创建图标列表 为连续的16*16图标
 //m_imgState.Create(IDB_BITMAP_STATE,16, 1, RGB(255,255,255));//创建状态图标
 m_TripleTree.SetImageList(&m_imgList,TVSIL_NORMAL);//选择与非选择
 //m_TripleTree.SetImageList(&m_imgState,TVSIL_STATE);//用户定义
HTREEITEM hItem1A=m_TripleTree.InsertItem(_T("根部"),0,0);
      
 HTREEITEM hItem2_1B=m_TripleTree.InsertItem(_T("根上一"),1,2,hItem1A,TVI_LAST);
 m_TripleTree.InsertItem(_T("根上二"1,2,hItem2_1B); 
 m_TripleTree.InsertItem(_T("根上二"),1,2,hItem2_1B); 

 HTREEITEM hItem2_2B=m_TripleTree.InsertItem(_T("根上一"),1,2,hItem1A,TVI_LAST);
 m_TripleTree.InsertItem(_T("根上一"),1,2,hItem2_2B); 
 m_TripleTree.InsertItem(_T("根上一"),1,2,hItem2_2B); 

  m_TripleTree.InsertItem(_T("根上一"),1,2,hItem1A,TVI_LAST);
 m_TripleTree.InsertItem(_T("根上一"),1,2,hItem1A,TVI_LAST);
    
    //设置显示风格
 m_TripleTree.SetBkColor (RGB(220,200,220));//背景颜色
 m_TripleTree.Expand(hItem1A,TVE_EXPAND);//根部展开
 DWORD dwStyle=GetWindowLong(m_TripleTree.m_hWnd ,GWL_STYLE);//获得树的信息
 dwStyle|=TVS_HASBUTTONS|TVS_HASLINES|TVS_LINESATROOT;//设置风格
 ::SetWindowLong (m_TripleTree.m_hWnd ,GWL_STYLE,dwStyle);
QQ: 378890364 微信:wwtree(省短信费) 紧急事宜发短信到0061432027638  本站微博:http://t.qq.com/wwtree QQ群:122538123
级别: 管理员
发帖
8532
金币
2762
威望
3231
贡献值
0
元宝
0
只看该作者 板凳  发表于: 2010-08-16
VC中inc、lib、dll路径设置问题

当一个工程很大的时候,恰当的路径设置可以将不同模块的动态链接库和工程很好的组织起来。

在VC中的设置如下:
include : project->setting->C/C++->Preprocessor: Additional include directories: ../../inc
dll : project->setting->Link->General: Output file name: ../../bin/moduld
.dll
pdb : project->setting->Link->Customize: Program database name: ../../bin/moduld.pdb
lib : 应该是在project->setting->Link->OutPut下,但是没有找到,所以更改Project Option:/implib:"../../lib/moduld.lib"

一般来讲,把所有的动态链接库的头文件放在inc目录下,而cpp文件放在src目录下,生成文件和pdb文件(方便进入动态链接库调试)放在bin目录下。这样可以满足不同项目调用同一个动态链接库,而且实时更新动态链接库。
ps:
definition of dllimport static data member not allowed
有可能是重复包含了文件,定义了和链接库中已经定义的同名变量

QQ: 378890364 微信:wwtree(省短信费) 紧急事宜发短信到0061432027638  本站微博:http://t.qq.com/wwtree QQ群:122538123
级别: 管理员
发帖
8532
金币
2762
威望
3231
贡献值
0
元宝
0
只看该作者 地板  发表于: 2010-08-16
CTabCtrl控件,选项卡控件用法
方案一


在对话框上放置一个Tab Control的控件,再在对话框上放置所需的控件(本例放置了2个按钮,试图在每个标签中显示一个)。然后利用Class Wizard来为Tab Control控件创建一个控件变量,该变量是CTabCtrl类的,再为其他控件也创建相应的控件类。 在主对话框的初始函数中CProperty1Dlg::OnInitDialog()加入如下代码:


//本例插入两个标签,实际运用中可通过循环插入所需个数的标签,运行后默认第一个标签被选中


m_tab.InsertItem( 0, _T("Tab1") );


m_tab.InsertItem( 1, _T("Tab2") );


//将不是第一个标签的控件隐藏掉,只留下你要的控件


m_button2.ShowWindow( SW_HIDE );


再利用ClassWizard处理Tab Control的 TCN_SELCHANGE 的消息。在消息处理函数中,利用CWnd::ShowWindow来使相应的控件显示和隐藏。


void CProperty1Dlg::OnSelchangeTab1(NMHDR* pNMHDR, LRESULT* pResult)


{


//GetCurSel返回当前被选中的标签的索引号(以0为基础算起)


int sel = m_tab.GetCurSel();


switch(sel)


{


case 0:


m_button1.ShowWindow( SW_SHOW );


m_button2.ShowWindow( SW_HIDE );


break;


case 1:


m_button2.ShowWindow( SW_SHOW );


m_button1.ShowWindow( SW_HIDE );


break;


}


*pResult = 0;


}





方案二


本这个方案中,我将使用MFC中现成的CPropertySheet和CPropertyPage类来完成将控件分散到各个对话框类中。


首先加入两个(或数个)对话框资源。修改各对话框资源的属性,将对话框的Caption属性改为你要在标签上所显示的文字。将对话框的Style属性改为:Child, Border属性改为:Thin, 只选中Title Bar复选框,去掉其他复选框。然后你可以在这些对话框中加入要分开显示的各个控件。


为上述对话框资源分别制作一个对话框类,该对话框类是从CPropertyPage继承。这样一来各子对话框类就好了,主对话框类可以直接使用CPropertySheet类。使用如下代码即可:


CPropertySheet sheet("属性页对话框");


CPage1 page1;


CPage2 page2;


//加入子对话框作为一个属性页


sheet.AddPage(&page1);


sheet.AddPage(&page2);


//产生一个模态对话框,也可以使用Create方法来产生一个非模态对话框(具体参见MSDN)


sheet.DoModal();


如何在主对话框中放置其他控件呢?如果直接使用CPropertySheet的话,是不可以的,但是别忘了我们可以从CPropertySheet类继承自己的类啊!


方案三


首先还是要创建那些要在属性页中的显示的子对话框类,创建步骤和方案二一样,都是从CPropertyPage继承。


这次我们将从CPropertySheet类继承自己的类(假设类名为CMySheet)。我们要在这里放上一个button控件。那么现在先在CMySheet中加入一个CButton类的成员变量m_button。


在CMySheet类中的OnInitDialog()函数里,这样写:


BOOL bResult = CPropertySheet::OnInitDialog();


//取得属性页的大小


CRect rectWnd;


GetWindowRect(rectWnd);


//调整对话框的宽度


SetWindowPos(NULL, 0, 0,rectWnd.Width() + 100,rectWnd.Height(),SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE);


CRect rectButton(rectWnd.Width() + 25, 25,rectWnd.Width()+75, 75);


//用程序创建一个按钮


m_button.Create("Button", BS_PUSHBUTTON, CRect(rectWnd.Width(), 25,rectWnd.Width()+75, 50) , this, 1);


//显示这个按钮


m_button.ShowWindow( SW_SHOW );


CenterWindow();


return bResult;


 使用方案三虽然能在主对话框中加入控件,但是也比较麻烦,首先所加的控件只能在属性页的右边或下边。并且用程序来产生控件比较烦琐,位置与大小不易控制。那么还有其他方法,既能在对话框中加入属性页,又能在主对话框随意添加控件?


方案四


不从CPropertySheet继承自己的类,还是直接使用它。各属性页的子对话框类还是需要的,创建方法和上述两个方案相同。


首先我们新建一个基于对话框的工程。在编辑已有的一个主对话框中可以自由加一些所需的控件,但是得留出一定的空间用于放置属性页。


在主对话框类里加入一个CPropertySheet类的一个成员变量(m_sheet)代表整个属性页。再加入一些各子对话框类的实例作为成员变量(m_page1、m_page2……)。


在主对话框类的OnInitDialog()函数中加入:


//加入标签,标签名由各个子对话框的标题栏决定


m_sheet.AddPage(&m_page1);


m_sheet.AddPage(&m_page2);


//用Create来创建一个属性页


m_sheet.Create(this, WS_CHILD | WS_VISIBLE, WS_EX_CONTROLPARENT);


RECT rect;


m_sheet.GetWindowRect(&rect);


int width = rect.right - rect.left;


int height = rect.bottom - rect.top;


//调整属性页的大小和位置


m_sheet.SetWindowPos(NULL, 20, 50, 0, 0, SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);


这个方案可以自由在主对话框中加一些必要的控件,而且属性页中的控件也都分散在了各个子对话框类中,使用非常方便。


方案五


使用Tab Control,并且从CTabCtrl控件类继承自己的类(CTabSheet)来处理。


首先我先介绍一下如何使用CTabSheet。


先要制作子对话框类,这次的子对话框类不要从CPropertyPage继承,而是直接从CDialog继承。并且各个子对话框资源的属性应设置为:Style为Child, Border为None。


在主对话框资源中,加入一个Tab Control,并且适当调整位置和大小。利用ClassWizard来为这个Tab Control创建一个CTabSheet的控件变量。


在主对话框的OnInitDialog()加入:


m_sheet.AddPage("tab1", &m_page1, IDD_DIALOG1);


m_sheet.AddPage("tab2", &m_page2, IDD_DIALOG2);


m_sheet.Show();


就这样就可以在对话框上制作出一个完美的属性页了。效果和上图完全一样。


下面我就来讲讲CTabSheet类的细节内容。


CTabSheet是从CTabCtrl继承来的,用于Tab Control的控件类。在类中有一个成员变量用来记录各子对话框的指针CDialog* m_pPages[MAXPAGE]; MAXPAGE是该类所能加载的标签的最大值。


类中有一个AddPage方法,用于记录子对话框的指针和所使用对话框资源的ID号。


BOOL CTabSheet::AddPage(LPCTSTR title, CDialog *pDialog,UINT ID)


{


if( MAXPAGE == m_nNumOfPages )


return FALSE;


//保存目前总的子对话框数


m_nNumOfPages++;


//记录子对话框的指针、资源ID、要在标签上显示的文字


m_pPages[m_nNumOfPages-1] = pDialog;


m_IDD[m_nNumOfPages-1] = ID;


m_Title[m_nNumOfPages-1] = title;


return TRUE;


}


在使用AddPage加入了若干子对话框后,必须调用CTabSheet的Show方法来真正生成标签和子对话框。


void CTabSheet::Show()


{


//利用CDialog::Create来创建子对话框,并且使用CTabCtrl::InsertItem来加上相应的标签


for( int i=0; i < m_nNumOfPages; i++ )


{


m_pPages[ii]->Create( m_IDD[ii], this );[ii][ii]


InsertItem( i, m_Title[ii] );[ii]


}


//由于对话框显示时默认的是第一个标签被选中,所以应该让第一个子对话框显示,其他子对话框隐藏


m_pPages[0]->ShowWindow(SW_SHOW);


for( i=1; i < m_nNumOfPages; i++)


m_pPages[ii]->ShowWindow(SW_HIDE);[ii]


SetRect();


}


生成好标签和子对话框后,调用CTabSheet::SetRect来计算并调整属性页的大小。


void CTabSheet::SetRect()


{


CRect tabRect, itemRect;


int nX, nY, nXc, nYc;


//得到Tab Control的大小


GetClientRect(&tabRect);


GetItemRect(0, &itemRect);


//计算出各子对话框的相对于Tab Control的位置和大小


nX=itemRect.left;


nY=itemRect.bottom+1;


nXc=tabRect.right-itemRect.left-2;


nYc=tabRect.bottom-nY-2;


//利用计算出的数据对各子对话框进行调整


m_pPages[0]->SetWindowPos(&wndTop, nX, nY, nXc, nYc, SWP_SHOWWINDOW);


for( int nCount=1; nCount < m_nNumOfPages; nCount++ )


m_pPages[nCount]->SetWindowPos(&wndTop, nX, nY, nXc, nYc, SWP_HIDEWINDOW);


}


在单击标签栏后,应该是相应的子对话框显示,正在显示的子对话框应该隐藏。因此利用ClassWizard来处理WM_LBUTTONDOWN消息。


void CTabSheet::OnLButtonDown(UINT nFlags, CPoint point)


{


CTabCtrl::OnLButtonDown(nFlags, point);


//判断是否单击了其他标签


if(m_nCurrentPage != GetCurFocus())


{


//将原先的子对话框隐藏


m_pPages[m_nCurrentPage]->ShowWindow(SW_HIDE);


m_nCurrentPage=GetCurFocus();


//显示当前标签所对应的子对话框


m_pPages[m_nCurrentPage]->ShowWindow(SW_SHOW);


}


}


这样利用CTabSheet这个类就可以轻松地在对话框上放置自己的属性页了,并且控件都分散在各子对话框类中,符合对象封装的思想。而且用这个方法来制作属性页就可以利用ClassWizard来轻松地生成消息映射处理Tab Control的消息了。例如:可以处理TCN_SELCHANGE消息来对切换了标签时进行一些动作。


  方案五另一写法
           思路:当我们调用InsertItem()这个函数的时候,选项卡控件将会添加一个标签页,这个时候,我们将自己的对话框的窗体的指针与此标签页关联起来,当用户进行标签页的切换的时候,我们根据当前是哪个标签页,显示哪个对话框,不是与当前标签页关联的对话框,我们将其隐藏即可.这样我们便可以实现选项卡控件.
        第一步:新建一个自己的类CTabSheet继承CTabCtrl.
        第二步:定义有用的成员变量
        CDialog* m_dlgWnd[MAXTABPAGE]; //这个是存放对话框指针的指针数组
        int m_curTabNumber; //记录当前用户添加了几个标签页
        int m_selTabID;  //当前用户点击的标签页的ID
        第三步:添加成员函数
        //通过这个函数,可以将一个对话框指针与添加的标签页关联起来,insWnd是创建的非模式对话框的指针,wndID是对话框的ID,pageText是标签页的标题
        void CreateTabPage(CWnd *insWnd, int wndID,CString pageText) 
       //添加控件的点击事件的处理,当点击后得到当前点击的标签页的ID,然后将与此标签页相关的对话框显示,其它的隐藏即可
        void OnLButtonDown(UINT nFlags, CPoint point) 
       通过添加以上的成员变量及成员函数即可实现一个简单的选项卡控件的用法
       下面我将这两个成员函数的代码贴出来,并详细讲解
        //创建并且增加一个标签页
    //创建并且增加一个标签页
void CTabSheet::CreateTabPage(CWnd *insWnd, int wndID,CString pageText)
{
     if (m_curTabNumber >= MAXTABPAGE)
     {
         MessageBox("标签页己经达到最大!","创建出错!",MB_OK);
         return;
     }
        //首先new一个对话框的指针,但是不要调用create函数,再将些指针当成参数传进来即可,创建己由此函数做完
     if (NULL == insWnd)
     {
         MessageBox("标签页为空","创建出错",MB_OK);
         return;
     }
                 //创建对话框,并且增加标签页
     CDialog* curDlg = (CDialog*)insWnd;
     curDlg->Create(wndID,this);
     int suc = InsertItem(m_curTabNumber,pageText);
     if (-1 == suc)
     {
         MessageBox("插入标签页失败","失败",MB_OK);
         return;
     }
     curDlg->ShowWindow(SW_SHOW);
     //将这个对应的窗体指针存放起来
     m_dlgWnd[m_curTabNumber] = curDlg;
     //此时选择当前页面
     SetCurSel(m_curTabNumber);
     m_selTabID = m_curTabNumber;
     m_curTabNumber ++;
}
//点击左键事件,处理
void CTabSheet::OnLButtonDown(UINT nFlags, CPoint point) 
{
    // TODO: Add your message handler code here and/or call default
    CTabCtrl::OnLButtonDown(nFlags, point);
    //得到当前用户点击的标签页的ID
    int curSelect = GetCurSel();


    //得到当前标签页的位置以便设置对话框显示的位置


    CRect curRect;
    GetClientRect(curRect);
    if (-1 == curSelect)
    {
        return;
    }
               //查找标签页,将与当前用户点击的标签页相关的对话框显示出来,其它的对话框隐藏
    for (int i = 0; i < m_curTabNumber; i ++)
    {
        if (i == curSelect)
        {
                                       m_dlgWnd[ii]->SetWindowPos(NULL,0,20,curRect.Width(),curRect.bottom,SWP_SHOWWINDOW);[ii]
        }
        else
        {
               m_dlgWnd[ii]->SetWindowPos(NULL,0,20,curRect.Width(),curRect.bottom,SWP_HIDEWINDOW);[ii]
        }
    }
    m_selTabID = curSelect;
    Invalidate();
    //CTabCtrl::OnLButtonDown(nFlags, point);
}


以上为关键的两个函数,下面介绍调用的方法
创建非模式的对话框
CTabSheet m_tabSheet;
CMyDlg* m_dlg = new CMyDlg;
m_tabSheet.CreateTabPage(m_dlg ,IDD_DLG_ID,"第一个标签页");
这样就可以产生一个标签页了,当然还可以继续调用此函数添加标签页 
QQ: 378890364 微信:wwtree(省短信费) 紧急事宜发短信到0061432027638  本站微博:http://t.qq.com/wwtree QQ群:122538123
级别: 管理员
发帖
8532
金币
2762
威望
3231
贡献值
0
元宝
0
只看该作者 4楼 发表于: 2010-08-21
为 CTreeCtrl增加上下文菜单
在 Windows 平台上窗口化子系统鼓励开发人员处理上下文菜单请求在顶级窗口。 传统 Windows 编程范例因此集中的所有上下文菜单请求在几个窗口和对话框过程处理的代码。 但是,与对象的方向编程另一种解决方案可以被采纳,即要处理控件内的 WM_CONTEXTMENU 消息从而创建独立的实体和对象本身。 窗口和对话框,此方法允许在多个只是一个父控件的重复使用。


在树的视图控件的情况下它将嵌入的拖放功能要求以获得从所在的窗口的独立性采取特殊步骤。树视图控件接收 WM_RBUTTONDOWN 消息后,邮件是不由控件转发,直到收到 WM_RBUTTONUP 消息。 如果拖放操作时,或者不初始化,将决定该控件。
如果用户未启动拖放操作,USER32 生成 WM_CONTEXTMENU 消息,并将其发送到树视图控件。 如果树视图控件不能处理此邮件然后将默认窗口过程转发它到控件的父窗口。
如果该用户不启动拖放操作然后将该控件的窗口过程发送 WM_NOTIFY (代码 NM_RCLICK) 邮件,然后 WM_CONTEXTMENU 消息向父窗口。
因此,请考虑以下三种情况:
当按下 SHIFT + F10,并在控件获得焦点时 WM_CONTEXTMENU 消息发送到树视图控件。
当用户执行一个拖放操作 WM_CONTEXTMENU 消息被发送到树视图控件中。
当用户右击、 WM_NOTIFY (代码 NM_RCLICK) 消息通过树控件发送到控件的父级。 如果您要处理反射的通知如下所示,控件将在所有情况下显示上下文菜单。
若要实现树视图控件的上下文菜单建议 WM_CONTEXTMENU 和反射的 WM_NOTIFY (NM_RCLICK) 消息的消息处理程序由该控件实现。 例如:


BEGIN_MESSAGE_MAP(CMyTreeCtrl, CTreeCtrl)
//{{AFX_MSG_MAP(CMyTreeCtrl)
ON_NOTIFY_REFLECT(NM_RCLICK, OnRClick)
ON_WM_CONTEXTMENU()
//}}AFX_MSG_MAP
END_MESSAGE_MAP()


void CMyTreeCtrl::OnRClick(NMHDR* pNMHDR, LRESULT* pResult) 
{
TRACE0("CMyTreeCtrl::OnRClick()\n");
// Send WM_CONTEXTMENU to self
SendMessage(WM_CONTEXTMENU, (WPARAM) m_hWnd, GetMessagePos());
// Mark message as handled and suppress default handling
*pResult = 1;
}


void CMyTreeCtrl::OnContextMenu(CWnd* pWnd, CPoint ptMousePos) 
{
// if Shift-F10
if (ptMousePos.x == -1 && ptMousePos.y == -1)
ptMousePos = (CPoint) GetMessagePos();


ScreenToClient(&ptMousePos);


UINT uFlags;
HTREEITEM htItem;

htItem = HitTest( ptMousePos, &uFlags );


if( htItem == NULL )
return;

m_hActiveItem = htItem;


CMenu menu;
CMenu* pPopup;


// the font popup is stored in a resource
menu.LoadMenu(IDR_TREEITEM_CONTEXTMENU);
pPopup = menu.GetSubMenu(0);
ClientToScreen(&ptMousePos);
pPopup->TrackPopupMenu( TPM_LEFTALIGN, ptMousePos.x, ptMousePos.y, this );
}

QQ: 378890364 微信:wwtree(省短信费) 紧急事宜发短信到0061432027638  本站微博:http://t.qq.com/wwtree QQ群:122538123
级别: 管理员
发帖
8532
金币
2762
威望
3231
贡献值
0
元宝
0
只看该作者 5楼 发表于: 2010-08-22
MFC HTML Dialog用法入门

开发环境:VS7,Windows XP,Windows 2K 

  在VS7中添加了一种新的对话框类:CDHtmlDialog,顾名思义就是能够显示DHTML内容的对话框,但不同与以前的CHTMLView不同的是添加了对DHTML的支持,能够响应各种DHTML的事件,而且能够方便的得到网页上的各种内容和输入。在这以前要完成这些功能必须通过复杂的COM接口来完成,而现在MS MFC已经为我们做好了这一切。 


  在下面我会按照下面的顺序讲解CDHtmlDialog的用法。但本文也只能对DHTML对话框的功能进行部分的讲解,更多更全的说明需要大家自己摸索和查找资料。本文的目的是在于讲解如何得到和设置网页上的各种元素的值,如何处理事件。 


  此外在阅读本文前你必须对HTML和DHTML有所了解。

类成员函数介绍 

  构造函数

  CDHtmlDialog( );
  CDHtmlDialog(
   LPCTSTR lpszTemplateName,
   LPCTSTR szHtmlResID,
   CWnd *pParentWnd = NULL 
  );
  CDHtmlDialog(
   UINT nIDTemplate,
   UINT nHtmlResID = 0,
   CWnd *pParentWnd = NULL 
  );

  你可以看到和CDialog不同的就在于第二个参数,表示在对话框创建时是否自动加载HTML,这里必须说明的是HTML必须以资源的形式存放,这个参数指明的是资源的ID或名称。


或者你可以利用 

  BOOL LoadFromResource(
     LPCTSTR lpszResource 
  );
  BOOL LoadFromResource(
     UINT nRes 
  );
  将
HTML内容在后期进行装载。

  页面浏览 

  此外一些函数如:OnNavigateComplete,OnBeforeNavigate,Navigate等用于页面转换的函数,在以前的CHtmlView中就有这里就不再作讲解。 

  
得到当前URL 

  void GetCurrentUrl(
     CString& szUrl 
  );

  
得到网页中的DHTML元素的指定接口 

  HRESULT GetElementInterface(
     LPCTSTR szElementId,
     REFIID riid,
     void** ppvObj 
  );

  第一个参数指定,第二个参数指定接口ID,第三个参数返回接口指针。 

  
得到网页中的DHTML元素的IHTMLElement接口 

  HRESULT GetElement(
     LPCTSTR szElementId,
     IHTMLElement **pphtmlElement 
  );
  相当于调用 GetElementInterface(szElementId,IHTMLElement,pphtmlElement);

  这个函数非常的重要,因为如果要得到DHTML的内容就必须通过页面上的各个元素的属性来得到,例如:对于Input type=text的属性value就是输入的值,对于p的属性innerText就是段落中的内容。

  函数的第二个参数就是元素的名称。 

  函数的第二个参数,是一个指向指针的指针,通过它得到IHTMLElement的接口。函数返回值是HRESULT其值的定义符合COM中对返回值的定义。(如果你不了解,可以简单的检测返回值是否等于S_OK)


得到元素的innerText和innerHTML的属性 

  innerHTML属性:
  BSTR GetElementHtml(
     LPCTSTR szElementId 
  );
  innerText属性:
  BSTR GetElementText(
     LPCTSTR szElementId 
  );
  相当于调用IHTMLElement接口的gett_innerHTML和get_innerText方法

  与之对应的是设置元素的innerText和InnerHTML属性:
  innerHTML属性:
  void SetElementHtml(
     LPCTSTR szElementId,
     BSTR bstrText 
  );
  innerText属性:
  void SetElementText(
     LPCTSTR szElementId,
     BSTR bstrText 
  );
  相当于调用IHTMLElement接口的put_innerHTML和put_innerText方法

  
示范代码 

  假设页面上的代码为:<p id=p2>test</p>,执行下面代码可以显示原来的内容和将新内容设置为:abcdefg 

  CComPtr<IHTMLElement> spP1;
  HRESULT hr = S_OK;

   // Use the template overload
   hr = GetElementInterface("p2", &spP1);
  // 或者 hr = GetElement("p2", &spP1);
  // 或者 hr = GetElementInterface("p2", IID_IHTMLElement, reinterpret_cast<void**>(&spP1));
  if(S_OK == hr)
   {
    BSTR bStr;
    spP1->get_innerHTML(&bStr);
    CString szTemp;
    szTemp = bStr;
    AfxMessageBox(szTemp);

   CString strTable="abcdefg";
    BSTR bstrTable = strTable.AllocSysString();
    spP1->put_innerHTML(bstrTable);
   }
  或者利用SetElementHtml和SetElementText来进行设置:
   BSTR bStr;
   bStr = GetElementHtml("p2");
   CString szTemp;
   szTemp = bStr;
   AfxMessageBox(szTemp);
   CString strTable="ABCDEFG";
   BSTR bstrTable = strTable.AllocSysString();
   //spP1->put_innerHTML(bstrTable);
   SetElementHtml("p2",bstrTable);


事件处理映射宏 

  
基本格式

  
BEGIN_DHTML_EVENT_MAP(className )
  DHTML_EVENT_ONCLICK(elemName, memberFxn ) //处理onclick事件
  DHTML_EVENT_ONFOCUS(elemName, memberFxn ) //处理onfocus事件
  DHTML_EVENT_ONKEYDOWN(elemName, memberFxn ) //处理onkeydown事件
  DHTML_EVENT_ONMOUSEMOVE(elemName, memberFxn ) //处理mousemove事件
  DHTML_EVENT_ONMOUSEOUT(elemName, memberFxn ) //处理mousemoveout事件
  等等………
  END_DHTML_EVENT_MAP()

  更详细的说明可以查阅MSDN中DHTML Event Map Macros部分。MSDN中对可以处理的事件进行了详细的说明。DHTML中的事件与
Windows中消息不是同一个概念,虽然映射宏的形式很相同。 

  
添加映射处理代码 

  我在VC7中没有发现自动添加各种事件映射的方法,只能通过手工添加代码的方式。

  定义事件处理函数:
   函数原型为:HRESULT urClass::OnXXXXX(IHTMLElement* /*pElement*/)
  添加消息映射:
   BEGIN_DHTML_EVENT_MAP(urClass)
    DHTML_EVENT_ONCLICK(_T("id name"), OnXXXXX)
   END_DHTML_EVENT_MAP()

  下面是一段示范代码:
  // mydlg.h
  class CmydhtmlDlg : public CDHtmlDialog
  {
  // 构造
  public:
   CmydhtmlDlg(CWnd* pParent = NULL); // 标准构造函数

  // 对话框数据
   enum { IDD = IDD_MYDHTML_DIALOG, IDH = IDR_HTML_MYDHTML_DIALOG };

   protected:
   virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV 支持


HRESULT OnButtonOK(IHTMLElement *pElement);
   HRESULT OnButtonCancel(IHTMLElement *pElement);
   HRESULT OnButtonTest1(IHTMLElement *pElement);
   HRESULT OnButtonTest2(IHTMLElement *pElement);
   HRESULT OnButtonTest3(IHTMLElement *pElement);
   HRESULT OnSelectTest1(IHTMLElement *pElement);
   HRESULT OnDivMouseMove1(IHTMLElement *pElement);
   HRESULT OnDivMouseOut1(IHTMLElement *pElement);

  //mydlg.cpp
  BEGIN_DHTML_EVENT_MAP(CmydhtmlDlg)
  DHTML_EVENT_ONCLICK(_T("ButtonOK"), OnButtonOK)
  DHTML_EVENT_ONCLICK(_T("ButtonCancel"), OnButtonCancel)
   DHTML_EVENT_ONCLICK(_T("Test1"), OnButtonTest1)
   DHTML_EVENT_ONCLICK(_T("Test2"), OnButtonTest2)
   DHTML_EVENT_ONCLICK(_T("Test3"), OnButtonTest3)
   DHTML_EVENT_ONCHANGE(_T("s1"), OnSelectTest1)
   DHTML_EVENT_ONMOUSEMOVE(_T("d1"), OnDivMouseMove1 )
   DHTML_EVENT_ONMOUSEOUT(_T("d1"), OnDivMouseOut1 )
  END_DHTML_EVENT_MAP()

  HRESULT CmydhtmlDlg::OnButtonOK(IHTMLElement* /*pElement*/)
  {
   OnOK();
   return S_OK;
  }

  HRESULT CmydhtmlDlg::OnButtonCancel(IHTMLElement* /*pElement*/)
  {
   OnCancel();
   return S_OK;
  }

  HRESULT CmydhtmlDlg::OnButtonTest1(IHTMLElement* /*pElement*/)
  {
   AfxMessageBox("test1 button clicked");
   return S_OK;
  }

  HRESULT CmydhtmlDlg::OnSelectTest1(IHTMLElement* /*pElement*/)
  {
   TRACE("select1 changed\n");
   return S_OK;
  }

  HRESULT CmydhtmlDlg::OnDivMouseMove1(IHTMLElement* /*pElement*/)
  {
   TRACE("div1 mouse move\n");
   return S_OK;
  }

  HRESULT CmydhtmlDlg::OnDivMouseOut1(IHTMLElement* /*pElement*/)
  {
   TRACE("div1 mouse out\n");
   return S_OK;
  }

各种DDX帮助宏 

  
DDX宏介绍 

  
如同CDialog类一样,CHtmlDialog也提供各种DDX帮助宏来与HTML页面上的控件交换数据。

  遗憾的是VS7中无法为CDHTMLDialog 的子类自动添加DDX变量,必须通过手工添加。


设置innerText和innerHTML属性 

  DDX_DHtml_ElementInnerText(
     CDataExchange* dx,
     LPCTSTR name,
     CString& var 
     )
  和
   DDX_DHtml_ElementInnerHtml(
     CDataExchange* dx,
     LPCTSTR name,
     CString& var 
  )
  相当与前面提到的设置和获取innerText,innerHTML属性。

  获取和设置控件中的值

  在DHTML中利用“对象名程.value”可以得到控件中输入的值,利用DDX也同样可以得到。 
  
  DDX_DHtml_ElementValue(
     CDataExchange* dx,
     LPCTSTR name,
     var 
  )
  用于在控件和对象之间交换数据。

  使用方法

  假设HTML文件中代码如下 

  <p id="p4"><b>p4 for ddx</b></p>
  <input type="text" ID="input1" size="20" value="input1 for ddx" NAME="input1">
  <input type="text" ID="input2" size="20" value="101" NAME="input2">

  在H文件中添加变量定义:
  public: //DDX
   CString m_szP4,m_szInput1;
   int m_iInput2;

  在类的构造函数中赋初值:
  CmydhtmlDlg::CmydhtmlDlg(CWnd* pParent /*=NULL*/)
   : CDHtmlDialog(CmydhtmlDlg::IDD, CmydhtmlDlg::IDH, pParent)
   {
    m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
    m_szP4 = "test for p4";
    m_szInput1= "test for input1";
    m_iInput2 = 101;
   }

  在CPP文件中的void CmydhtmlDlg::DoDataExchange(CDataExchange* pDX)函数中添加代码:
  void CmydhtmlDlg::DoDataExchange(CDataExchange* pDX)
  {
   CDHtmlDialog::DoDataExchange(pDX);
   //// for html ddx
   DDX_DHtml_ElementInnerHtml(pDX,"p4",m_szP4); //对应 p4
   DDX_DHtml_ElementValue(pDX,"input1",m_szInput1); //对应 input1
   DDX_DHtml_ElementValue(pDX,"input2",m_iInput2); //对应 input2
  }
  使用是与CDialog一样利用UpdateData。
  HRESULT CmydhtmlDlg::OnButtonTest4(IHTMLElement* /*pElement*/)
  {
   UpdateData();
   TRACE("p4=%s\n",m_szP4);
   CString szTemp=m_szP4;
   m_szP4 =m_szInput1;
   m_szInput1=szTemp; //对换p4和input1中内容
   m_iInput2 ++; //将input2中数字加一
   UpdateData(FALSE);
   return S_OK;
  }


一个简单的例子 

  最后介绍一下如何利用VC7创建一个利用CDHtmlDialog的工程。

  首先创建工程,进行如下设置:

VC7 HTML Dialog开发实例讲解


  在资源管理中修改HTML文件:

VC7 HTML Dialog开发实例讲解


  最后添加自己的代码。我提供的例子中所使用的函数在上面都已经提到。大家可以下载这份
例子去进行一定的参考。

QQ: 378890364 微信:wwtree(省短信费) 紧急事宜发短信到0061432027638  本站微博:http://t.qq.com/wwtree QQ群:122538123
级别: 管理员
发帖
8532
金币
2762
威望
3231
贡献值
0
元宝
0
只看该作者 6楼 发表于: 2010-08-23
CDHtmlDialog类的使用心得
在CDHtmlDialog类使用中,总是会遇到HTML不能正确解析资源的问题。我的经验如下:


1。使用绝对路径在资源里引入HTML网页和图片资源:
     使用RES://应用程序名称/资源类型/#资源号,
     例如:如果你的应用程序名为c.exe,html网页或资源保存在HTML类型下。并且查看资源标示号为133,便可以这样引用:res://c.exe/html/#133。或这样引用:res
://c:/c.exe/html/#133


2。使用相对路径引用:
    如果使用方法1,总得去寻找应用程序名称,如果引入的是html中的图片,有诸多不便,譬如:应用程序如果改变了名称,那调用便会无效。为了防止这些纠葛,有一个非常好的办法就是:把所有使用的资源(图片,CSS,JS等),统统放到HTML资源类型下,引用的时候写成 res://#资源号就可以了。譬如上例便可以这样引用:res:/#133。当然好像res:/ 和res:// 是通用的。


3。FLASH控件的支持:
   我没有在资源里引用flash成功,看了看flash的官方文档,好像必须为绝对路径才可以。例如movie=“d:\\flash\\test.swf”是可以成功的。


4。不在资源里引用HTML,直接在本地文件里使用。可以在CDHtmlDialog的继承类里添加一个函数来处理。


void CDHtmlViewSpec::GotoUrl(LPCTSTR pszUrl) { CString strPath,str; DWORD dwSize=MAX_PATH; ::GetModuleFileName(NULL,strPath.GetBuffer(MAX_PATH),dwSize); //AfxGetResourceHandle() strPath.ReleaseBuffer(dwSize); str=strPath.Left(strPath.ReverseFind('\\')+1); ASSERT(pszUrl!=NULL); if(pszUrl!=NULL) { CString strUrl; strUrl=str+pszUrl;
Navigate(_T("file:///")+strUrl); } }
本函数为了方便没有对路径进行更多处理,在使用的时候,可以以exe文件所在的目录为当前目录,以相对路径表示pszUrl。调用方法:
gotoUrl("html\\test.html")
gotoUrl("..\\html2\test.htm")
这样载入HTML网页,网页内的资源定位都是相对路径定义即可。图片和flash都可以正常载入。
不过这样的话,就有个问题,用户可以随时修改你的html文件。如果你有消息处理的话,很容易就使你的程序崩溃。
我一般的做法就是修改html的扩展名,迫使一般用户不轻易修改它,譬如改称.dat。
QQ: 378890364 微信:wwtree(省短信费) 紧急事宜发短信到0061432027638  本站微博:http://t.qq.com/wwtree QQ群:122538123
级别: 管理员
发帖
8532
金币
2762
威望
3231
贡献值
0
元宝
0
只看该作者 7楼 发表于: 2010-08-23
用CDHtmlDialog实现WebGUI
自从去年年底一次棘手的界面,开始研究用web做界面到现在大约1年,这一年间不是局限在实现层面,也并非一直研究这一个问题,有很多问题其实不是问题, 只是自己没有想清楚或者思想没放开。对于一个界面开发人员,想必拉的对话框不少于100个,腻味不必说,光是对话框大小改变导致控件跟着变化都需要一番功 夫,加上界面美观,界面的风格统一,界面的灵活多变......,头痛。在对话框里面加载位图,加载gif,超链接......,啊,没法控制了吧!在考 虑远点,现在.net3.0技术已经完全打破应用和桌面的界限,我们的界面html资源完全可以放在一个web站点上,这样界面是完全动态的。

其 间写过2篇这方面的文章,基于vc6实现,绕弯很大。在vc7.1、vc8里面要简单很多,主要是把几个以前为公开的类公开了,最重要的是在CWnd里面 加入了一个虚函数CreateControlSite使得有机会改变控件站点以修改控件行为。在mfc类层次上,CHTMLView和 CDHtmlDialog为开发者提供了创建webgui的一系列基础设施,包括事件机制、窗口行为、以及对html文档操纵接口。我们在此基础上实现 webgui很简单,然而仍然困惑我很久,经理也催过我几次我一直未肯决定最终方案。在我脑袋里一直琢磨是要应用程序完全操纵html文档,还是html 访问应用获取信息,其实就是它们之间的通信模式。一直到昨天我才定下方案,应用通过IWebBrowser2接口操纵html元素,html通过 vbscript、java script脚本响应本身事件,访问应用。主要是考虑通信自然畅通,而以前我一味想通过应用指令完全控制html元素,导致去 解析html文档,费力不讨好。下面开始我的想法:

写一个dll,封装CDHtmlDialog,提供一个类似html容器的对话框,功 能就是加载html网页,以及创建与html呼应的com组件。它本身不包含与应用功能有关代码,应用有关的部分是html页面和对于的com功能组件。 这里需要对CDHtmlDialog进行了适当的改造以适合自己的目标:

首先从CDHtmlDialog派生一个类CHTMLContainerDlg,默认情况下会生成一个网页资源,这个网页是这个对话框创建时加载的,我们需要的其实是一个容器而不是一个具体的对话框,所以删除网页资源,修改对话框头文件:
enum { IDD = IDD_HTMLCONTAINERDLG, IDH = 0 };
这里把IDH修改为0,因为我们删除了网页资源。然而在对话框创建后会加载该资源,在CDHtmlDialog的OnInitDialog函数里面我们可以看到:
if (m_nHtmlResID)
        LoadFromResource(m_nHtmlResID);
    else if (m_szHtmlResID)
        LoadFromResource(m_szHtmlResID);
    else if (m_strCurrentUrl)
        Navigate(m_strCurrentUrl);
结果就是对话框一出现就会出现加载一个无效地址的页面,出现无法打开链接的页面,为了避免这个问题,需要重载OnInitDialog函数。其实就是拷贝mfc代码然后去掉上面那段代码就ok,强制不加载页面。那么为了加载指定页面,需要一个函数:
void CHTMLContainerDlg::SetHtmlAndCom(CString strURL, CString strProg)
{
    HRESULT        hr        = NOERROR;
    m_strURL = strURL;
    hr = m_spComDisp.CoCreateInstance(strProg);
    if(FAILED(hr))
    {
        TRACE(_T("Some error when create com object\n"));
    }
    SetExternalDispatch(m_spComDisp);
}
指定html的url和对应功能组件的progid,这样在网页里面可以通过脚本window.external访问该com组件。
这样就可以加载html网页,但是html页面里面的元素风格却是2k风格(至少在ie7以下版本是如此),这个怕是没起到一点美观作用,为之我考虑了半天,问过做web的人是否有办法,最终还是灵感光临,误撞上了。重载GetHostInfo函数
STDMETHODIMP CHTMLContainerDlg::GetHostInfo(DOCHOSTUIINFO* pInfo)
{
    pInfo->dwFlags = DOCHOSTUIFLAG_THEME;
    return S_OK;
}
这个多得不说,^_^。
下面就可以演示了,在vs2005里面找个向导来show一下:
CHTMLContainerDlg    dlg;
    TCHAR                szPath[MAX_PATH] 
= 0 };
    CString                strPath;
    GetCurrentDirectory(MAX_PATH, szPath);
    strPath 
= szPath;
    strPath 
+= _T("\\Default.htm");
    dlg.SetHtmlAndCom(strPath, _T(
"TestWebCom.WebComCtrl"));
    dlg.DoModal();


对话框标题其实可以通过解析html文档获取title标题设置,目前还未处理。下面看看html与应用交互的组件。
生成一个atl工程,TestWebCom,添加一个com组件WebComCtrl,添加方法处理上面那个带...的按钮(文件夹浏览按钮):
STDMETHODIMP CWebComCtrl::ShowFolderBrowser(void)
{
    AFX_MANAGE_STATE(AfxGetStaticModuleState());

    
// TODO: 在此添加实现代码
    AfxMessageBox(_T("In Com, you can show folder select dialog"));
    
return S_OK;
}

这里不作具体处理,只是象征性弹出一个对话框。好了,上面我们在对话框里面已经设置了com组件的progid,这里可以把html和组件关联上了,通过脚本可以访问com组件方法:
<BUTTON CLASS="buttonClass3Custom" ID="BrowseBtn" TYPE="BUTTON" TITLE="浏览头文件。" onClick="OnBrowseHeaderFile();"></BUTTON>
脚本如下:
function OnBrowseHeaderFile()
{
    window.external.ShowFolderBrowser();
}
下面运行试一试,按下选择文件夹按钮会出现如下询问组件是否安全的对话框:

这个很恼人,用户可没有耐心忍受每次多弹出这个对话框询问组件是否安全。我开始打算将组件实现安全接口解决掉此问题,不过不知道缘何,没有成功,网上搜索一下好像说在ie7里面无效,没办法还是看mfc源码来解决问题。
CDHtmlDialog类获取external代码如下:
STDMETHODIMP CDHtmlDialog::GetExternal(IDispatch **ppDispatch)
{
    
if(ppDispatch == NULL)
        
return E_POINTER;
        
    
*ppDispatch = NULL;
    
if (m_spExternalDisp.p && CanAccessExternal())
    
{
        m_spExternalDisp.p
->AddRef();
        
*ppDispatch = m_spExternalDisp.p;
        
return S_OK;
    }

    
return E_NOTIMPL;
}
看到CanAccessExternal函数,肯定就是验证安全性的代码,找到函数声明,幸好是虚函数,重载直接返回TRUE:
BOOL CHTMLContainerDlg::CanAccessExternal()
{
    
// we trust all com object (haha, you can make virus)
 return TRUE;
}
有兴趣的朋友可以看下内部实现。
这下就好了,按下网页选择文件夹按钮,弹出对话框:

一套流程完备,方案个人觉得不错,各司其职,通信自然畅通,一个html配对一个com功能组件,功能组件化不仅使代码封装性好,而且可以用于多种语言。

由于此技术不用于公司开发,今整理提供下载
QQ: 378890364 微信:wwtree(省短信费) 紧急事宜发短信到0061432027638  本站微博:http://t.qq.com/wwtree QQ群:122538123
级别: 管理员
发帖
8532
金币
2762
威望
3231
贡献值
0
元宝
0
只看该作者 8楼 发表于: 2010-09-18
VC中窗口ID,句柄,指针三者相互转换

ID--HANDLE--HWND三者之间的互相转换
id->句柄        hWnd = ::GetDlgItem(hParentWnd,id);
id->指针        CWnd::GetDlgItem();
句柄->id        id = GetWindowLong(hWnd,GWL_ID);
句柄->指针    CWnd *pWnd=CWnd::FromHandle(hWnd);
指针->ID       id = GetWindowLong(pWnd->GetSafeHwnd,GWL_ID);
                                            GetDlgCtrlID();
指针->句柄     hWnd=cWnd.GetSafeHandle() or mywnd->m_hWnd;  

指针的使用在编程过程中至关重要,恰到好处并能正确无误的使用指针不但能够提高程序自身的运行效率,而且有助于节省程序执行所需要消耗的资源。指针对应着某个数据在内存空间中的地址,得到了指针就可以自由地修改该数据。句柄代表指针的“指针”,也可以将其比作表中数据项的索引值( 表对应某个进程自身的内存空间 )。句柄是间接的引用对象。

        指针和句柄的不同之处

  • 句柄所指的可以是一个很复杂的结构,并且很有可能与系统有关的,比如上面所说线程的句柄,它指向的就是一个类或者结构,它和系统有很密切的关系。当一个线程由于不可预料的原因而终止时,系统就可以通过句柄来回收它所占用的资料,如CPU,内存等等。反过来想,这些句柄中的某一些,是与系统进行交互用的。
  • 指针它也可以指向一个复杂的结构,但通常是由用户自我定义的,所以一些必需的工作都要由用户自己完成,特别是在删除的时候。
  • 另外需要注意的是句柄往往有自己的存在区限,比如一个进程,如果将其传递到另一个进程中,句柄也就失去了意义( 表中数据项的索引值,索引离开了具体的表也就失去了意义 )。

        具体转换

        ( 句柄转为指针 )

        CWnd* pWnd=FromeHandle(hMyHandle); 
        pWnd->SetWindowText("Hello World!"); 

        or 

        CWnd* pWnd; 
        pWnd->Attach(hMyHandle);  

        MFC类中有的还提供了标准方法,比如Window句柄:

        static CWnd* PASCAL FromHandle( 
        HWND hWnd 
        ); 
        HWND GetSafeHwnd( ) const; 

        对于位图: 
        static CBitmap* PASCAL FromHandle( 
       HBITMAP hBitmap 
         );  
         static CGdiObject* PASCAL FromHandle( 
       HGDIOBJ hObject 
         ); 
        HGDIOBJ GetSafeHandle( ) const;

        当然,更详细的信息需要在具体使用中自我查询。

建 议:指针和句柄的使用属于比较复杂、危险性较高的应用,在具体实践中,如果可以,尽量不要使用指针和句柄,最好选择现有的、封装完好的方式来实现,更别提指针同句柄的转换了,它更加危险。

        比如在操作字符串时,尽量使用CString类来实现,通过定义好的构造、析构函数来完成分配和回收,最好不要通过指针来动态操作。

QQ: 378890364 微信:wwtree(省短信费) 紧急事宜发短信到0061432027638  本站微博:http://t.qq.com/wwtree QQ群:122538123
级别: 管理员
发帖
8532
金币
2762
威望
3231
贡献值
0
元宝
0
只看该作者 9楼 发表于: 2010-09-18
得到VC++中的常用指针
摘要:指针,在VC++中是很常见的,这里我们并不打算去详细讲解在C++中那样的指针用法(我们会有另外的文章去详细讨论),这里主要讲一下VC++中常见的对指针获取的方法,包括:工具条、状态条、控件和窗口的指针。
     获取工具条的指针
     在缺省状态下,有一个默认的工具条AFX_IDW_TOOLBAR,我们可以根据相应的ID去获取工具条指针,方法如下:
     CToolBar* pToolBar=(CToolBar*)AfxGetMainWnd()->GetDescendantWindow(AFX_IDW_TOOLBAR);
     是不是很简单?
 
     获取状态条的指针
     在缺省状态下,有一个默认的状态条AFX_IDW_STATUS_BAR,我们自然也可以根据相应的ID去获取状态条指针,方法如下:
     CStatusBar* pToolBar=(CStatusBar*)AfxGetMainWnd()->GetDescendantWindow(AFX_IDW_STATUS_BAR);
     是不是同样很简单?

    获取控件的指针 
    这里有两种方法。
    一、调用CWnd: : GetDlgItem,获取一个CWnd*指针调用成员函数。例如,我们想获取CButton指针,方法如下:
    CButton* pButton=(CButton*) GetDlgItem (IDC_MYBUTTON);
    二、可以使用ClassWizard将控件和成员变量联系起来。在ClassWizard中简单地选择Member Variables标签,然后选择Add Variable …按钮。如果在对话资源编辑器中,按下Ctrl键并双击控件即可转到Add Member Variable对话。

    在文档类中调用视图类指针
    我们可以利用文档类的成员函数GetFirstView()和GetNextView()遍历视图。

    在视图类中调用文档类
    其实,在视图类中有一个现成的成员函数供我们使用,那就是:GetDocument();利用它我们可以很容易的得到文档类指针,我们先看一下GetDocument()函数的实现:
    CColorButtonDoc* CColorButtonView::GetDocument() 
   {
 ASSERT(m_pDocument->IsKindOf(RUNTIME_CLASS(CColorButtonDoc)));
 return (CColorButtonDoc*)m_pDocument;
   }
   这里实际上是将m_pDocument强制转换成CColorButtonDoc*,也就是我们想要的。

   在框架类中调用文档类、视图类
   这里我们可以利用GetActiveXXXXX()去掉用当前激活的文档和视图:
   CMyDoc*  pDoc=(CMyDoc*)GetActiveDocument();
   CMyView* pView=(CMyView*)GetActiveView();

   获得应用程序指针
   这个很简单,一句话搞定:
   CMyApp* pApp=(CMyApp*)AfxGetApp();

   获得主框架指针
   在类CWinThread里面有一个公有的成员变量:CWnd* m_pMainWnd; 它存在的主要目的就是提供我们获得CWnd指针,我们可以利用它来达到我们的目的:
   CMainFrame* pFrame=(CMainFrame*)(AfxGetApp()->m_pMainWnd);
   
   
通过鼠标获得子窗口指针
   这里我们要用到一个不太常用的函数:ChildWindowFromPoint。他的原型如下:
   CWnd* ChildWindowFromPoint(POINT point) const;
   CWnd* ChildWindowFromPoint(POINT point,UINT nFlags) const;
   这个函数用于确定包含指定点的子窗口,如果指定点在客户区之外,函数返回NULL;如果指定点在客户区内,但是不属于任何一个子窗口,函数返回该CWnd的指针;如果有多个子窗口包含指定点,则返回第一个子窗口的指针。不过,这里还要注意的是:该函数返回的是一个伪窗口指针,不能将它保存起来供以后使用。
   对于第二个参数nFlags有几个含义:
   CWP_ALL             
file://不忽略任何子窗口
   CWP_SKIPNIVSIBLE    
file://忽略不可见子窗口
   CWP_SKIPDISABLED    
file://忽略禁止的子窗口
  CWP_SKIPRANSPARENT  
file://忽略透明子窗口
QQ: 378890364 微信:wwtree(省短信费) 紧急事宜发短信到0061432027638  本站微博:http://t.qq.com/wwtree QQ群:122538123
描述
快速回复

您目前还是游客,请 登录注册
如果您提交过一次失败了,可以用”恢复数据”来恢复帖子内容