Mangiare Senza Glutine disponibile su App Store

Per altre informazioni scrivi a fabriziocaldarelli@negusweb.it

Shell Notify Icon con le MFC

Da Programmazione Software.

Descrizione

Vediamo in quest'articolo come aggiungere una notify icon (le icone in basso a destra nella barra delle applicazioni) alla nostra applicazione, cercando di scrivere del codice più pulito e riutilizzabile possibile. La stessa cosa è concretizzabile, a meno di piccole modifiche, ad applicazioni che sfruttano direttamente le API di Windows o comunque in presenza di framework diversi dalle MFC. L'IDE che ho utilizzato per gli esempi è Visual C++ del pacchetto Visual Studio 2005 Professional. Cominciamo con il creare un nuovo progetto, il più semplice possibile. Quindi un progetto Win32, vuoto. A questo punto creiamo una classe principale che chiameremo CMain con il codice di base per l'applicazione, ovvero:

CMain.h

class CMain : public CWinApp
{
public:
    virtual BOOL InitInstance ();
};
 
class CMainWindow : public CFrameWnd
{
public:
    CMainWindow ();
 
protected:
    DECLARE_MESSAGE_MAP ()
};

CMain.cpp

#include 
#include "CMain.h"
 
CMain mainApp;
 
/////////////////////////////////////////////////////////////////////////
// CMain member functions
 
BOOL CMain::InitInstance ()
{
    m_pMainWnd = new CMainWindow;
 
 
   m_pMainWnd->ShowWindow (m_nCmdShow);
    m_pMainWnd->UpdateWindow ();
    return TRUE;
}
 
/////////////////////////////////////////////////////////////////////////
// CMainWindow message map and member functions
 
BEGIN_MESSAGE_MAP (CMainWindow, CFrameWnd)
END_MESSAGE_MAP ()
 
CMainWindow::CMainWindow ()
{
    Create (NULL, _T ("Shell Notify Icon con MFC"));
}


Proviamo innanzitutto a compilare questo codice, ricordandoci di abilitare il supporto per le MFC nel nostro progetto. Nel caso di Visual Studio il percorso è il seguente:

Progetto -> Proprietà di ShellNotifyIconMFC -> Proprietà di configurazione -> Generale -> Uso di MFC -> utilizza in libreria statica

Per l'utilizzo delle notify icon bisogna far riferimento alla funzione API di Windows "Shell_NotifyIcon" che accetta 2 parametri: il primo è l'operazione che si vuole compiere sulla notify icon (aggiungere, modificare, cancellare) ed il secondo è una struttura di tipo NOTIFYICONDATA che contiene alcuni parametri riguardo gli stili e le caratteristiche della notify icon.

Quello che faremo ora è incapsulare la funzione "Shell_NotifyIcon" in una classe in modo da rendere più diretto e semplice l'utilizzo delle notify icon.

A questo proposito avremo la seguente classe, CNotifyIcon:

CNotifyIcon.h

#include 
 
class CNotifyIcon
{
private:
	PNOTIFYICONDATA pNID;
 
public:
 
	// Metodi
	bool Add();
	bool Delete();
	bool Modify();
	PNOTIFYICONDATA getPNID() { return this->pNID; };
 
	//Costruttori
	CNotifyIcon(HWND hWnd, UINT uID, UINT callbackMessage, LPCTSTR lpszPathIcona, LPCTSTR lpszTip);
	~CNotifyIcon();
};

CNotifyIcon.cpp

#include 
#include "CNotifyIcon.h"
 
#ifdef _DEBUG
#define new DEBUG_NEW
#endif
 
CNotifyIcon::CNotifyIcon(HWND hWnd, UINT uID, UINT callbackMessage, LPCTSTR lpszPathIcona, LPCTSTR lpszTip)
{
	HRESULT hr;
 
	pNID = new NOTIFYICONDATA();
	pNID->cbSize = sizeof(NOTIFYICONDATA);
	pNID->hWnd = hWnd;
	pNID->uID = uID; 
	pNID->uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP;
 
	hr = StringCchCopy(pNID->szTip, ARRAYSIZE(pNID->szTip), lpszTip);
 
	pNID->hIcon = (HICON) LoadImage(GetModuleHandle(NULL),lpszPathIcona,IMAGE_ICON,0,0,LR_LOADFROMFILE);
	pNID->uCallbackMessage = callbackMessage;
}
 
bool CNotifyIcon::Add()
{
	bool retVal;
 
	retVal = Shell_NotifyIcon(NIM_ADD,this->pNID);
 
	return retVal;
}
 
bool CNotifyIcon::Modify()
{
	bool retVal;
 
	retVal = Shell_NotifyIcon(NIM_MODIFY,this->pNID);
 
	return retVal;
}
 
bool CNotifyIcon::Delete()
{
	bool retVal;
 
	retVal = Shell_NotifyIcon(NIM_DELETE,this->pNID);
 
	return retVal;
}
 
CNotifyIcon::~CNotifyIcon()
{
	delete pNID;
}

Cominciamo dal costruttore CNotifyIcon(HWND hWnd, UINT uID, UINT callbackMessage, LPCTSTR lpszPathIcona, LPCTSTR lpszTip) che accetta 5 parametri:

1) hWND: Handle della finestra che riceverà i messaggi di notifiche dalla notifyicon (click del mouse, ecc.); 2) uID: Id da associare alla notifyicon; 3) callbackMessage: identificativo per distinguere i messaggi provenienti dalla notifyicon indirizzati alla finestra identificata con l'handle hWND; 4) pathIcon: percorso del file dell'icona da visualizzare; 5) tip: tooltip da visualizzare quando il puntatore del mouse si trova sopra l'icona;

La classe espone inoltre 4 metodi: Add, Modify, Delete, getPNID. Il primo molto semplicemente aggiunge la notifyicon in base alla struttura notifyicon passata, il secondo rivisualizza la notifyicon rileggendo la struttura notifyicon passata, il terzo elimina la notifyicon dalla taskbar e l'ultimo è il getter della struttura notifyicon incapsulata nella classe. A questo punto procuriamoci un'icona che inseriremo nella stessa cartella del nostro progetto e che chiamaremo "icona.ico" e creaimo un'istanza della classe CNotifyIcon nella funzione InitInstance della classe CMain:

CNotifyIcon *notifyIcon = new CNotifyIcon(m_pMainWnd->m_hWnd, 101, WM_USER+1,_T("icona.ico"), _T("Tooltip di ShellNotifyIconMFC")); notifyIcon->Add();


Sarà bene dare al secondo parametro del costruttore di CNotifyIcon (101, per intenderci) un'associazione con un'ID, così come al terzo, in modo da poter cambiare e lavorare agevolmente questi parametri. Allora creiamo un file chiamato ad esempio "risorse.h" con il richiamo a tutte le risorse del programma e definiamo le due costanti:

risorse.h

#define ID_NOTIFYICON 101
#define ID_MESSAGEMAP_NOTIFYICON WM_USER+1


A questo punto includiamo il file "risorse.h" anche nel file CMain.h e modifichiamo le due righe precedenti in questo modo:

CNotifyIcon *notifyIcon = new CNotifyIcon(m_pMainWnd->m_hWnd, ID_NOTIFYICON, WM_NOTIFYICON,_T("icona.ico"), _T("Tooltip di ShellNotifyIconMFC"));
notifyIcon->Add();


Ora se compilliamo e lanciamo il nostro programma, dovreste vedere la finestra del programma e l'icona nella taskbar. Se passate sopra l'icona con il mouse vedrete anche il messaggio tooltip che avete impostato. Ora passiamo alla gestione degli messaggi passati dalla notifyicon, per gestire eventuali click del mouse, per esempio. Tutto quello che dobbiamo fare ora è inserire nella MESSAGE_MAP della nostra applicazione (nel file CMain.cpp) la gestione del messaggio WM_NOTIFYICON, la visualizzazione di un menù di popup nel caso in cui l'utente clicchi con il tasto destro del mouse sulla notifyicon ed infine gestire gli eventi del menù popup. Per il menù popup prevediamo due voci: la prima la chiamiamo "nessun evento" che cliccandoci non fa nulla (da considerare come punto di riferimento per l'aggiunta di altre voci nel menù) e la secondi è "esci", per terminare l'applicazione. Aggiungiamo alla MESSAGE_MAP la gestione di questi tre eventi, il primo derivante dalla notifyicon e gli ultimi due dalla gestione degli eventi del menù popup che apriremo quando si cliccherà con il tasto destro del mouse sull'icona.

La MESSAGE_MAP nel CMain.cpp diventerà:

BEGIN_MESSAGE_MAP (CMainWindow, CFrameWnd)
	ON_MESSAGE (WM_NOTIFYICON, OnShellNotifyIcon)
	ON_COMMAND (IDM_MENUPOPUP_ESCI, OnMenuPopupEsci)
	ON_COMMAND (IDM_MENUPOPUP_NESSUNEVENTO, OnMenuPopupNessunEvento)
END_MESSAGE_MAP ()


Ricordiamoci di aggiungere il valore di IDM_MENUPOPUP_ESCI e IDM_MENUPOPUP_NESSUNEVENTO nel file risorse.h, in questo modo:

#define IDM_MENUPOPUP_ESCI 102
#define IDM_MENUPOPUP_NESSUNEVENTO 103

A questo punto definiamo nella CMainWindow i tre metodi che saranno i gestori di questi 3 eventi: OnShellNotifyIcon(), OnMenuPopupEsci() e OnMenuPopupNessunEvento(), con queste firme:

protected:
	afx_msg LRESULT OnShellNotifyIcon(WPARAM wParam, LPARAM lParam );
	afx_msg void OnMenuPopupEsci();
	afx_msg void OnMenuPopupNessunEvento();

Definiamo adesso il corpo di queste tre funzioni nel file CMain.cpp:

LRESULT CMainWindow::OnShellNotifyIcon(WPARAM wParam, LPARAM lParam )
{
	CMenu menu;
	menu.CreatePopupMenu();
	menu.AppendMenu (MF_STRING, IDM_MENUPOPUP_NESSUNEVENTO, _T("Nessun Evento"));
	menu.AppendMenu (MF_SEPARATOR, 0, _T(""));
	menu.AppendMenu (MF_STRING, IDM_MENUPOPUP_ESCI, _T("Esci"));
 
	switch(lParam)
	{
	case WM_LBUTTONDOWN:
		break;
	case WM_RBUTTONDOWN:
		POINT point;
		GetCursorPos(&point);
		menu.TrackPopupMenu(TPM_LEFTALIGN | TPM_LEFTBUTTON |TPM_RIGHTBUTTON, point.x, point.y, this);
		break;
	case WM_LBUTTONDBLCLK:
		break;
	}
 
	return 0;	
}
void CMainWindow::OnMenuPopupEsci()
{
        this->SendMessage(WM_CLOSE);
}
void CMainWindow::OnMenuPopupNessunEvento()
{
}


Gli ultimi due metodi sono la gestioni degli eventi sul menù popup che uscirà quando cliccheremo con il tasto destro sull'icona; il metodo OnMenuPopupEsci() termina l'applicazione invece OnMenuPopupNessunEvento(), come dice la firma stessa, non fa nulla. Il metodo OnShellNotifyIcon() si occupa di impostare le voci del menù, posizionare e visualizzare il menù nella locazione del mouse quando questo clicca con il tasto destro sull'icona. Ora che la nostra applicazione è completa dobbiamo preoccuparci di togliere l'icona e cancellare tutti gli oggetti istanziati alla chiusura del programma. A questo proposito spostiamo l'istanza della classe CNotifyIcon dalla CMain::InitInstance come attributo della classe CMain. Aggiungiamo quindi l'attributo

private:
        CNotifyIcon *notifyIcon;


alla classe CMain. Quindi nel metodo CMain::InitInstance avremo:

this->notifyIcon = new CNotifyIcon(m_pMainWnd->m_hWnd, ID_NOTIFYICON, WM_NOTIFYICON,_T("icona.ico"), _T("Tooltip di ShellNotifyIconMFC"));
this->notifyIcon->Add();

A questo punto definiamo il distruttore di CMain::~CMain in questo modo:

CMain::~CMain()
{
            this->notifyIcon->Delete();
            delete this->notifyIcon;
}

Ora il programma è davvero completo con la gestione anche della chiusura dell'applicazione. Abbiamo finora utilizzato i metodi Add e Delete di CNotifyIcon. Per mostrarvi l'uso di Modify vi parlo della possibilità di impostare delle tooltip della notifyicon con un titolo, un testo ed una piccola icona che rende questa tooltip molto carina e professionale. Imposteremo a questo proposito il flag NIF_INFO della struttura NOTIFYICONDATA in un metodo di CNotifyIcon che potremo chiamare "ScriviBaloon" proprio perchè si tratta di una tooltip simile alla nuvoletta dei fumetti. Il metodo CNotifyIcon::ScriviBaloon può essere definito come segue:

void CNotifyIcon::ScriviBaloon(TCHAR *titolo, TCHAR *testo)
{
	HRESULT hr;
 
	this->pNID->uFlags = NIF_INFO;
 
	hr = StringCchCopy(this->pNID->szInfoTitle, ARRAYSIZE(this->pNID->szInfoTitle), titolo);
	hr = StringCchCopy(this->pNID->szInfo, ARRAYSIZE(this->pNID->szInfo), testo);
	this->pNID->uTimeout = 10000; // in milliseconds
	this->Modify();
 
}

Visualizzeremo questo baloon quanto l'utente cliccherà con il tasto sinistro sulla notify icon. A questo punto basta modificare il metodo OnShellNotifyIcon come segue:

case WM_LBUTTONDOWN:
     mainApp.getNotifyIcon()->ScriviBaloon(_T("titolo della tooltup"),_T("testo della tooltip"));
     break;

Aggiungendo questo codice l'applicazione non compilerà perchè non abbiamo definito il metodo CMain::getNotifyIcon() che è un semplice getter della variabile CMain::notifyIcon. Bene, a questo punto è davvero tutto. Vi lascio ristampandovi tutti i sorgenti dell'applicazione ed alla fine un pacchetto .zip con tutto il materiale qui illustrato. Per qualsiasi domanda, chiarimento sono a vostra disposizione.

risorse.h

#define ID_NOTIFYICON 101
#define WM_NOTIFYICON WM_USER+1
#define IDM_MENUPOPUP_ESCI 102
#define IDM_MENUPOPUP_NESSUNEVENTO 103


CMain.h

#include 
#include "risorse.h"
#include "CNotifyIcon.h"
 
 
class CMain : public CWinApp
{
private:
	CNotifyIcon *notifyIcon;
public:
	~CMain();
	CNotifyIcon* getNotifyIcon() { return this->notifyIcon; }
    virtual BOOL InitInstance ();
};
 
class CMainWindow : public CFrameWnd
{
public:
    CMainWindow ();
 
protected:
	afx_msg LRESULT OnShellNotifyIcon(WPARAM wParam, LPARAM lParam );
	afx_msg void OnMenuPopupEsci();
	afx_msg void OnMenuPopupNessunEvento();
    DECLARE_MESSAGE_MAP ()
};


CNotifyIcon.h

#include 
 
class CNotifyIcon
{
private:
	PNOTIFYICONDATA pNID;
 
public:
 
	// Metodi
	bool Add();
	bool Delete();
	bool Modify();
	PNOTIFYICONDATA getPNID() { return this->pNID; };
	void ScriviBaloon(TCHAR *titolo, TCHAR *testo);
 
	//Costruttori
	CNotifyIcon(HWND hWnd, UINT uID, UINT callbackMessage, LPCTSTR lpszPathIcona, LPCTSTR lpszTip);
	~CNotifyIcon();
};


CMain.cpp

#include "CMain.h"
 
CMain mainApp;
 
/////////////////////////////////////////////////////////////////////////
// CMain member functions
 
BOOL CMain::InitInstance ()
{
    m_pMainWnd = new CMainWindow;
 
	this->notifyIcon = new CNotifyIcon(m_pMainWnd->m_hWnd, ID_NOTIFYICON, WM_NOTIFYICON,_T("icona.ico"), _T("Tooltip di ShellNotifyIconMFC"));
	this->notifyIcon->Add();
 
    m_pMainWnd->ShowWindow (m_nCmdShow);
    m_pMainWnd->UpdateWindow ();
    return TRUE;
}
 
/////////////////////////////////////////////////////////////////////////
// CMainWindow message map and member functions
 
BEGIN_MESSAGE_MAP (CMainWindow, CFrameWnd)
	ON_MESSAGE (WM_NOTIFYICON, OnShellNotifyIcon)
	ON_COMMAND (IDM_MENUPOPUP_ESCI, OnMenuPopupEsci)
	ON_COMMAND (IDM_MENUPOPUP_NESSUNEVENTO, OnMenuPopupNessunEvento)
END_MESSAGE_MAP ()
 
CMainWindow::CMainWindow ()
{
    Create (NULL, _T ("Shell Notify Icon con MFC"));
}
 
LRESULT CMainWindow::OnShellNotifyIcon(WPARAM wParam, LPARAM lParam )
{
	CMenu menu;
	menu.CreatePopupMenu();
	menu.AppendMenu (MF_STRING, IDM_MENUPOPUP_NESSUNEVENTO, _T("Nessun Evento"));
	menu.AppendMenu (MF_SEPARATOR, 0, _T(""));
	menu.AppendMenu (MF_STRING, IDM_MENUPOPUP_ESCI, _T("Esci"));
 
	switch(lParam)
	{
	case WM_LBUTTONDOWN:
		mainApp.getNotifyIcon()->ScriviBaloon(_T("titolo della tooltup"),_T("testo della tooltip"));
		break;
	case WM_RBUTTONDOWN:
		POINT point;
		GetCursorPos(&point);
		menu.TrackPopupMenu(TPM_LEFTALIGN | TPM_LEFTBUTTON |TPM_RIGHTBUTTON, point.x, point.y, this);
		break;
	case WM_LBUTTONDBLCLK:
		break;
	}
 
	return 0;	
}
void CMainWindow::OnMenuPopupEsci()
{
	this->SendMessage(WM_CLOSE);
}
void CMainWindow::OnMenuPopupNessunEvento()
{
}
CMain::~CMain()
{
	this->notifyIcon->Delete();
	delete this->notifyIcon;
}


CNotifyIcon.cpp

#include 
#include "CNotifyIcon.h"
 
#ifdef _DEBUG
#define new DEBUG_NEW
#endif
 
CNotifyIcon::CNotifyIcon(HWND hWnd, UINT uID, UINT callbackMessage, LPCTSTR lpszPathIcona, LPCTSTR lpszTip)
{
	HRESULT hr;
 
	pNID = new NOTIFYICONDATA();
	pNID->cbSize = sizeof(NOTIFYICONDATA);
	pNID->hWnd = hWnd;
	pNID->uID = uID; // icon is changed when UI is visible
	pNID->uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP;
 
	hr = StringCchCopy(pNID->szTip, ARRAYSIZE(pNID->szTip), lpszTip);
	pNID->hIcon = (HICON) LoadImage(GetModuleHandle(NULL),lpszPathIcona,IMAGE_ICON,0,0,LR_LOADFROMFILE);
	pNID->uCallbackMessage = callbackMessage;
}
 
bool CNotifyIcon::Add()
{
	bool retVal;
 
	retVal = Shell_NotifyIcon(NIM_ADD,this->pNID);
 
	return retVal;
}
 
bool CNotifyIcon::Modify()
{
	bool retVal;
 
	retVal = Shell_NotifyIcon(NIM_MODIFY,this->pNID);
 
	return retVal;
}
 
bool CNotifyIcon::Delete()
{
	bool retVal;
 
	retVal = Shell_NotifyIcon(NIM_DELETE,this->pNID);
 
	return retVal;
}
 
CNotifyIcon::~CNotifyIcon()
{
	delete pNID;
}
 
void CNotifyIcon::ScriviBaloon(TCHAR *titolo, TCHAR *testo)
{
	HRESULT hr;
 
	this->pNID->uFlags = NIF_INFO;
 
	hr = StringCchCopy(this->pNID->szInfoTitle, ARRAYSIZE(this->pNID->szInfoTitle), titolo);
	hr = StringCchCopy(this->pNID->szInfo, ARRAYSIZE(this->pNID->szInfo), testo);
	this->pNID->uTimeout = 10000; // in milliseconds
	this->Modify();
 
}


Allegati