代码改变世界

MFC中托盘(TRAYICON)实现

2011-11-12 20:42  捣乱小子  阅读(3495)  评论(0编辑  收藏  举报

写在最前面的

将MFC中托盘功能模块抽象成一个类,把整个程序的功能模块细分了。这个想法在网上已经是俯拾即是了,但仍旧不能一下子明白其中的东西,特别是将其抽象之后。 在使用这个类的时候,需要注意:托盘菜单的ID要和图标资源的ID一样,否则会出错。

具体实现代码 

添加新的类,选择父类是CCmdTarget,下面的代码中有足够的提醒:

TrayIcon.h  

#pragma once

// TrayIcon.h : 头文件
//
//    继承自CCmdTarget才能接收消息,详见《深入浅出 MFC》
class CTrayIcon : public CCmdTarget 

    DECLARE_DYNAMIC(CTrayIcon)

public
    CTrayIcon(UINT uID); 
     ~CTrayIcon(); 
     
    // Call this to receive tray notifications 
    void SetNotificationWnd(CWnd* pNotifyWnd, UINT uCbMsg);

    // SetIcon functions. To remove icon, call SetIcon(0) 
    
//只安装图盘图标 
    BOOL SetIcon(UINT uID,LPWSTR lpTip=NULL);

    //安装托盘图标和设置提示信息 
    BOOL SetIcon(HICON hIcon, LPWSTR lpTip);

    //安装托盘图标和设置提示信息 
    BOOL SetIcon(LPCTSTR lpResName, LPWSTR lpTip) 
    { 
        return SetIcon(lpResName ?  
            AfxGetApp()->LoadIcon(lpResName) : NULL, lpTip); 
    }

    /* 
    BOOL SetStandardIcon(LPCTSTR lpszIconName, LPCSTR lpTip) 
    { 
        return SetIcon(::LoadIcon(NULL, lpszIconName), lpTip); 
    } 
    
*/

    virtual LRESULT OnTrayNotification(WPARAM uID, LPARAM lEvent);

    /*
    BOOL ShowBalloonTip();
    笔者没有实现泡泡其实,如果有想法的可以留言
    
*/
protected
    NOTIFYICONDATA m_nid; 
    DECLARE_MESSAGE_MAP() 
};

 

TrayIcon.cpp 

// TrayIcon.cpp : 实现文件 
//
#include "stdafx.h" 
#include "TrayIconShell.h" 
#include "TrayIcon.h"

/* 
注意:托盘菜单的ID要和图标资源的ID一样,否则会出错!
*/
IMPLEMENT_DYNAMIC(CTrayIcon, CCmdTarget)

CTrayIcon::CTrayIcon(UINT uID) 

    ::memset(&m_nid,0,sizeof(m_nid)); 
    m_nid.uID = uID; 
    m_nid.cbSize = sizeof(NOTIFYICONDATA); 
}

CTrayIcon::~CTrayIcon() 

    this->SetIcon(0); 
}

void CTrayIcon::SetNotificationWnd(CWnd* pNotifyWnd, UINT uCbMsg) 

    //断言,正确才中断 
    ASSERT(pNotifyWnd==NULL 
        ||::IsWindow(pNotifyWnd->GetSafeHwnd()));

    m_nid.hWnd = pNotifyWnd->m_hWnd;

    ASSERT(uCbMsg == 0||uCbMsg>=WM_USER);

    m_nid.uCallbackMessage = uCbMsg; 
}

BOOL CTrayIcon::SetIcon(UINT uID,LPWSTR lpTip) 

    HICON hIcon = NULL; 
    if(uID) 
    { 
        m_nid.uID = uID;//更改当前对象的uID 
        hIcon = AfxGetApp()->LoadIconW(uID); 
    } 
    return SetIcon(hIcon,lpTip); 
}

BOOL CTrayIcon::SetIcon(HICON hIcon, LPWSTR lpTip) 

    UINT uMsg; 
    m_nid.uFlags = 0;

    if(hIcon) 
    { 
        uMsg = m_nid.hIcon?NIM_MODIFY:NIM_ADD;//当前如果存在ICON,就修改 
        m_nid.hIcon = hIcon; 
        m_nid.uFlags = NIF_ICON;//设置标志

        
//如果存在hIcon才进行设置提示 
        if(lpTip) 
        { 
            ::lstrcpyn(m_nid.szTip,(LPCWSTR)lpTip,sizeof(m_nid.szTip)); 
        } 
        if(m_nid.szTip[0]) 
            m_nid.uFlags |= NIF_TIP;//设置标志 
    } 
    else//删除图标 
    { 
        //如果当前m_nid的hIcon为null,那么直接结束,啥都不用干啦 
        if(m_nid.hIcon==NULL)    return true;
        uMsg = NIM_DELETE;    //如果当前m_nid的hIcon不为null,删除图标 
        m_nid.hIcon = NULL; 
    }

    if(m_nid.uCallbackMessage && m_nid.hWnd) 
        m_nid.uFlags |= NIF_MESSAGE;

    BOOL bRet = ::Shell_NotifyIcon(uMsg,&m_nid);

    //把失败和NIM_DELETE的操作放在这里 
    if(!bRet)    m_nid.hIcon = NULL;
    return bRet; 


LRESULT CTrayIcon::OnTrayNotification(WPARAM uID, LPARAM lEvent) 

    if(uID != m_nid.uID ||        //如果uID不属于此对象 
        (lEvent != WM_RBUTTONUP && lEvent != WM_LBUTTONDBLCLK)) 
        return 0;

    CMenu menu; //知道吗,MFC的封装我觉得在这里已经表现得淋漓尽致了。
    if(!menu.LoadMenuW(m_nid.uID)) 
        return 0;

    CMenu * pSubMenu = menu.GetSubMenu(0);

    if(!pSubMenu) 
        return 0;

    if(lEvent == WM_RBUTTONUP) 
    { 
        CPoint mouse; 
        ::GetCursorPos(&mouse); 
        ::SetForegroundWindow(m_nid.hWnd); 
        ::TrackPopupMenu(pSubMenu->m_hMenu,TRUE,mouse.x,mouse.y,0
            m_nid.hWnd,NULL); 
    } 
    if(lEvent == WM_LBUTTONDBLCLK) 
    { 
        ::SendMessage(m_nid.hWnd,WM_COMMAND, 
            pSubMenu->GetMenuItemID(0),0); 
    } 
    return 1
}

BEGIN_MESSAGE_MAP(CTrayIcon, CCmdTarget) 
END_MESSAGE_MAP()

使用方法 

由于这个类的父类是CCmdTarget,所以,可以接受命令消息(菜单的消息),我们可以自定义添加消息。 

在需要使用“托盘”的窗口类中声明一个CTrayIcon的对象,然后在OnCreate函数当中调用即可。

int CTrayIconShellDlg::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
    if (CDialog::OnCreate(lpCreateStruct) == -1)
        return -1;

    // TODO:  在此添加您专用的创建代码
    TCHAR tip[100] = TEXT("捣乱小子");
    TrayIcon.SetNotificationWnd(this,WM_NOTIFY_MSG);
    TrayIcon.SetIcon(IDR_MAINFRAME,tip);
    return 0;
}

 总结 

  在这一次实验之后,我更加仰慕和崇尚面向对象(OO)的思想,如果编程思路清晰,数据设计得当,接口功能完善,以及接口与接口之间有良好的信息沟通,OO能让你在程序设计当中游刃有余,所以在平时的时候要养成面向对象的思考习惯。

  在C++面向对象编程的过程当中,会遇到一些公共的接口,这些接口只完成某些固定的操作,与其他的接口的联系不是非常的紧密的时候,我们可以将此接口设计为static模式,这样就不会让每个对象都担负着那么多的方法了。

 

本文完 Monday, April 23, 2012(更新)

捣乱小子 http://www.cnblogs.com/daoluanxiaozi/