公告(重要):

本blog中的源码和其它文件均以ZIP格式压缩. 请使用下载工具下载文件链接(推荐用FlashGet或类似工具),否则可能会出现下载的压缩包不完整,无法解压的情况.

2006-11-30

分隔视图与TreeView的显示和控制

分隔视图与TreeView都是在界面中常用的元素,本文介绍了如果使用这两种元素,并说明了TreeView的显示与控制方法.:如何使TreeView响应鼠标的单,双击,右击等事件.

  1. 首先说明如何分隔一个窗口.

MFC,分隔一个窗口,这个窗口必须是CWnd或其派生类.这里面以CFrameWnd为例.其它的也一样.

现在假如有一个类: class CMainFrame : public CframeWnn,我们在它的头文件中加入:

CSplitterWnd m_wndSplitter;

然后重载它的OnCreateClient函数.

在里面加入如下代码:

if(!m_wndSplitter.CreateStatic(this,1,2))

{

return FALSE;

}

if(!m_wndSplitter.CreateView(0,1,RUNTIME_CLASS(CMainView),size,pContext))

return FALSE;

if(!m_wndSplitter.CreateView(0,0,RUNTIME_CLASS(CRoomsTree),size,pContext))

return FALSE;


(CMainView*)m_pView = (CMainView*)m_wndSplitter.GetPane(0,1);

(CRoomsTree*)m_pTree = (CRoomsTree*)m_wndSplitter.GetPane(0,0);


其中, CreateStatic函数的原型可以查下MSDN,利用它就可以做出各种不同的分隔效果.

上面代码中的CMainViewCRoomsTree都是视类的派生类.CreatView函数也是可以查下MSDN,里面有具体的说明.

那么如何得到这两个类的指针呢?

(CMainView*)m_pView = (CMainView*)m_wndSplitter.GetPane(0,1);

(CRoomsTree*)m_pTree = (CRoomsTree*)m_wndSplitter.GetPane(0,0);

这两句就可以了.

下面运行程序,就可以看到效果了.

  1. 下面讲如果制作树视图(控件).

就拿上面的例子来说,我们在它的基础上加入一个树控件.

生成一个新类,就是上面的CRoomsTree,它的基类是CTreeView,在它的头文件中加入:

CTreeCtrl & m_Tree;

然后在构造函数中初始化它:

CRoomsTree::CRoomsTree():m_Tree(GetTreeCtrl())

这里是调用的它的考备构造函数,具体的找本C++的书看就行了.

下面讲如何给树的项目制作图标.

在其头文件中加入: CImageList m_ImageList ;

然后在需要显示树的时候(一般是OnCreate函数中):

m_ImageList.Create(IDB_TREEIMAGE,16,1,RGB(255,0,255));

这里面IDB_TREEIMAGE是一张位图在资源中的ID,这张位图要你自己画.

然后加上这句就把位图加到树上面了:

m_Tree.SetImageList(&m_ImageList,TVSIL_NORMAL);

下面讲如何给树插入项目:

m_Tree.InsertItem,

这个函数的返回值是一个项目的句柄,参数有四个,1.项目的文字,2.项目被选中时要显示的图标在ImageList中的位置,3.项目没选中时的图标.4.该项目父项目的句柄,如果为NULL,该项目就显示在根位置.

好了,现在,随便插入几个项目,运行程序,就可以看到效果了.


那么,我们如何控制这个树呢?如何让它响应我们的操作?


就拿单击来说吧

响应它的: NM_CLICK消息,在消息回调中加入:

GetCursorPos(&m_CurrentPnt);

ScreenToClient(&m_CurrentPnt);

m_CurrentItem = m_Tree.GetSelectedItem();

*pResult = 0;


用这种方法得到单击时鼠标的位置,和当前被选择的项目的句柄,然后用:

CRect rect;

m_Tree.GetItemRect(m_AllItem,&rect,TRUE);

if (rect.PtInRect(m_CurrentPnt))

{

//

}

来看是不是点击了其中之一就可以了.

如果想知道到底是点击的哪个项目,就要用上面的办法把所有的项目检查一遍



这样,就差不多了.双击也是一样的.


在实际的操作中,会出现一个问题,就是双击的时候,它会触发单击和双击两个事件.

这怎么办呢?

我用的办法是,在类中设一开关变量,

单检查到有单击事件时,开关打开,SetTimer,一般为500ms,然后,在双击事件中,关闭开关,Timer的响应函数中,先检查开关是否打开,如果打开,再做相应的单击后的处理就可以了.



2006-10-14

在2000和XP系统下系统的通知区(任务栏)创建图标并在其上响应鼠标事件

很多程序都在任务栏上的时钟边上创建了自己的图标,使得自己的程序的用户界面更加的友好.本文就说明了如果在任务栏上的系统通知区为自己的程序创建一个图标,并在其上响应鼠标事件.

1.首先先在任务栏上创建图标.

定义一全局变量
NOTIFYICONDATA nd;
//这里面的NOTIFYICONDATA是一个存着通知区图标有关信息的节构,一会我们要填充它.
在程序的初始化过程中(比如在对话框程序中,可以在OnInitDialog函数中),我们填充刚才定义的节构,如下:
nd.cbSize = sizeof (NOTIFYICONDATA);
nd.hWnd = m_hWnd;
nd.uID = IDI_ICON1;
nd.uFlags = NIF_ICONNIF_MESSAGENIF_TIP;
nd.hIcon = AfxGetApp()->LoadIcon(IDI_ICON1);
strcpy(nd.szTip, "文本");
Shell_NotifyIcon(NIM_ADD, &nd);

上面代码中,m_hWnd是当前窗口的句柄,IDI_ICON1为你要显示的图标在资源中的ID,nd.szTip中填充的是当鼠标停在图标上时要显示的Tip中的文字.可以按照你自己的要求填充这些变量.

现在,编译,运行程序,我们看到,通知区已经有了你的图标了.这样,第一步就完成了.

2.为图标加上响应鼠标操作的功能.

在刚才定义NOTIFYICONDATA nd 的地方,再加上一个消息宏:
#define WM_NOTIFYICON WM_USER+1005
在刚才填充NOTIFYICONDATA 结构的地方,也就是OnInitDialog函数中,加入:
nd.uCallbackMessage = WM_NOTIFYICON;

然后手动填加这个消息的映射:
在头文件中:
LONG OnNotifyIcon(WPARAM wParam, LPARAM lParam);
在CPP文件中:
BEGIN_MESSAGE_MAP(CCBabyImageDlg, CDialog)
//{{AFX_MSG_MAP(C*****)
....
....
....
//}}AFX_MSG_MAP
ON_MESSAGE(WM_NOTIFYICON, OnNotifyIcon)
//这句是我们要加入的

现在就可以写这个回调函数了:
LONG C*****Dlg::OnNotifyIcon(WPARAM wParam, LPARAM lParam)
{
switch ( lParam )
{
case WM_RBUTTONDOWN:
{
CMenu m_PopupMenu;
CPoint Point;
CMenu *hh;
m_PopupMenu.LoadMenu(IDR_MENU1);
hh=m_PopupMenu.GetSubMenu(0);
GetCursorPos( &Point );
SetForegroundWindow();
hh->TrackPopupMenu(
TPM_LEFTALIGN TPM_RIGHTBUTTON,
Point.x, Point.y, this);

//MessageBox("DFHJKDLJFKL",NULL,MB_OK);
}
break;
case WM_LBUTTONDBLCLK:
case WM_LBUTTONDOWN:
ShowWindow(SW_SHOW);
break;

break;
}
return 0;
}
上面这个函数中,case WM_RBUTTONDOWN:是单击右键
case WM_LBUTTONDBLCLK:是双击左键
case WM_LBUTTONDOWN:是单击左键,你可以在它们里面填加自己需要的内容.
而IDR_MENU1是你需要弹出的菜单.在资源中可以创建它.
不过这里有个问题,就是在你创建菜单资源的时候,要把你需要的项目放入菜单的第二级中,第一级就随便写什么都可以了.

到此,这个程序就几乎完成了.不过还有一点小问题,当程序退出或者你需要的时候要把这个图标除掉,这很简单,在退出过程中加入下面代码:
Shell_NotifyIcon(NIM_DELETE, &nd);
当执行到这句时,图标便会被除掉了.

(全文完)

2006-10-09

在VC6中使用GDI的方法(附一个使用GDI+的批量图片格式转换程序)

GDI+是MS官方的图像处理包,好处自不多说,很多高手都强烈推荐它.
本文说明如何在VC6的平台上配置和使用GDI+.并用一个很实用的小程序说明如何使用GDI+转换硬盘上的图片的格式.


下载GDI+的相关文件.网上给的很多连接都失效了.我就传了一个到我的网络硬盘上.请点击后面连接直接下载.
点击此处下载GDI+包

下载完成后,就可以进行配置了.网上给出的例子,要对VC进行设置,那样有些烦麻.我的办法是把包中的文件直接放到VC的系统目录下去.这样就不用对VC进行设置了,以后用起来也方便.
打开刚刚下载的压缩包,里面有include头文件,一个LIB,和一个DLL.
进入你的D:\Program Files\Microsoft Visual Studio\VC98目录下,把lib文件放到Lib文件夹里面.把那些头文件全部放进Include里面.就样就可以了.
现在,配置已经基本完成了.可以在工程中使用GDI+了.
在工程中使用GDI+,要在你的文件前包含它的头文件,引进它的库.

#ifndef ULONG_PTR
#define ULONG_PTR unsigned long*
#endif
#include
using namespace Gdiplus;
#pragma comment(lib, "gdiplus.lib")
做完以上工作,你就可以使用GDI+了!!!

也许以上说的太空洞了.下面就用一个实例来说明GDI+的用法.注意,本例子只是给出GDI+的一些基本概念,远没有用到GDI+的强大功能.GDI+的功能,还要读者自己慢慢去研究.
这个例子实现的是批量图片格式的转换.操作简单,功能也简单.
这个工程是基于VC6的,源文件请点击后面连接下载.
使用GDI+转换图片格式的例子的下载.

编写的过程是:

1.建立一个基于对话框的应用程序.
2.在stdafx.h中加入:

#ifndef ULONG_PTR
#define ULONG_PTR unsigned long*
#endif
#include
using namespace Gdiplus;

3.编辑对话框:
主要是调整大小,加入四个图片.具体看工程源文件.
4.在OnInitDialog()里加入如下代码.

DragAcceptFiles(TRUE);

上面这一句很重要,它使得这个窗口可以接受拖曳操作.
m_bmp.GetWindowRect(&Bmp);
m_jpg.GetWindowRect(&Jpg);
m_gif.GetWindowRect(&Gif);
m_png.GetWindowRect(&Png);
CRect rect;
GetClientRect(&rect);
ClientToScreen(&rect);
Bmp.top-=rect.top;
Bmp.left-=rect.left;
Bmp.bottom-=rect.top;
Bmp.right-=rect.left;
Jpg.top-=rect.top;
Jpg.left-=rect.left;
Jpg.bottom-=rect.top;
Jpg.right-=rect.left;
Gif.top-=rect.top;
Gif.left-=rect.left;
Gif.bottom-=rect.top;
Gif.right-=rect.left;
Png.top-=rect.top;
Png.left-=rect.left;
Png.bottom-=rect.top;
Png.right-=rect.left;
这一段代码主要是得到四个图标在窗口中的坐标.以便一会判断用户是要转成什么格式.
4.最关键的函数是OnDropFiles(HDROP hDropInfo)
它是一个系统的回调函数.当有鼠标把文件拖进来的时候,调用此函数.具体的不讲了,今天讲的是GDI+,关于拖曳的,以后再说.
下面给出这个函数的实现.还是,具体的看源码:
void CCBabyImageDlg::OnDropFiles(HDROP hDropInfo)
{
CPoint point;
::DragQueryPoint(hDropInfo,&point);

if (
(point.yBmp.top) &&
(point.x>Bmp.left) &&
(point.xJpg.top) &&
(point.x>Jpg.left) &&
(point.xGif.top) &&
(point.x>Gif.left) &&
(point.xPng.top) &&
(point.x>Png.left) &&
(point.x )
{
char szFilePathName[_MAX_PATH+1] = {0};
UINT nNumOfFiles = DragQueryFile(hDropInfo, 0xFFFFFFFF, NULL, 0); //得到文件个数
for (UINT nIndex=0 ; nIndex< nNumOfFiles; ++nIndex)
{
DragQueryFile(hDropInfo, nIndex, szFilePathName, _MAX_PATH); //得到文件名
Image im(ToWChar(szFilePathName));
int i,Temp;
for(i=0;i<_MAX_PATH;i++)
{
if (szFilePathName[i]=='.')
{
Temp = i;
}
if (szFilePathName[i]==0)
{
break;
}
}
szFilePathName[Temp+1] = 'p';
szFilePathName[Temp+2] = 'n';
szFilePathName[Temp+3] = 'g';
CLSID pngClsid;
GetEncoderClsid(L"image/png",&pngClsid);
im.Save(ToWChar(szFilePathName), &pngClsid, NULL);
}
}
DragFinish(hDropInfo);
}
到此,这个程序的核心功能已经完成了,还有一些具体的工作,比如程序框架什么的,读者自己看源码吧.
另外,还要说明,还有两个非常重要的函数,是别人的,现在也给出来.这些函数都是在使用GDI+的时候经常用到的.
(1).注意到上面代码中GetEncoderClsid(L"image/png",&pngClsid); 这句了.
这个函数的实现在下面:
BOOL CCBabyImageDlg::GetEncoderClsid(const WCHAR* format, CLSID* pCLSID)
{
UINT num = 0;
UINT size = 0;

ImageCodecInfo* pImageCodecInfo = NULL;
GetImageEncodersSize(&num, &size);
if(size == 0)
{
return FALSE;
}
pImageCodecInfo = (ImageCodecInfo *)(malloc(size));
if(pImageCodecInfo == NULL)
return FALSE;
GetImageEncoders(num, size, pImageCodecInfo);

// Find for the support of format for image in the windows
for(UINT i = 0; i < num; ++i)
{
//MimeType: Depiction for the program image
if( wcscmp(pImageCodecInfo[i].MimeType, format) == 0)
{
*pCLSID = pImageCodecInfo[i].Clsid;
free(pImageCodecInfo);
return TRUE;
}
}
free(pImageCodecInfo);
return FALSE;
}

这很有用,留着.

2.Image的Save函数的参数要WCHAR型的是UNICODE编码.而现在一般程序里用的是char,ASCII编码.下面这个函数是CSDN上看来的,它把char转到WCHAR.
WCHAR* CCBabyImageDlg::ToWChar(char * str)
{
static WCHAR buffer[1024];
wcsset(buffer,0);
MultiByteToWideChar(CP_ACP,0,str,strlen(str),buffer,1024);
return buffer;
}


核心的部分就是这些了.第一次写技术文章,各位见笑了.
说的不清楚,还是看源码吧.
也可以联系我,给我留言吧.

文章分类

这里是我学习编程中的一些心得体会,方法技巧等. 很高兴能与你分享这些. 我的联系方式:checkabc@gmail.com

访问计数