任务栏右下角弹出气球式消息
在任务栏右下角弹出气球式消息,指定时间后消失:
void CTrayDemoDlg::ShowBalloon(CString szText)
{
CTBalloon * pballoon = new CTBalloon(150,100);
pballoon->SetText(szText);//文字
pballoon->SetLifeTime(4); //停留时间(秒)
pballoon->CreateAndShow();
}
TBalloon.h
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
TBalloon.h
#if !defined(AFX_TBALLOON_H__8363E22A_9845_4086_B3A0_40117DCFDB1F__INCLUDED_)
#define AFX_TBALLOON_H__8363E22A_9845_4086_B3A0_40117DCFDB1F__INCLUDED_
#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
///////////////////////////////////////////////////////////////////////////////
// class CTBalloon
/////////////////////////////////////////////////////////////////////////////
// CTBalloon window
#include "Gradient.h"
class CTBalloon : public CWnd
{
// Construction
public:
CTBalloon(UINT nWidth, UINT nHeight);
BOOL CreateAndShow();
void SetText(CString str);
void SetLifeTime(UINT secs);
static UINT m_sActiveCount;
// Attributes
private:
CRect m_current_rect;
CRect m_screen_rect;
UINT m_nWidth;
UINT m_nHeight;
UINT m_totaltime;
UINT m_lifetime;
BOOL m_dir;
CString m_text;
CGradient m_grad;
// Overrides
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CTBalloon)
public:
protected:
virtual void PostNcDestroy();
//}}AFX_VIRTUAL
// Implementation
public:
virtual ~CTBalloon();
// Generated message map functions
protected:
//{{AFX_MSG(CTBalloon)
afx_msg void OnPaint();
afx_msg void OnTimer(UINT nIDEvent);
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};
/////////////////////////////////////////////////////////////////////////////
//{{AFX_INSERT_LOCATION}}
// Microsoft Visual C++ will insert additional declarations immediately before the previous line.
#endif // !defined(AFX_TBALLOON_H__8363E22A_9845_4086_B3A0_40117DCFDB1F__INCLUDED_)
TBalloon.cpp
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
TBalloon.cpp
///////////////////////////////////////////////////////////////////////////////
// class CTBalloon
#include "stdafx.h"
//#include "TrayBalloon.h"
#include "TBalloon.h"
#include "Gradient.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
/////////////////////////////////////////////////////////////////////////////
// CTBalloon
#define BALLOON_SHOW_TIMER 0x10
#define TIMER_MS 20
#define BALLOON_LIFETIME (3*1000)
#define STEP_SIZE 5
UINT CTBalloon::m_sActiveCount;
CTBalloon::CTBalloon(UINT nWidth, UINT nHeight): m_nWidth(nWidth), m_nHeight(nHeight)
{
SystemParametersInfo(SPI_GETWORKAREA, 0, &m_screen_rect, 0);
m_totaltime = 0;
m_lifetime = BALLOON_LIFETIME;
m_dir = TRUE;
}
CTBalloon::~CTBalloon()
{
}
BEGIN_MESSAGE_MAP(CTBalloon, CWnd)
//{{AFX_MSG_MAP(CTBalloon)
ON_WM_PAINT()
ON_WM_TIMER()
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
void CTBalloon::SetText(CString str)
{
m_text =str;
}
void CTBalloon::SetLifeTime(UINT secs)
{
m_lifetime = secs * 1000;
}
BOOL CTBalloon::CreateAndShow()
{
const wchar_t * p = AfxRegisterWndClass(CS_HREDRAW | CS_VREDRAW );
CRect rect;
rect.top = m_screen_rect.bottom - m_nHeight;
rect.bottom = m_screen_rect.bottom;
rect.left = m_screen_rect.right - m_nWidth - 10 - (m_sActiveCount * 10) ;
rect.right = m_screen_rect.right - 10 - (m_sActiveCount * 10);
DWORD dwStyle = WS_POPUP;
DWORD dwExStyle = WS_EX_TOOLWINDOW |WS_EX_CLIENTEDGE | WS_EX_TOPMOST | WS_EX_WINDOWEDGE ;
BOOL ret = CreateEx(dwExStyle,p,NULL,dwStyle,rect,NULL,NULL);
m_sActiveCount++;
m_current_rect.top = m_current_rect.bottom = m_screen_rect.bottom;
m_current_rect.left = rect.left;
m_current_rect.right = rect.right;
COLORREF lightblue = RGB(154,190,255);
COLORREF white = RGB(255,255,255);
m_grad.SetDirection(CGradient::RTL);
m_grad.SetGradientColorsX(3,lightblue,lightblue,white);
SetTimer(BALLOON_SHOW_TIMER,TIMER_MS,NULL);
return ret;
}
/////////////////////////////////////////////////////////////////////////////
// CTBalloon message handlers
void CTBalloon::OnPaint()
{
CPaintDC dc(this); // device context for painting
CBrush br(0xFF0000);
CRect rect(0,0,m_current_rect.Width(),m_current_rect.Height());
m_grad.DrawLinearGradient(&dc,rect);
dc.SelectStockObject(ANSI_VAR_FONT);
dc.SetBkMode(TRANSPARENT);
CRect temp = rect;
UINT height = dc.DrawText(m_text,-1,temp,DT_CENTER|DT_WORDBREAK|DT_CALCRECT);
rect.top = (rect.Height() - height)/2;
dc.DrawText(m_text,-1,rect,DT_CENTER|DT_WORDBREAK);
// Do not call CWnd::OnPaint() for painting messages
}
void CTBalloon::OnTimer(UINT nIDEvent)
{
// TODO: Add your message handler code here and/or call default
m_totaltime += TIMER_MS;
if (m_totaltime > m_lifetime)
{
m_dir = FALSE;
}
if (m_dir)
{
m_current_rect.top = ((m_current_rect.top - STEP_SIZE) < (m_current_rect.bottom - (LONG)m_nHeight))? m_current_rect.bottom - m_nHeight : m_current_rect.top - STEP_SIZE;
}
else
{
m_current_rect.top = ((m_current_rect.top + STEP_SIZE) > (m_current_rect.bottom))? m_current_rect.bottom : m_current_rect.top + STEP_SIZE;
}
MoveWindow(&m_current_rect);
if (m_current_rect.top == m_current_rect.bottom)
{
KillTimer(BALLOON_SHOW_TIMER);
DestroyWindow();
return;
}
CWnd * pWnd = GetFocus();
ShowWindow(SW_RESTORE);
if (pWnd)
pWnd->SetFocus();
CWnd::OnTimer(nIDEvent);
}
void CTBalloon::PostNcDestroy()
{
// TODO: Add your specialized code here and/or call the base class
delete this;
m_sActiveCount--;
}
Gradient.h
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Gradient.h
#if !defined(AFX_GRADIENT_H__2FB4902E_E00C_4892_AA03_60779F349F58__INCLUDED_)
#define AFX_GRADIENT_H__2FB4902E_E00C_4892_AA03_60779F349F58__INCLUDED_
#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
///////////////////////////////////////////////////////////////////////////////
// class CGradient
//
#include <afxtempl.h>
#ifndef UIBITS_API
#ifdef UIBITS_DLL
#define UIBITS_API __declspec(dllexport)
#else
#define UIBITS_API __declspec(dllimport)
#endif
#endif
struct CGradElement
{
CGradElement(COLORREF color = 0, int length = 0){this->color = color; this->length = length;}
COLORREF color;
int length;
};
typedef CArray<CGradElement, CGradElement&> CGradArray;
class /*UIBITS_API*/ CGradient
{
// Construction
public:
CGradient();
virtual ~CGradient();
// Attributes
public:
// linear gradient attributes
enum eDirection{LTR, RTL, TTB, BTT};
void SetDirection(eDirection fDirection){if(m_fDirection != fDirection){m_fDirection = fDirection;}}
eDirection GetDirection() {return m_fDirection;}
void SetStretchGradient(float flStretchFactor = 1); // useful for animation
float GetStretchGradient() {return m_flStretchGrad;}
// linear gradient operations
virtual void DrawLinearGradient(CDC *pDC, CRect rcGrad, int nClipStart = 0, int nClipEnd = -1, int nShift = 0);
// attributes
void SetGradientColors(COLORREF clrStart, COLORREF clrEnd) { SetGradientColorsX(2, clrStart, clrEnd);}
void GetGradientColors(COLORREF& clrStart, COLORREF& clrEnd);
void SetGradientColorsX(int nCount, COLORREF clrFirst, COLORREF clrNext, ...);
const CDWordArray& GetGradientColorsX() { return m_ardwGradColors; }
void AddColor(COLORREF clr);
void SetColorsStretch(double flFirst, ...); // in percent, num of arguments should be one less then num of colors
void SetCreatePalette(BOOL fCreate = TRUE) {m_fCreatePalette = fCreate;}
BOOL GetCreatePalette() { return m_fCreatePalette;}
CPalette& GetPalette() {CreatePalette(); return m_Pal;}
// operations
virtual void CalcShiftedGradient(CGradArray& arElements, int nShift, int nGradWidth, int nClipStart = 0, int nClipEnd = -1, UINT nMaxColors = (UINT)-1);
virtual void CalcMultiGradient(CGradArray& arElements, int nGradWidth, int nClipStart = 0, int nClipEnd = -1, UINT nMaxColors = (UINT)-1);
virtual void CalcGradient(CGradArray& arElements, COLORREF clrStart, COLORREF clrEnd, int nGradWidth, int nClipStart = 0, int nClipEnd = -1, UINT nMaxColors = (UINT)-1);
protected:
void CreatePalette();
void NormalizeColorsStretch();
// color atributes
CDWordArray m_ardwGradColors;
BOOL m_fCreatePalette;
CPalette m_Pal;
private:
CArray<double, double&> m_arflGradStretch;
protected:
// linear gradient
eDirection m_fDirection;
CRect ConvertToReal(CRect rcDraw, int nBandStart, int nBandEnd);
float m_flStretchGrad;
};
#endif // !defined(AFX_GRADIENT_H__2FB4902E_E00C_4892_AA03_60779F349F58__INCLUDED_)
Gradient.cpp
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Gradient.cpp
///////////////////////////////////////////////////////////////////////////////
// class CGradient
//
#include "stdafx.h"
#include "Gradient.h"
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
CGradient::CGradient()
{
m_fCreatePalette = FALSE;
m_fDirection = LTR;
m_flStretchGrad = 1;
}
CGradient::~CGradient()
{
}
void CGradient::DrawLinearGradient(CDC *pDC, CRect rcGrad, int nClipStart, int nClipEnd, int nShift)
{
BOOL f256Color = pDC->GetDeviceCaps(RASTERCAPS) & RC_PALETTE;
if(!f256Color && (pDC->GetDeviceCaps(BITSPIXEL)*pDC->GetDeviceCaps(PLANES) < 8))
{
// for 16 colors no gradient
ASSERT(m_ardwGradColors.GetSize() > 0);
pDC->FillSolidRect(&ConvertToReal(rcGrad, nClipStart, nClipEnd), m_ardwGradColors[0]);
return;
}
int nGradWidth = 0;
if(m_fDirection == TTB || m_fDirection == BTT)
nGradWidth = rcGrad.Height(); // vert
else
nGradWidth = rcGrad.Width(); // horz
if(nClipEnd == -1) nClipEnd = nGradWidth;
ASSERT(nGradWidth >= nClipEnd);
ASSERT(m_flStretchGrad >= 1);
nGradWidth = int(nGradWidth * m_flStretchGrad);
CGradArray arElements;
CalcShiftedGradient(arElements, nShift, nGradWidth, nClipStart, nClipEnd, f256Color ? 236 : -1);
int nSteps = arElements.GetSize();
int nBandStart = nClipStart;
int nBandEnd = nClipStart;
// Start filling
CPalette *pOldPal = NULL;
if (f256Color && m_fCreatePalette && GetPalette().GetSafeHandle())
{
pOldPal = pDC->SelectPalette(&GetPalette(), FALSE);
pDC->RealizePalette();
}
for (int i = 0; i < nSteps; i++, nBandStart = nBandEnd)
{
nBandEnd += arElements[i].length;
COLORREF nColor = arElements[i].color;
if(f256Color)
{
if (pOldPal) // in background draw dithered
nColor |= 0x02000000; // (PALETTERGB) without it will be dithering
CBrush br(nColor);
// CDC::FillSolidRect is faster, but it does not handle 8-bit color depth
pDC->FillRect(&ConvertToReal(rcGrad, nBandStart, nBandEnd), &br);
br.DeleteObject();
}
else
pDC->FillSolidRect(&ConvertToReal(rcGrad, nBandStart, nBandEnd), nColor);
}
if(pOldPal)
pDC->SelectPalette(pOldPal, TRUE);
}
CRect CGradient::ConvertToReal(CRect rcDraw, int nBandStart, int nBandEnd)
{
BOOL fReverse = (m_fDirection == TTB || m_fDirection == RTL);
BOOL fVert = (m_fDirection == TTB || m_fDirection == BTT);
CRect rc(rcDraw);
if(fVert)
{
if(nBandEnd == -1) nBandEnd = rcDraw.Height();
rc.top = rcDraw.top +
(fReverse ? nBandStart : (rcDraw.Height() - nBandEnd));
rc.bottom = rc.top + (nBandEnd - nBandStart);
}
else
{
if(nBandEnd == -1) nBandEnd = rcDraw.Width();
rc.left = rcDraw.left +
(fReverse ? (rcDraw.Width() - nBandEnd) : nBandStart);
rc.right = rc.left + (nBandEnd - nBandStart);
}
return rc;
}
void CGradient::CalcShiftedGradient(CGradArray& arElements, int nShift, int nGradWidth,
int nClipStart, int nClipEnd, UINT nMaxColors)
{
// normalize shift
if(nGradWidth == 0)
nShift = 0;
else
{
while(nShift < 0) nShift += nGradWidth;
nShift %= nGradWidth;
}
if(nShift == 0 || m_ardwGradColors.GetSize() < 2)
{
CalcMultiGradient(arElements, nGradWidth, nClipStart, nClipEnd, nMaxColors);
return;
}
CGradArray arElementsOrig;
CalcMultiGradient(arElementsOrig, nGradWidth, 0, -1, nMaxColors);
int nCount = arElementsOrig.GetSize();
if(nCount < 2)
{
// no gradient
arElements.Add(CGradElement(m_ardwGradColors[0], nClipEnd - nClipStart));
return;
}
// do shift
CGradArray arElementsShift;
int nLength = 0;
for(int i = nCount-1; i >= 0; i--)
{
CGradElement& el = arElementsOrig.ElementAt(i);
if(nLength + el.length > nShift)
{
// Separate
arElementsShift.InsertAt(0, CGradElement(el.color, nShift - nLength));
el.length -= nShift - nLength;
break;
}
else
{
nLength += el.length;
arElementsShift.InsertAt(0, el);
arElementsOrig.RemoveAt(i);
if(nLength == nShift)
break;
}
}
// combine shifted
arElementsShift.Append(arElementsOrig);
// do clip
if(nClipEnd == -1) nClipEnd = nGradWidth;
nCount = arElementsShift.GetSize();
nLength = 0;
for(int i = 0; i < nCount; i++)
{
CGradElement& el = arElementsShift.ElementAt(i);
if(nLength + el.length <= nClipStart)
{
nLength += el.length;
continue; // skip before clip start
}
int nStart = max(nLength, nClipStart);
nLength += el.length;
int nEnd = min(nLength, nClipEnd);
arElements.Add(CGradElement(el.color, nEnd - nStart));
if(nLength >= nClipEnd)
break; // skip after clip end
}
}
void CGradient::CalcMultiGradient(CGradArray& arElements, int nGradWidth,
int nClipStart, int nClipEnd, UINT nMaxColors)
{
if(nClipEnd == -1) nClipEnd = nGradWidth;
int nSteps = m_ardwGradColors.GetSize()-1;
if(nSteps < 0)
{
ASSERT(0); // at least 1 color should be added
return;
}
if(nSteps == 0)
{
arElements.Add(CGradElement(m_ardwGradColors[0], nClipEnd - nClipStart));
return;
}
double flBandGradStart = 0;
nMaxColors /= nSteps;
for (int i = 0; i < nSteps; i++)
{
int nBandGradStart = int((double)nGradWidth*flBandGradStart/100);
flBandGradStart += m_arflGradStretch[i];
int nBandGradEnd = int((double)nGradWidth*flBandGradStart/100);
if(i == nSteps-1) //last step (because of problems with float)
nBandGradEnd = nGradWidth;
if(nBandGradEnd < nClipStart)
continue; // skip - band before cliping rect
int nBandClipStart = nBandGradStart;
int nBandClipEnd = nBandGradEnd;
if(nBandClipStart < nClipStart)
nBandClipStart = nClipStart;
if(nBandClipEnd > nClipEnd)
nBandClipEnd = nClipEnd;
CalcGradient(arElements, m_ardwGradColors[i], m_ardwGradColors[i+1],
nBandGradEnd - nBandGradStart,
nBandClipStart - nBandGradStart,
nBandClipEnd - nBandGradStart,
nMaxColors);
if(nBandClipEnd == nClipEnd)
break; // stop filling - next band is out of clipping rect
}
}
void CGradient::CalcGradient(CGradArray& arElements, COLORREF clrStart, COLORREF clrEnd,
int nGradWidth, int nClipStart, int nClipEnd,
UINT nMaxColors)
{
if(nClipEnd == -1) nClipEnd = nGradWidth;
// Split colors to RGB chanels, find chanel with maximum difference
// between the start and end colors. This distance will determine
// number of steps of gradient
int r = (GetRValue(clrEnd) - GetRValue(clrStart));
int g = (GetGValue(clrEnd) - GetGValue(clrStart));
int b = (GetBValue(clrEnd) - GetBValue(clrStart));
UINT nSteps = max(abs(r), max(abs(g), abs(b)));
nSteps = min(nSteps, nMaxColors);
// if number of pixels in gradient less than number of steps -
// use it as numberof steps
UINT nPixels = nGradWidth;
nSteps = min(nPixels, nSteps);
if(nSteps == 0) nSteps = 1;
float rStep = (float)r/nSteps;
float gStep = (float)g/nSteps;
float bStep = (float)b/nSteps;
r = GetRValue(clrStart);
g = GetGValue(clrStart);
b = GetBValue(clrStart);
float nWidthPerStep = (float)nGradWidth / nSteps;
CBrush br;
// Start filling
for (UINT i = 0; i < nSteps; i++)
{
int nFillStart = (int)(nWidthPerStep * i);
int nFillEnd = (int)(nWidthPerStep * (i+1));
if(i == nSteps-1) //last step (because of problems with float)
nFillEnd = nGradWidth;
if(nFillEnd < nClipStart)
continue; // skip - band before cliping rect
// clip it
if(nFillStart < nClipStart)
nFillStart = nClipStart;
if(nFillEnd > nClipEnd)
nFillEnd = nClipEnd;
COLORREF clrFill = RGB(r + (int)(i * rStep),
g + (int)(i * gStep),
b + (int)(i * bStep));
// add band
arElements.Add(CGradElement(clrFill, nFillEnd - nFillStart));
if(nFillEnd >= nClipEnd)
break; // stop filling if we reach current position
}
}
void CGradient::SetGradientColorsX(int nCount, COLORREF clrFirst, COLORREF clrNext, ...)
{
ASSERT(nCount > 1);
m_ardwGradColors.SetSize(nCount);
m_ardwGradColors.SetAt(0, clrFirst);
m_ardwGradColors.SetAt(1, clrNext);
if(nCount > 2)
{
va_list pArgs;
va_start(pArgs, clrNext);
for(int i = 2; i < nCount; i++)
m_ardwGradColors.SetAt(i, va_arg(pArgs, COLORREF));
va_end( pArgs );
}
nCount--;
m_arflGradStretch.SetSize(nCount);
for(int i = 0; i < nCount; i++)
m_arflGradStretch[i] = (double)100 / nCount;
// remove all dependent objects
m_Pal.DeleteObject();
}
void CGradient::GetGradientColors(COLORREF& clrStart, COLORREF& clrEnd)
{
if(m_ardwGradColors.GetSize() > 0)
{
clrStart = m_ardwGradColors[0];
clrEnd = m_ardwGradColors[m_ardwGradColors.GetSize() > 1 ? 1 : 0];
}
else
clrStart = clrEnd = CLR_NONE;
}
void CGradient::AddColor(COLORREF clr)
{
m_ardwGradColors.Add(clr);
if(m_ardwGradColors.GetSize() > 1)
{
double flNew = (double)100/m_arflGradStretch.GetSize();
m_arflGradStretch.Add(flNew);
NormalizeColorsStretch();
}
}
void CGradient::CreatePalette()
{
if(!m_fCreatePalette || m_Pal.GetSafeHandle())
return;
int nNumColors = 236;
CGradArray arElements;
CalcMultiGradient(arElements, nNumColors, 0, nNumColors, nNumColors);
ASSERT(nNumColors >= arElements.GetSize());
nNumColors = arElements.GetSize();
if(!nNumColors)
return;
LPLOGPALETTE lpPal = (LPLOGPALETTE)new BYTE[sizeof(LOGPALETTE) +
sizeof(PALETTEENTRY) *
nNumColors];
if (!lpPal)
return;
lpPal->palVersion = 0x300;
lpPal->palNumEntries = nNumColors;
for (int i = 0; i < nNumColors; i++)
{
COLORREF nColor = arElements[i].color;
lpPal->palPalEntry[i].peRed = GetRValue(nColor);
lpPal->palPalEntry[i].peGreen = GetGValue(nColor);
lpPal->palPalEntry[i].peBlue = GetBValue(nColor);
lpPal->palPalEntry[i].peFlags = 0;
}
m_Pal.CreatePalette(lpPal);
delete [](PBYTE)lpPal;
}
void CGradient::SetStretchGradient(float flStretchFactor)
{
ASSERT(flStretchFactor >= 1);
if(flStretchFactor < 1)
flStretchFactor = 1;
m_flStretchGrad = flStretchFactor;
}
void CGradient::NormalizeColorsStretch()
{
int nCount = m_arflGradStretch.GetSize();
ASSERT(nCount == m_ardwGradColors.GetSize()-1);
double flFull = 0;
for(int i = 0; i < nCount; i++)
flFull += m_arflGradStretch[i];
for(int i = 0; i < nCount; i++)
m_arflGradStretch[i] = m_arflGradStretch[i] * 100 / flFull;
}
void CGradient::SetColorsStretch(double flFirst, ...)
{
int nCount = m_ardwGradColors.GetSize()-1;
if(nCount < 1)
{
ASSERT(0); // before you use this function add all necessary colors
return;
}
m_arflGradStretch.SetSize(nCount);
m_arflGradStretch.SetAt(0, flFirst);
va_list pArgs;
va_start(pArgs, flFirst);
for(int i = 1; i < nCount; i++)
m_arflGradStretch.SetAt(i, va_arg(pArgs, double));
va_end( pArgs );
NormalizeColorsStretch(); // normalize
}