|
My Project is Client Server communication. Server sends Error Message
to the Client. Client will Popup the MessageBox and server will wait till
to get the Response from the client.If the Client doesn't response,
server will be deadlocked state. So i need to implement same like a
modelss MessageBox with timer. I found your fantastic article and changed
it to my own way.
Regards,
Uma
|
|
|
|
|
The text of common dialogs like File Open is in the(/an) OS language,
i.e. only one language is used, maybe not the one the user would prefer,
maybe not the one used by the invoking application.
MessageBox (and AfxMessageBox) mix things up,
with the message text coming from the application
and the button text from the OS.
Having two languages in the one interaction is something that
international users have maybe come to accept/ignore/overlook,
but to me it makes a shoddy (and schizophrenic) impression.
It also prevents the meticulous designer from using message texts like
"If <bla bla>, press Yes"
I tried out XMessageBox because it offers the possibility of forcing
the button text to be in the same language as the message text.
It has a bug here: LoadButtonStringsFromResources uses ghInst.
If the application switches languages e.g. by loading a resource dll,
the message text is in the dll language but
the button text stays in the exe language.
To correct this, use the resource handle instead:
static void LoadButtonStringsFromResources()
{
HINSTANCE hRes=AfxGetResourceHandle();
if (::LoadString(hRes, IDS_OK, szOK, MAXBUTTONSTRING) == 0)
repeatedly passing hRes to LoadString instead of ghInst.
Robin Bannister
|
|
|
|
|
The file afxpriv.h contains useful classes that are documented only in the MFC Technical Notes. These classes may change from version to version.
If you are including "afxpriv.h" into your project then you have to rename CDialogTemplate in XMessageBox.cpp as it is implemented in these useful MFC classes in "afxpriv.h". Atleast in MFC 4.2 (MSDEV 6).
ook
|
|
|
|
|
Hi,
just to let you know that IDCONTINUE is now defined in the november 2001 platform SDK update, so yours is in conflict with it.
Regards
Nicolas Cadilhac
CAE Inc.
|
|
|
|
|
longer-labeled check-boxes are better with
#if 0 //t!
w += w/3;
#else
w += (checkboxrect.bottom - checkboxrect.top) * 2;
#endif
(and still better with big fonts too, i hope noone will set smaller ones; generaly: it has not depend on font size but i do not know how to find system-defined box size and box-text-distance; what about control and dialog resizing after their creation?)
t!
|
|
|
|
|
Hi Hans,
Thank you for your usefull code.
I have adjusted the code to suit our project needs. Things I did were:
- making it MFC free
- removing of all static/global variables. so you can use it in case you have more msg boxes at one time (in e.g. a multithreaded app)
- addition of Report button
- correct character counts in case _TCHAR is multi-byte
- make the messagebox, in case called with parentwnd NULL, a child of the current active window
Add this line to the header
#define MB_REPORT 0x20000000L // Add a report button
New implementatation of XMessageBox.cpp
// XMessageBox.cpp
//
// Original Author: Hans Dietrich
// hdietrich2@hotmail.com
//
// Some parts of this software are from information in the
// Microsoft SDK.
// Code downloaded from The Code Project http://codeproject.com or http://thecodeproject.com
//
// This software is released into the public domain.
// You are free to use it in any way you like.
//
// This software is provided "as is" with no expressed
// or implied warranty. I accept no liability for any
// damage or loss of business that this software may cause.
//
// Code is adjusted by Anne Jan Beeks
// - making it MFC free
// - removing of static variables. so you can use it in case you have more boxes at one time
// Which could happen in a Multithread app
// - addition of Report button
// - correct character counts in case _TCHAR is multi-byte
// - make the messagebox, in case called with parent-wnd NULL, a child of the current active window
//
///////////////////////////////////////////////////////////////////////////////
#include <new> //std::bad_alloc
#include <windows.h>
#include <tchar.h>
#pragma hdrstop
//#include "amsqo/QOInclude.h" // TRACE ASSERT TException Reminder QOGetAppName QOReportError
// We include our own TRACE ASSERT and a TException class.
// I include some dummy definetions to make it compilable.
#ifndef KLM_UTIL_AMSQO_QOINCLUDE_H
#ifndef TRACE
#define TRACE (void)0
#endif
#ifndef ASSERT
#define ASSERT (void)0
#endif
#define TException //just throw a const char*
const _TCHAR* QOGetAppName() { return _T("My Application");};
void QOReportError(const _TCHAR* /*errortext*/) {}; // function will be called in case the user.....
// ...presses the Report button. Our implementation sends an email to our support team.
#endif //KLM_UTIL_AMSQO_QOINCLUDE_H
#include "XMessageBox.h"
inline const int Width(const RECT&rect) { return rect.right-rect.left;};
inline const int Height(const RECT&rect) { return rect.bottom-rect.top;};
///////////////////////////////////////////////////////////////////////////////
//
// Class definitions
//
class CDialogTemplate;
class CDialogItem
{
public:
DLGITEMTEMPLATE m_dlgItemTemplate;
enum Econtroltype {ICON = 0x7F, BUTTON, EDITCONTROL, STATICTEXT, CHECKBOX};
Econtroltype m_controltype;
_TCHAR m_szCaption[1024];
public:
CDialogItem(Econtroltype cType); // default constructor will fill in default values
CDialogItem() {}; // default constructor, not to be called directly
void AddItem(CDialogTemplate& dialog, Econtroltype cType,
UINT nID,
RECT* prect = NULL,
LPCTSTR pszCaption = NULL);
};
class CDialogTemplate
{
protected:
enum { FirstControlId = 1001};
enum { MaxButtonStringSize = 100};
enum
{
SpacingSize = 8,
ButtonWidth = 82,
ButtonHeight = 23,
ButtonSpacing = 6,
BottomMargin = 10,
MinimalHeight = 70,
DoNotAskAgainHeight = 16,
IdExHelp = 300,
IdExReport = 301,
IdDoNotAskAgian = 5555,
// if you change the value for MaxItems, make sure that the code
// in CDialogTemplate remains consistent with your changes.
MaxItems = 20, // max no. of items in the dialog
};
CDialogItem* m_pDlgItemArray[MaxItems];
static BOOL CALLBACK MsgBoxDlgProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM me);
int m_indicator;
int m_nButton; // current button no.
int m_nDefButton; // Default button
UINT m_nMaxID; // max control id (one more)
UINT m_nDefId; // button number of default button
UINT m_nHelpId; // help context id
HWND m_hWnd; // handle of owner window
const _TCHAR* m_lpszMessage; // The message retain pointer for Report functiom
HICON m_hIcon; // Handle of icon
HANDLE m_hFont; // handle to font for the message box
DLGTEMPLATE m_dlgTempl; // message box dialog template
_TCHAR szOK[MaxButtonStringSize];
_TCHAR szCancel[MaxButtonStringSize];
_TCHAR szIgnore[MaxButtonStringSize];
_TCHAR szRetry[MaxButtonStringSize];
_TCHAR szAbort[MaxButtonStringSize];
_TCHAR szHelp[MaxButtonStringSize];
_TCHAR szYes[MaxButtonStringSize];
_TCHAR szNo[MaxButtonStringSize];
_TCHAR szContinue[MaxButtonStringSize];
_TCHAR szDoNotAskAgain[MaxButtonStringSize];
_TCHAR szDoNotTellAgain[MaxButtonStringSize];
_TCHAR szYesToAll[MaxButtonStringSize];
_TCHAR szNoToAll[MaxButtonStringSize];
_TCHAR szReport[MaxButtonStringSize];
enum EOpt
{
DoNotAskAgain = 0x01, //include Do Not Ask checkbox
DoNotTellAgain = 0x02, //include Do Not Tell checkbox
CancelButton = 0x04, //include Cancel button
OkButton = 0x08, //MB_OK used
CancelOrOkButton = CancelButton | OkButton,
EDefault = 0x00
};
void Unset(EOpt id) { m_indicator &= ~id;};
void Set(EOpt id) { m_indicator |= id;};
int Option(EOpt id) const { return m_indicator & id;};
void LoadButtonStrings();
void LoadButtonStringsFromResources();
public:
const _TCHAR* MessageText() const { return m_lpszMessage;};
int& ButtonCount() { return m_nButton;};
void DefaultButtonId(UINT nDefId) { m_nDefId = nDefId;};
int DefaultButton() const { return m_nDefButton;};
//UINT MaxControlId() const ( return m_nMaxID;};
CDialogTemplate(HWND hWnd, LPCTSTR lpszMessage, UINT nStyle, UINT nHelpId);
virtual ~CDialogTemplate();
int Display(LPCTSTR lpszCaption);
void AddItem(CDialogItem::Econtroltype cType,
UINT nID,
RECT* prect = NULL,
LPCTSTR pszCaption = NULL);
};
int XMessageBox(HWND hwnd,
LPCTSTR lpszMessage,
LPCTSTR lpszCaption /* = NULL */,
UINT nStyle /* = MB_OK|MB_ICONEXCLAMATION */,
UINT nHelpId /* = 0 */)
{
ASSERT(lpszMessage);
if (hwnd == NULL)
{
hwnd = ::GetActiveWindow() ;
if (hwnd != NULL)
{
hwnd = ::GetLastActivePopup(hwnd) ;
}
};
if ((nStyle & MB_ICONHAND) && (nStyle & MB_SYSTEMMODAL))
{
// NOTE: When an application calls MessageBox and specifies the
// MB_ICONHAND and MB_SYSTEMMODAL flags for the nStyle parameter,
// the system displays the resulting message box regardless of
// available memory.
return ::MessageBox(hwnd, lpszMessage, lpszCaption, nStyle);
}
if (lpszCaption == NULL || lpszCaption[0] == 0)
{
lpszCaption = QOGetAppName();
}
CDialogTemplate dlg(hwnd, lpszMessage, nStyle, nHelpId);
if ((nStyle & MB_NOSOUND) == 0)
{
::MessageBeep(nStyle & MB_ICONMASK);
}
int rc = dlg.Display(lpszCaption);
return rc;
}
///////////////////////////////////////////////////////////////////////////////
// IconProc
LONG CALLBACK IconProc(HWND hwnd, UINT message, WPARAM, LPARAM)
{
if (message == WM_PAINT)
{
PAINTSTRUCT ps;
HDC hdc;
hdc = BeginPaint(hwnd, &ps);
DrawIcon(hdc, 0, 0, reinterpret_cast<hicon>(::GetWindowLong(hwnd, GWL_USERDATA)));
EndPaint(hwnd, &ps);
}
return FALSE;
}
///////////////////////////////////////////////////////////////////////////////
// CDialogTemplate class
///////////////////////////////////////////////////////////////////////////////
// CDialogTemplate ctor
CDialogTemplate::CDialogTemplate(HWND hWnd, LPCTSTR lpszMessage, UINT nStyle, UINT nHelpId) :
m_hWnd(hWnd),
m_lpszMessage(lpszMessage),
m_nHelpId(nHelpId),
m_indicator(EDefault),
m_hIcon(NULL),
m_hFont(NULL),
m_nButton(0), // current button no.
m_nDefId(1), // button number of default button
m_nMaxID(FirstControlId) // control id
{
// if (nStyle & MB_NORESOURCE)
LoadButtonStrings(); // use English strings
// else
// LoadButtonStringsFromResources(); // try to load from resource strings
HDC hdc = ::CreateDC(_T("DISPLAY"), NULL, NULL, NULL);
ASSERT(hdc);
// get font for message box
/*
LOGFONT lf;
memset(&lf, 0, sizeof(lf));
_tcscpy(lf.lfFaceName, _T("MS Sans Serif"));
lf.lfHeight = -12;
lf.lfWeight = FW_NORMAL;
m_hFont = ::CreateFontIndirect(&lf);
*/
NONCLIENTMETRICS ncm;
memset(&ncm, 0, sizeof(ncm));
ncm.cbSize = sizeof(ncm);
SystemParametersInfo(SPI_GETNONCLIENTMETRICS, 0, &ncm, 0);
m_hFont = ::CreateFontIndirect(&ncm.lfMessageFont);
HFONT hOldFont = (HFONT)::SelectObject(hdc, m_hFont);
int nMaxWidth = (::GetSystemMetrics(SM_CXSCREEN) / 2) + 100;
if (nStyle & MB_ICONMASK)
{
nMaxWidth -= GetSystemMetrics(SM_CXICON) + 2*SpacingSize;
}
RECT msgrect;
SetRect(&msgrect, 0, 0, nMaxWidth, nMaxWidth);
::DrawText(hdc, MessageText(), -1, &msgrect, DT_LEFT | DT_NOPREFIX | DT_WORDBREAK | DT_CALCRECT);
msgrect.right += 12;
msgrect.bottom += 5;
msgrect.left = 2 * SpacingSize;
msgrect.top = 2 * SpacingSize;
msgrect.right += 2 * SpacingSize;
msgrect.bottom += 2 * SpacingSize;
// client rect
RECT mbrect;
SetRect(&mbrect, 0, 0,
Width(msgrect) + (2 * SpacingSize),
Height(msgrect) + (2 * SpacingSize));
if (Height(mbrect) < MinimalHeight)
mbrect.bottom = MinimalHeight;
// now initialize the DLGTEMPLATE structure
m_dlgTempl.x = 0;
m_dlgTempl.y = 0;
m_dlgTempl.cdit = 0;
m_dlgTempl.style = WS_CAPTION | WS_VISIBLE | WS_SYSMENU |
WS_POPUP | DS_MODALFRAME | DS_CENTER;
m_dlgTempl.dwExtendedStyle = 0;
if (nStyle & MB_SYSTEMMODAL)
{
m_dlgTempl.style |= DS_SYSMODAL;
}
for (int j = 0; j < MaxItems; j++)
{
m_pDlgItemArray[j] = NULL;
}
int x, y;
RECT iconrect;
SetRect(&iconrect, 0, 0, 0, 0);
RECT rect;
if (nStyle & MB_ICONMASK)
{
int cxIcon;
int cyIcon;
LPSTR lpIcon = NULL;
switch (nStyle & MB_ICONMASK)
{
case MB_ICONEXCLAMATION:
lpIcon = (LPSTR)IDI_EXCLAMATION;
break;
case MB_ICONHAND:
lpIcon = (LPSTR)IDI_HAND;
break;
case MB_ICONQUESTION:
lpIcon = (LPSTR)IDI_QUESTION;
break;
case MB_ICONASTERISK:
lpIcon = (LPSTR)IDI_ASTERISK;
break;
}
if (lpIcon)
{
m_hIcon = ::LoadIcon(NULL, lpIcon);
cxIcon = GetSystemMetrics(SM_CXICON);
cyIcon = GetSystemMetrics(SM_CYICON);
int icon_x = SpacingSize;
int icon_y = SpacingSize;
msgrect.left += cxIcon + icon_x;
msgrect.right += cxIcon + icon_x;
mbrect.right = msgrect.right + SpacingSize;
SetRect(&iconrect, icon_x, icon_y, icon_x + cxIcon + 2, icon_y + cyIcon + 2);
AddItem(CDialogItem::STATICTEXT, 1000, &iconrect, _T(""));
}
}
AddItem(CDialogItem::STATICTEXT, m_nMaxID++, &msgrect, MessageText());
int cItems = 0;
switch (nStyle & MB_TYPEMASK)
{
case MB_OK :
cItems = 1;
break;
case MB_OKCANCEL :
cItems = 2;
break;
case MB_YESNO :
cItems = 2;
break;
case MB_YESNOCANCEL :
cItems = 3;
break;
case MB_ABORTRETRYIGNORE :
cItems = 3;
break;
case MB_RETRYCANCEL :
cItems = 2;
break;
case MB_CONTINUEABORT :
cItems = 2;
break;
}
if (nStyle & MB_HELP)
{
cItems++;
}
if (nStyle & MB_YESTOALL)
{
if ((nStyle & MB_YESNO) || (nStyle & MB_YESNOCANCEL))
{
cItems++;
}
else
{
ASSERT(FALSE); // must have either MB_YESNO or MB_YESNOCANCEL
}
}
if (nStyle & MB_NOTOALL)
{
if ((nStyle & MB_YESNO) || (nStyle & MB_YESNOCANCEL))
{
cItems++;
}
else
{
ASSERT(FALSE); // must have either MB_YESNO or MB_YESNOCANCEL
}
}
if (nStyle & MB_REPORT)
{
cItems++;
}
if (nStyle & MB_DONOTASKAGAIN)
{
Set(DoNotAskAgain);
}
else if (nStyle & MB_DONOTTELLAGAIN)
{
Set(DoNotTellAgain);
};
ASSERT(cItems > 0);
RECT buttonrow;
y = (msgrect.bottom > iconrect.bottom) ? msgrect.bottom : iconrect.bottom;
y += SpacingSize;
int w = ButtonWidth * cItems + (ButtonSpacing * (cItems - 1));
SetRect(&buttonrow,0, y, w,
y + ButtonHeight);
switch (nStyle & MB_DEFMASK)
{
case MB_DEFBUTTON1 :
m_nDefButton = 1;
break;
case MB_DEFBUTTON2 :
m_nDefButton = 2;
break;
case MB_DEFBUTTON3 :
m_nDefButton = 3;
break;
case MB_DEFBUTTON4 :
m_nDefButton = 4;
break;
case MB_DEFBUTTON5 :
m_nDefButton = 5;
break;
case MB_DEFBUTTON6 :
m_nDefButton = 6;
break;
default:
m_nDefButton = 1;
}
if (m_nDefButton > cItems)
{
m_nDefButton = 1;
}
x = (Width(mbrect) - Width(buttonrow)) / 2;
mbrect.bottom = buttonrow.bottom + BottomMargin;
int bw = Width(buttonrow);
int bleft = 2 * SpacingSize;
int bright = bleft + bw;
if (mbrect.right <= (bright + (2 * SpacingSize)))
{
mbrect.right = bright + (2 * SpacingSize);
}
x = (Width(mbrect) - bw) / 2;
y = buttonrow.top;
switch (nStyle & MB_TYPEMASK)
{
case MB_OK:
SetRect(&rect,x, y, x + ButtonWidth, y + ButtonHeight);
AddItem(CDialogItem::BUTTON, IDOK, &rect, szOK);
Set(OkButton);
break;
case MB_OKCANCEL:
SetRect(&rect,x, y, x + ButtonWidth, y + ButtonHeight);
AddItem(CDialogItem::BUTTON, IDOK, &rect, szOK);
x += ButtonWidth + ButtonSpacing;
SetRect(&rect,x, y, x + ButtonWidth, y + ButtonHeight);
AddItem(CDialogItem::BUTTON, IDCANCEL, &rect, szCancel);
Set(CancelButton);
break;
case MB_YESNO:
SetRect(&rect,x, y, x + ButtonWidth, y + ButtonHeight);
AddItem(CDialogItem::BUTTON, IDYES, &rect, szYes);
if (nStyle & MB_YESTOALL)
{
x += ButtonWidth + ButtonSpacing;
SetRect(&rect,x, y, x + ButtonWidth, y + ButtonHeight);
AddItem(CDialogItem::BUTTON, IDYESTOALL, &rect, szYesToAll);
}
x += ButtonWidth + ButtonSpacing;
SetRect(&rect,x, y, x + ButtonWidth, y + ButtonHeight);
AddItem(CDialogItem::BUTTON, IDNO, &rect, szNo);
if (nStyle & MB_NOTOALL)
{
x += ButtonWidth + ButtonSpacing;
SetRect(&rect,x, y, x + ButtonWidth, y + ButtonHeight);
AddItem(CDialogItem::BUTTON, IDNOTOALL, &rect, szNoToAll);
}
break;
case MB_YESNOCANCEL:
SetRect(&rect,x, y, x + ButtonWidth, y + ButtonHeight);
AddItem(CDialogItem::BUTTON, IDYES, &rect, szYes);
if (nStyle & MB_YESTOALL)
{
x += ButtonWidth + ButtonSpacing;
SetRect(&rect,x, y, x + ButtonWidth, y + ButtonHeight);
AddItem(CDialogItem::BUTTON, IDYESTOALL, &rect, szYesToAll);
}
x += ButtonWidth + ButtonSpacing;
SetRect(&rect,x, y, x + ButtonWidth, y + ButtonHeight);
AddItem(CDialogItem::BUTTON, IDNO, &rect, szNo);
if (nStyle & MB_NOTOALL)
{
x += ButtonWidth + ButtonSpacing;
SetRect(&rect,x, y, x + ButtonWidth, y + ButtonHeight);
AddItem(CDialogItem::BUTTON, IDNOTOALL, &rect, szNoToAll);
}
x += ButtonWidth + ButtonSpacing;
SetRect(&rect,x, y, x + ButtonWidth, y + ButtonHeight);
AddItem(CDialogItem::BUTTON, IDCANCEL, &rect, szCancel);
Set(CancelButton);
break;
case MB_ABORTRETRYIGNORE:
SetRect(&rect,x, y, x + ButtonWidth, y + ButtonHeight);
AddItem(CDialogItem::BUTTON, IDABORT, &rect, szAbort);
x += ButtonWidth + ButtonSpacing;
SetRect(&rect,x, y, x + ButtonWidth, y + ButtonHeight);
AddItem(CDialogItem::BUTTON, IDRETRY, &rect, szRetry);
x += ButtonWidth + ButtonSpacing;
SetRect(&rect,x, y, x + ButtonWidth, y + ButtonHeight);
AddItem(CDialogItem::BUTTON, IDIGNORE, &rect, szIgnore);
break;
case MB_RETRYCANCEL:
SetRect(&rect,x, y, x + ButtonWidth, y + ButtonHeight);
AddItem(CDialogItem::BUTTON, IDRETRY, &rect, szRetry);
x += ButtonWidth + ButtonSpacing;
SetRect(&rect,x, y, x + ButtonWidth, y + ButtonHeight);
AddItem(CDialogItem::BUTTON, IDCANCEL, &rect, szCancel);
Set(CancelButton);
break;
case MB_CONTINUEABORT:
SetRect(&rect,x, y, x + ButtonWidth, y + ButtonHeight);
AddItem(CDialogItem::BUTTON, IDCONTINUE, &rect, szContinue);
x += ButtonWidth + ButtonSpacing;
SetRect(&rect,x, y, x + ButtonWidth, y + ButtonHeight);
AddItem(CDialogItem::BUTTON, IDABORT, &rect, szAbort);
break;
default:
SetRect(&rect,x, y, x + ButtonWidth, y + ButtonHeight);
AddItem(CDialogItem::BUTTON, IDOK, &rect, szOK);
break;
}
if (nStyle & MB_HELP)
{
x += ButtonWidth + ButtonSpacing;
SetRect(&rect,x, y, x + ButtonWidth, y + ButtonHeight);
AddItem(CDialogItem::BUTTON, IdExHelp, &rect, szHelp);
}
nMaxWidth = ::GetSystemMetrics(SM_CXSCREEN) / 3;
RECT checkboxrect;
SetRect(&checkboxrect,0, 0, nMaxWidth, DoNotAskAgainHeight);
if (nStyle & MB_DONOTASKAGAIN)
{
x = 2 * ButtonSpacing + 5;
y += ButtonHeight + (2 * ButtonSpacing);
::DrawText(hdc, szDoNotAskAgain, -1, &checkboxrect,
DT_LEFT | DT_NOPREFIX | DT_CALCRECT | DT_SINGLELINE);
int w = checkboxrect.right - checkboxrect.left;
w += w/3;
//rect.SetRect(x, y, x + MSGBOXEX_DONOTASKAGAIN_WIDTH, y + DoNotAskAgainHeight);
SetRect(&rect,x, y, x + w, y + DoNotAskAgainHeight);
AddItem(CDialogItem::CHECKBOX, IdDoNotAskAgian, &rect, szDoNotAskAgain);
buttonrow.bottom = y + DoNotAskAgainHeight;
mbrect.bottom = buttonrow.bottom + SpacingSize;
if (Width(mbrect) < (x + w))
mbrect.right = mbrect.left + x + w;
}
else if (nStyle & MB_DONOTTELLAGAIN)
{
x = 2 * ButtonSpacing + 5;
y += ButtonHeight + (2 * ButtonSpacing);
::DrawText(hdc, szDoNotTellAgain, -1, &checkboxrect,
DT_LEFT | DT_NOPREFIX | DT_CALCRECT | DT_SINGLELINE);
int w = checkboxrect.right - checkboxrect.left;
w += w/3;
//rect.SetRect(x, y, x + MSGBOXEX_DONOTASKAGAIN_WIDTH, y + DoNotAskAgainHeight);
SetRect(&rect,x, y, x + w, y + DoNotAskAgainHeight);
AddItem(CDialogItem::CHECKBOX, IdDoNotAskAgian, &rect, szDoNotTellAgain);
buttonrow.bottom = y + DoNotAskAgainHeight;
mbrect.bottom = buttonrow.bottom + SpacingSize;
if (Width(mbrect) < (x + w))
{
mbrect.right = mbrect.left + x + w;
}
}
if (nStyle & MB_REPORT)
{
x += ButtonWidth + ButtonSpacing;
SetRect(&rect,x, y, x + ButtonWidth, y + ButtonHeight);
AddItem(CDialogItem::BUTTON, IdExReport, &rect, szReport);
}
if (buttonrow.bottom >= mbrect.bottom)
{
mbrect.bottom = buttonrow.bottom + (2 * SpacingSize);
}
if (mbrect.right < (buttonrow.right + (2 * SpacingSize)))
{
mbrect.right = buttonrow.right + (2 * SpacingSize);
}
short hidbu = HIWORD(GetDialogBaseUnits());
short lodbu = LOWORD(GetDialogBaseUnits());
m_dlgTempl.x = 0;
m_dlgTempl.y = 0;
m_dlgTempl.cx = (short)((Width(mbrect) * 4) / lodbu);
m_dlgTempl.cy = (short)((Height(mbrect) * 8) / hidbu);
::SelectObject(hdc, hOldFont);
::DeleteDC(hdc);
}
///////////////////////////////////////////////////////////////////////////////
// CDialogTemplate dtor
CDialogTemplate::~CDialogTemplate()
{
if (m_hIcon)
{
DestroyIcon(m_hIcon);
}
if (m_hFont)
{
::DeleteObject(m_hFont);
}
for (int i = 0; i < MaxItems; i++)
{
if (m_pDlgItemArray[i])
{
delete m_pDlgItemArray[i];
m_pDlgItemArray[i] = NULL;
}
}
}
///////////////////////////////////////////////////////////////////////////////
// LoadButtonStrings
void CDialogTemplate::LoadButtonStrings()
{
_tcscpy(szOK, _T("OK"));
_tcscpy(szCancel, _T("Cancel"));
_tcscpy(szIgnore, _T("&Ignore"));
_tcscpy(szRetry, _T("&Retry"));
_tcscpy(szAbort, _T("&Abort"));
_tcscpy(szHelp, _T("&Help"));
_tcscpy(szYes, _T("&Yes"));
_tcscpy(szNo, _T("&No"));
_tcscpy(szContinue, _T("&Continue"));
_tcscpy(szDoNotAskAgain, _T("Don't ask me again"));
_tcscpy(szDoNotTellAgain, _T("Don't tell me again"));
_tcscpy(szYesToAll, _T("Yes to &All"));
_tcscpy(szNoToAll, _T("No to A&ll"));
_tcscpy(szReport, _T("Re&port"));
}
///////////////////////////////////////////////////////////////////////////////
// LoadButtonStringsFromResources
void CDialogTemplate::LoadButtonStringsFromResources()
{
HINSTANCE hInstance = GetModuleHandle(NULL);
if (::LoadString(hInstance, IDS_OK, szOK, MaxButtonStringSize) == 0)
{
_tcscpy(szOK, _T("OK"));
}
szOK[MaxButtonStringSize-1] = _T('\0');
if (::LoadString(hInstance, IDS_CANCEL, szCancel, MaxButtonStringSize) == 0)
{
_tcscpy(szCancel, _T("Cancel"));
}
szCancel[MaxButtonStringSize-1] = _T('\0');
if (::LoadString(hInstance, IDS_IGNORE, szIgnore, MaxButtonStringSize) == 0)
{
_tcscpy(szIgnore, _T("&Ignore"));
}
szIgnore[MaxButtonStringSize-1] = _T('\0');
if (::LoadString(hInstance, IDS_RETRY, szRetry, MaxButtonStringSize) == 0)
{
_tcscpy(szRetry, _T("&Retry"));
}
szRetry[MaxButtonStringSize-1] = _T('\0');
if (::LoadString(hInstance, IDS_ABORT, szAbort, MaxButtonStringSize) == 0)
{
_tcscpy(szAbort, _T("&Abort"));
}
szAbort[MaxButtonStringSize-1] = _T('\0');
if (::LoadString(hInstance, IDS_HELP, szHelp, MaxButtonStringSize) == 0)
{
_tcscpy(szHelp, _T("&Help"));
}
szHelp[MaxButtonStringSize-1] = _T('\0');
if (::LoadString(hInstance, IDS_YES, szYes, MaxButtonStringSize) == 0)
{
_tcscpy(szYes, _T("&Yes"));
}
szYes[MaxButtonStringSize-1] = _T('\0');
if (::LoadString(hInstance, IDS_NO, szNo, MaxButtonStringSize) == 0)
{
_tcscpy(szNo, _T("&No"));
}
szNo[MaxButtonStringSize-1] = _T('\0');
if (::LoadString(hInstance, IDS_CONTINUE, szContinue, MaxButtonStringSize) == 0)
{
_tcscpy(szContinue, _T("&Continue"));
}
szContinue[MaxButtonStringSize-1] = _T('\0');
if (::LoadString(hInstance, IDS_DONOTASKAGAIN, szDoNotAskAgain, MaxButtonStringSize) == 0)
{
_tcscpy(szDoNotAskAgain, _T("Don't ask me again"));
}
szDoNotAskAgain[MaxButtonStringSize-1] = _T('\0');
if (::LoadString(hInstance, IDS_DONOTTELLAGAIN, szDoNotTellAgain, MaxButtonStringSize) == 0)
{
_tcscpy(szDoNotTellAgain, _T("Don't tell me again"));
}
szDoNotTellAgain[MaxButtonStringSize-1] = _T('\0');
if (::LoadString(hInstance, IDS_YESTOALL, szYesToAll, MaxButtonStringSize) == 0)
{
_tcscpy(szYesToAll, _T("Yes to &All"));
}
szYesToAll[MaxButtonStringSize-1] = _T('\0');
if (::LoadString(hInstance, IDS_NOTOALL, szNoToAll, MaxButtonStringSize) == 0)
{
_tcscpy(szNoToAll, _T("No to A&ll"));
}
szNoToAll[MaxButtonStringSize-1] = _T('\0');
}
///////////////////////////////////////////////////////////////////////////////
// MsgBoxDlgProc
BOOL CALLBACK CDialogTemplate::MsgBoxDlgProc(HWND hwnd,
UINT message,
WPARAM wParam,
LPARAM lParam)
{
CDialogTemplate* Me = reinterpret_cast<cdialogtemplate*>(::GetWindowLong(hwnd, GWL_USERDATA));
HWND hwndChild;
switch (message)
{
case WM_INITDIALOG:
{
::SetWindowLong(hwnd, GWL_USERDATA, lParam); // safe it for the others
Me = reinterpret_cast<cdialogtemplate*>(lParam);
HDC hdc = ::CreateDC(_T("DISPLAY"), NULL, NULL, NULL);
ASSERT(hdc);
::SelectObject(hdc, Me->m_hFont);
::DeleteDC(hdc);
UINT nID;
for (nID = FirstControlId; nID < Me->m_nMaxID; nID++)
{
hwndChild = ::GetDlgItem(hwnd, nID);
if (::IsWindow(hwndChild))
{
::SendMessage(hwndChild, WM_SETFONT, (WPARAM)Me->m_hFont, 0);
}
else
{
break;
}
}
for (nID = 1; nID < 18; nID++)
{
hwndChild = ::GetDlgItem(hwnd, nID);
if (hwndChild && ::IsWindow(hwndChild))
{
::SendMessage(hwndChild, WM_SETFONT, (WPARAM)Me->m_hFont, 0);
}
}
hwndChild = ::GetDlgItem(hwnd, IdExHelp);
if (hwndChild && ::IsWindow(hwndChild))
{
::SendMessage(hwndChild, WM_SETFONT, (WPARAM)Me->m_hFont, 0);
}
hwndChild = ::GetDlgItem(hwnd, IdDoNotAskAgian);
if (hwndChild && ::IsWindow(hwndChild))
{
::SendMessage(hwndChild, WM_SETFONT, (WPARAM)Me->m_hFont, 0);
CheckDlgButton(hwnd, IdDoNotAskAgian, 0);
}
hwndChild = ::GetDlgItem(hwnd, IdExReport);
if (hwndChild && ::IsWindow(hwndChild))
{
::SendMessage(hwndChild, WM_SETFONT, (WPARAM)Me->m_hFont, 0);
}
hwndChild = ::GetDlgItem(hwnd, Me->m_nDefId);
if (hwndChild && ::IsWindow(hwndChild))
{
::SetFocus(hwndChild);
}
// disable close button just like real MessageBox
if (!Me->Option(CancelOrOkButton))
{
EnableMenuItem(GetSystemMenu(hwnd, FALSE), SC_CLOSE, MF_GRAYED);
};
if (Me->m_hIcon)
{
HWND hwndIcon;
hwndIcon = ::GetDlgItem(hwnd, 1000);
::SetWindowLong(hwndIcon, GWL_WNDPROC, reinterpret_cast<long>(IconProc));
::SetWindowLong(hwndIcon, GWL_USERDATA, reinterpret_cast<long>(Me->m_hIcon));
}
return FALSE;
}
case WM_COMMAND:
{
switch(wParam)
{
case IDCLOSE:
return TRUE;
case IDCANCEL:
if (Me->Option(CancelButton))
{
::EndDialog(hwnd, IDCANCEL);
}
else if (Me->Option(OkButton))
{
::EndDialog(hwnd, IDOK);
}
return TRUE;
case IdExHelp:
{
_TCHAR szBuf[_MAX_PATH*2];
szBuf[0] = 0;
GetModuleFileName(NULL, szBuf, (sizeof(szBuf)/sizeof(szBuf[0])) - 1); //AfxGetInstanceHandle()
if (strlen(szBuf) > 0)
{
_TCHAR *cp = _tcsrchr(szBuf, _T('.'));
if (cp)
{
_tcscpy(cp, _T(".hlp"));
::WinHelp(hwnd, szBuf,
(Me->m_nHelpId == 0) ? HELP_PARTIALKEY : HELP_CONTEXT,
Me->m_nHelpId);
}
}
return FALSE;
}
case IdExReport:
{
//QOReportError(Me->MessageText());
QODumpStack(Me->MessageText());
return FALSE;
}
case IdDoNotAskAgian: //IdDoNotAskAgian & DoNotTellAgain share the same id!!
return FALSE;
default:
//if (wParam != IdExHelp && wParam != IdDoNotAskAgian)
{
hwndChild = ::GetDlgItem(hwnd, IdDoNotAskAgian);
BOOL bFlag = FALSE;
if (hwndChild && ::IsWindow(hwndChild))
{
bFlag = ::SendMessage(hwndChild, BM_GETCHECK, 0, 0);
}
if (Me->Option(DoNotAskAgain))
{
wParam |= bFlag ? MB_DONOTASKAGAIN : 0;
}
else if (Me->Option(DoNotTellAgain))
{
wParam |= bFlag ? MB_DONOTTELLAGAIN : 0;
}
::EndDialog(hwnd, wParam);
}
return FALSE;
}
}
}
return FALSE;
}
///////////////////////////////////////////////////////////////////////////////
// CDialogTemplate::AddItem
void CDialogTemplate::AddItem(CDialogItem::Econtroltype cType, UINT nID, RECT* prect, LPCTSTR pszCaption)
{
ASSERT(m_pDlgItemArray[m_dlgTempl.cdit] == NULL);
CDialogItem::Econtroltype ct = cType;
if (ct == CDialogItem::CHECKBOX)
{
ct = CDialogItem::BUTTON;
}
m_pDlgItemArray[m_dlgTempl.cdit] = new CDialogItem(ct);
ASSERT(m_pDlgItemArray[m_dlgTempl.cdit]);
m_pDlgItemArray[m_dlgTempl.cdit]->AddItem(*this, cType, nID, prect, pszCaption);
m_dlgTempl.cdit++;
ASSERT(m_dlgTempl.cdit < MaxItems);
}
///////////////////////////////////////////////////////////////////////////////
// CDialogTemplate::Display
int CDialogTemplate::Display(LPCTSTR lpszCaption)
{
// The first step is to allocate memory to define the dialog. The information to be
// stored in the allocated buffer is the following:
//
// 1. DLGTEMPLATE structure
// typedef struct
// {
// DWORD style;
// DWORD dwExtendedStyle;
// WORD cdit;
// short x;
// short y;
// short cx;
// short cy;
// } DLGTEMPLATE;
// 2. 0x0000 (Word) indicating the dialog has no menu
// 3. 0x0000 (Word) Let windows assign default class to the dialog
// 4. (Caption) Null terminated unicode string
// 5. 0x000B (size of the font to be used)
// 6. "MS Sans Serif" (name of the typeface to be used)
// 7. DLGITEMTEMPLATE structure for the button (HAS TO BE DWORD ALIGNED)
// typedef struct
// {
// DWORD style;
// DWORD dwExtendedStyle;
// short x;
// short y;
// short cx;
// short cy;
// WORD id;
// } DLGITEMTEMPLATE;
// 8. 0x0080 to indicate the control is a button
// 9. (Title). Unicode null terminated string with the caption
// 10. 0x0000 0 extra bytes of data for this control
// 11. DLGITEMTEMPLATE structure for the Static Text (HAS TO BE DWORD ALIGNED)
// 12. 0x0081 to indicate the control is static text
// 13. (Title). Unicode null terminated string with the text
// 14 0x0000. 0 extra bytes of data for this control
int rc = IDCANCEL;
_TCHAR szTitle[1024];
_tcsncpy(szTitle, lpszCaption, (sizeof(szTitle)/sizeof(szTitle[0]))-1);
szTitle[(sizeof(szTitle)/sizeof(szTitle[0]))-1] = _T('\0');
int nTitleLen = _tcslen(szTitle);
int i;
try // catch memory exceptions and don't worry about allocation failures
{
int nBufferSize = sizeof(DLGTEMPLATE) +
(2 * sizeof(WORD)) + // menu and class
((nTitleLen + 1) * sizeof(WCHAR));
// NOTE - font is set in MsgBoxDlgProc
nBufferSize = (nBufferSize + 3) & ~3; // adjust size to make
// first control DWORD aligned
// loop to calculate size of buffer we need -
// add size of each control:
// sizeof(DLGITEMTEMPLATE) +
// sizeof(WORD) + // atom value flag 0xFFFF
// sizeof(WORD) + // ordinal value of control's class
// sizeof(WORD) + // no. of bytes in creation data array
// sizeof title in WCHARs
for (i = 0; i < m_dlgTempl.cdit; i++)
{
int nItemLength = sizeof(DLGITEMTEMPLATE) + 3 * sizeof(WORD);
int nChars = _tcslen(m_pDlgItemArray[i]->m_szCaption) + 1;
nItemLength += nChars * sizeof(WCHAR);
if (i != m_dlgTempl.cdit - 1) // the last control does not need extra bytes
{
nItemLength = (nItemLength + 3) & ~3; // take into account gap
} // so next control is DWORD aligned
nBufferSize += nItemLength;
}
HLOCAL hLocal = LocalAlloc(LHND, nBufferSize);
if (hLocal == NULL)
{
throw TException("XMessageBox: Out of memory");
}
BYTE* pBuffer = (BYTE*)LocalLock(hLocal);
if (pBuffer == NULL)
{
LocalFree(hLocal);
throw TException("XMessageBox: Lock failed");
}
BYTE* pdest = pBuffer;
// transfer DLGTEMPLATE structure to the buffer
memcpy(pdest, &m_dlgTempl, sizeof(DLGTEMPLATE));
pdest += sizeof(DLGTEMPLATE);
*(WORD*)pdest = 0; // no menu
*(WORD*)(pdest + 1) = 0; // use default window class
pdest += 2 * sizeof(WORD);
// transfer title
WCHAR* pchCaption;
int nActualChars;
pchCaption = new WCHAR[nTitleLen + 1];
nActualChars = MultiByteToWideChar(CP_ACP, 0, szTitle, -1, pchCaption, nTitleLen + 1);
ASSERT(nActualChars > 0);
memcpy(pdest, pchCaption, nActualChars * sizeof(WCHAR));
pdest += nActualChars * sizeof(WCHAR);
delete pchCaption;
// will now transfer the information for each one of the item templates
for (i = 0; i < m_dlgTempl.cdit; i++)
{
pdest = (BYTE*)(((DWORD)pdest + 3) & ~3); // make the pointer DWORD aligned
memcpy(pdest, (void *)&m_pDlgItemArray[i]->m_dlgItemTemplate, sizeof(DLGITEMTEMPLATE));
pdest += sizeof(DLGITEMTEMPLATE);
*(WORD*)pdest = 0xFFFF; // indicating atom value
pdest += sizeof(WORD);
*(WORD*)pdest = (WORD)m_pDlgItemArray[i]->m_controltype; // atom value for the control
pdest += sizeof(WORD);
// transfer the caption even when it is an empty string
WCHAR* pchCaption;
int nChars = _tcslen(m_pDlgItemArray[i]->m_szCaption) + 1;
pchCaption = new WCHAR[nChars];
int nActualChars = MultiByteToWideChar(CP_ACP, 0,
m_pDlgItemArray[i]->m_szCaption, -1, pchCaption, nChars);
ASSERT(nActualChars > 0);
memcpy(pdest, pchCaption, nActualChars * sizeof(WCHAR));
pdest += nActualChars * sizeof(WCHAR);
delete pchCaption;
*(WORD*)pdest = 0; // How many bytes in data for control
pdest += sizeof(WORD);
}
ASSERT(pdest - pBuffer == nBufferSize); // just make sure we did not overrun the heap
HINSTANCE hInstance = GetModuleHandle(NULL);
rc = ::DialogBoxIndirectParam(hInstance, reinterpret_cast<lpdlgtemplate>(pBuffer),
m_hWnd, MsgBoxDlgProc, reinterpret_cast<lparam>(this));
LocalUnlock(hLocal);
LocalFree(hLocal);
}
catch (std::bad_alloc a) //I have not succeeded in testing this catch
{
::MessageBox(m_hWnd, _T("Memory allocation for dialog template failed."), a.what(),
MB_ICONEXCLAMATION | MB_OK);
rc = IDCANCEL;
}
return rc;
}
///////////////////////////////////////////////////////////////////////////////
// CDialogItem class
///////////////////////////////////////////////////////////////////////////////
// CDialogItem ctor
CDialogItem::CDialogItem(CDialogItem::Econtroltype ctrlType)
{
m_controltype = ctrlType;
}
///////////////////////////////////////////////////////////////////////////////
// CDialogItem::AddItem
void CDialogItem::AddItem(CDialogTemplate& dialog, Econtroltype ctrltype, UINT nID, RECT* prect, LPCTSTR lpszCaption)
{
short hidbu = HIWORD(GetDialogBaseUnits());
short lodbu = LOWORD(GetDialogBaseUnits());
// first fill in the type, location and size of the control
m_controltype = ctrltype;
if (m_controltype == CHECKBOX)
{
m_controltype = BUTTON;
}
if (prect != NULL)
{
m_dlgItemTemplate.x = (short)((prect->left * 4) / lodbu);
m_dlgItemTemplate.y = (short)((prect->top * 8) / hidbu);
m_dlgItemTemplate.cx = (short)((Width(*prect) * 4) / lodbu);
m_dlgItemTemplate.cy = (short)((Height(*prect) * 8) / hidbu);
}
else
{
m_dlgItemTemplate.x = 0;
m_dlgItemTemplate.y = 0;
m_dlgItemTemplate.cx = 10; // some useless default
m_dlgItemTemplate.cy = 10;
}
m_dlgItemTemplate.dwExtendedStyle = 0;
m_dlgItemTemplate.id = (WORD)nID;
switch (ctrltype)
{
case ICON:
m_dlgItemTemplate.style = WS_CHILD | SS_ICON | WS_VISIBLE;
break;
case BUTTON:
dialog.ButtonCount()++;
m_dlgItemTemplate.style = WS_VISIBLE | WS_CHILD | WS_TABSTOP;
if (dialog.ButtonCount() == dialog.DefaultButton())
{
m_dlgItemTemplate.style |= BS_DEFPUSHBUTTON;
dialog.DefaultButtonId(nID);
}
else
{
m_dlgItemTemplate.style |= BS_PUSHBUTTON;
}
break;
case CHECKBOX:
m_dlgItemTemplate.style = WS_VISIBLE | WS_CHILD | WS_TABSTOP | BS_AUTOCHECKBOX;
break;
case EDITCONTROL:
m_dlgItemTemplate.style = WS_CHILD | WS_VISIBLE | WS_TABSTOP | ES_MULTILINE | ES_LEFT;
break;
case STATICTEXT:
m_dlgItemTemplate.style = WS_CHILD | WS_VISIBLE | SS_LEFT;
break;
default:
ASSERT(FALSE); // should never get here
}
_tcsncpy(m_szCaption, (lpszCaption != NULL) ? lpszCaption : _T(""), (sizeof(m_szCaption)/sizeof(m_szCaption[0]))-1);
m_szCaption[(sizeof(m_szCaption)/sizeof(m_szCaption[0]))-1] = _T('\0');
}
<>
|
|
|
|
|
This code is very useful, but does not work with Unicode! You can't use sizeof() when passing a character count to string functions, because sizeof() always returns bytes. I've patched it up for my own use, but in the process I modified some code to take care of things like a caption > 1024 being passed in, so that's not good (but not a problem for me, because I know not to do it). The author should patch it up and post a new version.
Jim
|
|
|
|
|
So let's see ... you got the code for free, you apparently found it somewhat useful, you fixed some problems, and now instead of sharing your fixes you complain that the author should post his fixes? I'm glad not everyone using this web site has your attitude! At work do you say, "Well, I fixed it in my own code, but I'm not giving it to you, go fix it yourself."
If you check other submissions, you will find plenty of fixes posted in response to the original article. That's how this site keeps getting better. Give a little back.
|
|
|
|
|
Well, I suppose you have a point. But in my defense...
The author originally wrote this code to work with unicode. I did in fact point out what the problem was, so that he could fix it.
There's a lot of code here, and it's obvious that the author has put a lot of time in this, and he knows what he is doing. I am pointing out the problem to him, and deferring to him to update his code, which I think is the respectful thing to do.
If you yourself need this code to work in unicode, you probably, like me, figured out in a few minutes what changes need to be made. If not, then you have nothing to worry about.
(Also note that someone has since posted a substantial revision to the code, which revision fixes the problem I pointed out).
This is why I don't like posting on Internet boards anymore. While I would not say you guys exactly "flamed" me, it certainly came close.
JF
|
|
|
|
|
I just wanted to let you know that I appreciated your comments. As you found out, there are some people on this site who are very passionate about coding. So posting articles (and responses!) can be dangerous to your blood pressure.
The way I look at it, nearly all responses have some merit. So if your objective is to have peer review of your code, then Codeproject is #1 place on web. And yes, peer review will sometimes be harshly honest. But as someone else pointed out, it's free.
Thanks again, and please don't be discouraged by random negative comments. Let's share, make it better, and we all benefit.
Best wishes,
Hans
|
|
|
|
|
I agree with Sam Levy, this site is all about Coding, and if you arent apart of the solution, your just apart of the problem, lol
But. anyways it would been nice if you was to point out what u fixed, it would of been professional of you, because the user did post the articile to show of talent and also have others test so if their was problems they could also contribute their opinions/fixes/enhancements
|
|
|
|
|
This has been on my todo list for quite some time. Nice job!
I too get fed up with making dialogs to deal with odd messages and particularly like the "Don't ask/tell again" feature.
Anyhow I think seen as you have gone so far already it would be nice to make it so that the button text can be customised. eg.
int rc = ::XMessageBox(m_hWnd, strText, strCaption, MB_CUSTOM|nDefButton|nIconType|nDontAsk, nHelpID, IDS_BUTTON1, IDS_BUTTON2, IDS_BUTTON3);
This would then load the strings associated with IDS_BUTTONx and display a message box containing 3 buttons, each button could contain completely custom text. The function could return something like ID_BUTTON1 pressed, etc.
Apologies if your code already does this as the article seems to imply at the start, but I cannot find it and I really think this feature would make the messagebox so much more versatile.
Don't think there would be too many problems. Though I guess you would have to parse the button string for the ampersand to provide accelerator support.
Let me know your thoughts.
--
The Obliterator
|
|
|
|
|
I think this is good idea. Perhaps passing a pointer to an array of UINTs is better? I am trying to think of how to handle multiple languages. Would IDS_BUTTONx have same id, but load different string, according to language?
Another idea: maybe also custom icons.
One other thing I have seen in some Microsoft message boxes: one of the buttons (e.g., No button) will have a counter that counts down to 0. So you will see "No 10", and then "No 9", and so forth. In other words, when it reaches 0, No button will automatically be selected. Do you think there is any use for this?
Best wishes,
Hans
|
|
|
|
|
An array of UINTs is probably an improvement over my initial posting. I agree keeping it multilingual is a priority. I prefer your suggestion received via email, implementing both alternatives below would be ideal:
> Input an ID which would be a string resource of the form:
> IDS_TF_BUTTONS "&Word 6.0 Document\n&Rich Text Document\n&Text Document\nCancel"
> In this scheme, each button is separated by \n (there are four buttons). The return code is numbered from 1 to 4.
>
> An alternative to the above would be to pass an actual string instead of an ID.
Custom icons are definitely a good idea. Don't think I would use them personally, but I'm sure many people would.
Automatic timeout is a feature I sometimes use in my custom dialogs for unattended operations. I have a simple message at the bottom of the dialog counting down the time remaining, if the user clicks anywhere in the dialog the timer stops (message disappears) and the user must answer manually. I guess you could return MB_TIMEOUT or simply the default button choice otherwise.
There are some implementations of timeout messageboxes here on CP and also on codeguru.com. From memory all the solutions I examined had problems as they used real system messageboxes and patched close messages to them - sometimes closing the app by mistake. Others didn't show a countdown to the user or were simply buggy. With your messagebox I think it could be done much better. A custom string/ID could be passed containing a %d which would be a placeholder for the seconds remaining - this message could be formatted at the bottom of the message box every second.
I also like your message in the button idea as it shows the user which button will be selected. I guess both methods are viable and each have their own merits.
--
The Obliterator
|
|
|
|
|
Thank You for your code.
I have installed big-font (because of developerstudio, at 1280x1024 settings are unreadable) and with big-fonts the buttons in X-Message-Box are too small for it's content, Billie's Messagebox is here much bigger. Any tips?
|
|
|
|
|
This is something I would like to do. Basically the buttons should be sized dynamically, according to selected font. For a quick fix, you could increase MSGBOXEX_BUTTONWIDTH to suit font you are using. Let me know if this works for you.
Best wishes,
Hans
|
|
|
|
|
checkboxrect.bottom changes with system font size but
it is not simple
MSGBOXEX_BUTTONHEIGHT = b - p + 10
as i thought
t!
|
|
|
|
|
It would be better to have checkbox above buttons or to have an option. User clicks the checkbox before clicking on button.
-Sergei
|
|
|
|
|
I agree with what you say. I think I did it this way because I see other message dialogs do it like this. There is no reason checkbox could not be above buttons like you say.
Best wishes,
Hans
|
|
|
|
|
Hi Hans,
A nice addition would be to include flags for "yes to all" and "no to all" buttons.
|
|
|
|
|
Those are good idea. I am adding them now. Thank you!
Best wishes,
Hans
|
|
|
|
|
This is kewl!
Something which I have been looking for!
How about including some of these combinations?
"Skip", "Skip All" and "Cancel"
"Ignore", "Ignore All" and "Cancel"
I'll look into it myself soon, just thought I ask!
Btw, I had problem with re-definition of CDialogTemplate class when I used it with
#include <afxpriv.h> in the stdafx.hsimply rename the class resolve the problem.
|
|
|
|
|
Ok I decide to take effort to do it - so it's done!
Thanks to Hans Dietrich for the XMessageBox(...)
I also added a XMessageBoxEx(...) which allows
customizing of the buttons ID and Text/Caption of up
to 9 buttons. The customize buttons allow various
ID combination (eg IDOK, IDYESTOALL, IDCANCEL, IDRETRY, etc.) and any order with whatever
texts/captions (eg IDOK button can have caption "I Accept"), or define your own button ID (eg #define IDAREYOUSURE 27).
At the same time XMessageBoxEx(...) can used in place of the exist XMessageBox(...).
I hope to look at the customized Icon if time permit.
Btw, Hans Dietrich can I pass the modified code for you for updating or should I be sending it to some1 else?
ps: B4 some1 decided to start telling me off on the repeat issue about needing to rename the CDialogTemplate, well I missed it the first round of look!
Bye 4 now!
Shane L
|
|
|
|
|
Your project is interesting, but unlike what you say in your title, it is not exactly the same flavor than MS' MessageBox.
Among the differences I noted there are:
- No localization. The button Display Windows MessageBox shows eg. "Yes No Cancel" on an English Windows, but "Oui Non Annuler" on a French one. Automatically.
Perhaps you can fetch these localized strings (I don't know how) but of course, the strings you added will remain fixed.
- No sound. On my system, I have a different sound played, depending on the displayed icon. See the Sound Properties applet in the Control Panel for more details. This one seems easy to implement.
Thank you for sharing.
--._.·´¯`·._.·´¯`·._.·´¯`·._.·´¯`·._.·´¯`·._.·´¯`·._.--
Philippe Lhoste (Paris -- France)
Professional programmer and amateur artist
http://jove.prohosting.com/~philho/
--´¯`·._.·´¯`·._.·´¯`·._.·´¯`·._.·´¯`·._.·´¯`·._.·´¯`--
|
|
|
|
|
I agree with Philippe. And there is another incompatibility - the Esc key doesn't work in XMessageBox with the MB_OK flag while works in the standard MessageBox.
-Sergei
|
|
|
|
|