Click here to Skip to main content
Click here to Skip to main content
Add your own
alternative version

Balloon Help as a non-modal replacement for MessageBox()

, 7 Aug 2002 CPOL
Although sometimes useful, message boxes used to display information are often just annoying. This article describes a non-modal replacement.
balloonhelpr2_demo.zip
BalloonTest.exe
res
BalloonTest.ico
BalloonTest.manifest
BalloonTest.dsw
BalloonTest.dsp
balloonhelpr2_src.zip
balloonhelpr3_demo.zip
BalloonTest.ico
BalloonTest.manifest
BalloonTest.dsw
BalloonTest.dsp
BalloonTest.exe
balloonhelpr3_src.zip
balloonhelp_demo.zip
BalloonTest.exe
BalloonTest.ico
BalloonTest.manifest
BalloonTest.dsw
BalloonTest.dsp
balloonhelp_src.zip
bh-atl.zip
BalloonTest.dsp
BalloonTest.dsw
BalloonTestps.mk
BalloonTestps.def
BalloonTest.rgs
BalloonTest.ico
BalloonTest.manifest
BalloonTest.suo
wtl_balloonhelp_demo.zip
BalloonTest
BalloonTest.dsp
Release
BalloonTest.exe
res
BalloonTest.exe.manifest
BalloonTest.ico
bob.bmp
// BalloonTestDlg.cpp : implementation file
//

#include "stdafx.h"
#include "BalloonTest.h"
#include "BalloonTestDlg.h"
#include "BalloonHelp.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#endif


// CBalloonTestDlg dialog



CBalloonTestDlg::CBalloonTestDlg(CWnd* pParent /*=NULL*/)
	: CDialog(CBalloonTestDlg::IDD, pParent)
   , m_nDividend(50)
   , m_nDivisor(7)
{
	m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}

void CBalloonTestDlg::DoDataExchange(CDataExchange* pDX)
{
   CDialog::DoDataExchange(pDX);
   DDX_Text(pDX, IDC_EDIT1, m_nDividend);
   DDX_Text(pDX, IDC_EDIT2, m_nDivisor);
}

BEGIN_MESSAGE_MAP(CBalloonTestDlg, CDialog)
	ON_WM_PAINT()
	ON_WM_QUERYDRAGICON()
   ON_BN_CLICKED(IDC_BTN_CALCULATE, OnBnClickedBtnCalculate)
   ON_STN_CLICKED(IDC_LBL_LCLICK, OnStnClickedLblLclick)
   ON_WM_HELPINFO()
   ON_STN_DBLCLK(IDC_LBL_CLICK, OnStnDblclickLblClick)
END_MESSAGE_MAP()


// CBalloonTestDlg message handlers

BOOL CBalloonTestDlg::OnInitDialog()
{
	CDialog::OnInitDialog();

	// Set the icon for this dialog.  The framework does this automatically
	//  when the application's main window is not a dialog
	SetIcon(m_hIcon, TRUE);			// Set big icon
	SetIcon(m_hIcon, FALSE);		// Set small icon

   HICON hIconSmall = static_cast<HICON>(::LoadImage(AfxGetResourceHandle(), MAKEINTRESOURCE(IDR_MAINFRAME), IMAGE_ICON, 16,16, LR_DEFAULTCOLOR));
   bhPersistent.SetIcon(hIconSmall);
   ::DestroyIcon(hIconSmall);

   CFont* pFont = new CFont;
   pFont->CreatePointFont(120, "Times New Roman");
   bhPersistent.SetTitleFont(pFont);   // deleted by balloon.

   CRect rect;
   GetClientRect(&rect);
   bhPersistent.Create("Hello!", "Welcome to the Balloon Help test app!", rect.BottomRight(), CBalloonHelp::unSHOW_INNER_SHADOW|CBalloonHelp::unSHOW_CLOSE_BUTTON|CBalloonHelp::unDISABLE_XP_SHADOW, this);
	
	return TRUE;  // return TRUE  unless you set the focus to a control
}

// If you add a minimize button to your dialog, you will need the code below
//  to draw the icon.  For MFC applications using the document/view model,
//  this is automatically done for you by the framework.

void CBalloonTestDlg::OnPaint() 
{
	if (IsIconic())
	{
		CPaintDC dc(this); // device context for painting

		SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);

		// Center icon in client rectangle
		int cxIcon = GetSystemMetrics(SM_CXICON);
		int cyIcon = GetSystemMetrics(SM_CYICON);
		CRect rect;
		GetClientRect(&rect);
		int x = (rect.Width() - cxIcon + 1) / 2;
		int y = (rect.Height() - cyIcon + 1) / 2;

		// Draw the icon
		dc.DrawIcon(x, y, m_hIcon);
	}
	else
	{
		CDialog::OnPaint();
	}
}

// The system calls this function to obtain the cursor to display while the user drags
//  the minimized window.
HCURSOR CBalloonTestDlg::OnQueryDragIcon()
{
	return static_cast<HCURSOR>(m_hIcon);
}

void CBalloonTestDlg::OnBnClickedBtnCalculate()
{
   UpdateData(TRUE);
   if ( m_nDivisor == 0 )
   {
      CRect rect;
      GetDlgItem(IDC_EDIT2)->GetWindowRect(&rect);
      SetDlgItemText(IDC_LBL_RESULT, "Undefined");
      CBalloonHelp::LaunchBalloon("Division by Zero!", "Enter a number that is not zero, please.", rect.CenterPoint(), IDI_ERROR, CBalloonHelp::unCLOSE_ON_MOUSE_MOVE|CBalloonHelp::unDISABLE_FADEOUT|CBalloonHelp::unCLOSE_ON_KEYPRESS, NULL, "", 3000);   
   }
   else
   {
      CString strResult;
      strResult.Format("%d", m_nDividend/m_nDivisor);
      SetDlgItemText(IDC_LBL_RESULT, strResult);
   }
}

void CBalloonTestDlg::OnStnClickedLblLclick()
{
   CPoint point;
   ::GetCursorPos(&point);
   CBalloonHelp::LaunchBalloon("You are holding down the left mouse button!", "\tAs you see, balloons can hold lots of text.\nText that can appear on multiple lines, wrap,\n\tor\tbe\ttabbed\t;)\n\n\tYou can provide as much information as is necessary.", point, IDI_WINLOGO, CBalloonHelp::unCLOSE_ON_LBUTTON_UP, NULL, "", 0);
}


BOOL CBalloonTestDlg::OnHelpInfo(HELPINFO* pHelpInfo)
{
   CString strTitle;
   CString strMsg;
   CRect rect;
   if ( NULL != pHelpInfo->hItemHandle && HELPINFO_WINDOW == pHelpInfo->iContextType )
      ::GetWindowRect((HWND)pHelpInfo->hItemHandle, &rect);

   switch (pHelpInfo->iCtrlId)
   {
   case IDC_BTN_CALCULATE:
      strTitle = "Calculate";
      strMsg = "Click this button to perform the division.";
      break;
   case IDC_EDIT1:
      strTitle = "Dividend";
      strMsg = "Enter the number to be divided here.";
      break;
   case IDC_EDIT2:
      strTitle = "Divisor";
      strMsg = "Enter the number to divide by here.";
      break;
   case IDC_LBL_LCLICK:
      strTitle = "Left-button dependant balloon test area";
      strMsg = "Press and hold the left mouse button over this area.";
      break;
   case IDC_LBL_RCLICK:
      strTitle = "Right-button dependant balloon test area";
      strMsg = "Press and hold the right mouse button over this area.";
      break;
   case IDC_LBL_CLICK:
      strTitle = "Feedback balloon launcher area";
      strMsg = "Double-click this area.";
      break;
   case IDC_LBL_RESULT:
      strTitle = "Quotient";
      strMsg = "The result of the division will appear here.";
      break;
   case IDOK:
      strTitle = "Close";
      strMsg = "Click this button to close the program.";
      break;
   default:
      return TRUE;
   }

   CBalloonHelp::LaunchBalloon(strTitle, strMsg, rect.CenterPoint(), IDI_INFORMATION, CBalloonHelp::unCLOSE_ON_MOUSE_MOVE|CBalloonHelp::unDELAY_CLOSE, NULL, "", 1000);

   return TRUE;
}

BOOL CBalloonTestDlg::PreTranslateMessage(MSG* pMsg)
{
   if ( NULL != pMsg->hwnd && ::GetDlgCtrlID(pMsg->hwnd) == IDC_LBL_RCLICK && pMsg->message == WM_RBUTTONDOWN)
   {      
      CBalloonHelp::LaunchBalloon("You are holding down the right mouse button!", "\tWARNING!!!\n There is something about this balloon that you may not fully understand...  When you release the right mouse button IT WILL DISAPPEAR!!!  All the words of wisdom contained within it will NO LONGER BE AVAILABLE TO YOU!!!  I say this, hoping to spare you the pain and heartbreak that would surely afflict you, should you be so careless as to release the right mouse button.\n Now that that's out of the way, THE BALLOON CONTENT:\n\n\t\"Don't eat yellow snow\"\n", CPoint(pMsg->pt), IDI_WARNING, CBalloonHelp::unCLOSE_ON_RBUTTON_UP|CBalloonHelp::unDISABLE_FADE, NULL, "", 0);
      return TRUE;
   }
   else
   {
      return CDialog::PreTranslateMessage(pMsg);
   }
}

void CBalloonTestDlg::OnStnDblclickLblClick()
{
   CPoint point;
   ::GetCursorPos(&point);
   ScreenToClient(&point);
   CBalloonHelp::LaunchBalloon("Well...", "\tWhat do you think?  Is this good?  Is this useful?  Does it need a lot of work?  Or is it a complete waste of time...  Let me know your thoughts!\n\n\tClick this balloon to send me some feedback...", point, IDI_QUESTION, CBalloonHelp::unSHOW_CLOSE_BUTTON|CBalloonHelp::unSHOW_INNER_SHADOW|CBalloonHelp::unSHOW_TOPMOST, this, "mailto:shognine@yahoo.com", 0);
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Shog9
Software Developer
United States United States
Poke...

| Advertise | Privacy | Mobile
Web04 | 2.8.141015.1 | Last Updated 8 Aug 2002
Article Copyright 2001 by Shog9
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid