Win32 custom message box - windows

I want to make a custom message box. What I want to customize is the button's text.
MessageBoxW(
NULL,
L"Target folder already exists. Do you want to overwrite the folder?",
L"No title",
MB_YESNOCANCEL | MB_ICONQUESTION
);
I'd like to just change the buttons text to Overwrite, Skip, Cancel.
What's the most simple way?
I have to make this as having same look and feel with Windows default messagebox.

As said by others, a typical way is to create a dialog resource and have a completely independent dialog, which GUI you need to design in the way that it looks like standard dialog (to meet your request for feel and look). If you want to accept text messages, you might probably need to add code which resizes the window appropriately.
Still, there is another option for those who feel like diving into advanced things. While MessageBox API does not offer much for fint tuning, you still have SetWindowsHookEx in your hands. Having registgered the hook, you can intercept standard MessageBox window procedure and subclass it in the way you like.
Typical things include:
changing button text
adding more controls
adding timed automatic close
Hooking standard window can do all of those.
UPD. Hey, I realized I have some code with SetWindowsHookEx to share: http://alax.info/blog/127

You could create an own dialog. Or you could use a window hook as described in this article.
An archived version of the article can be found on web.archive.com.

Make a dialog resource (with a GUI editor, or by hand) and call DialogBox on it. There's no way to alter MessageBox behaviour, other than what's supported by its arguments.
That said, your message box can very well use stock Yes/No options.

The task dialog functionality introduced in Vista does exactly what you want and follows the prevailing system theme. However, if you have to support XP, then this will be of little comfort to you.

I know this question is old, but I just stumbled upon it now.
I would like to expand the other answers in regards to using a TaskDialog instead of a MessageBox. Here's a concise example of using a TaskDialog to do precisely what was asked; change the button's texts:
const TASKDIALOG_BUTTON buttons[] = { {IDYES, L"Overwrite"}, {IDNO, L"Skip"}, {IDCANCEL, L"Cancel"} };
TASKDIALOGCONFIG taskDialogConfig = {
.cbSize = sizeof(TASKDIALOGCONFIG),
.pszMainIcon = TD_WARNING_ICON, // TaskDialog does not support a question icon; see below
.pButtons = buttons,
.cButtons = ARRAYSIZE(buttons),
.pszWindowTitle = L"No title",
.pszContent = L"Target folder already exists. Do you want to overwrite the folder?"
};
TaskDialogIndirect(&taskDialogConfig, NULL, NULL, NULL);
Some noteworthy things:
You need to use TaskDialogIndirect, not the basic TaskDialog function
when not specifying a parent window, the icon specified in pszMainIcon is displayed in the title bar as well
There is no equivalent to the MessageBox's MB_ICONQUESTION, quoting a quote from this forumpost: Don't use the question mark icon to ask questions. Again, use the question mark icon only for Help entry points. There is no need to ask questions using the question mark icon anyway—it's sufficient to present a main instruction as a question.
checking which button was selected would have to be done by passing a pointer to an int as the second argument of TaskDialogIndirect and checking its value on return (the documentation should be pretty clear)

Here is a small open source library that allows you to customize Message Boxes. Developed by Hans Ditrich.
I have successfully used it in another POC that allows embedding a custom icon in such MessageBox that can be called even from a Console application.
I should also point to the Task Dialog. Here is an example of using it:
int nButtonPressed = 0;
TaskDialog(NULL, hInst,
MAKEINTRESOURCE(IDS_APPLICATION_TITLE),
MAKEINTRESOURCE(IDS_DOSOMETHING),
MAKEINTRESOURCE(IDS_SOMECONTENT),
TDCBF_OK_BUTTON | TDCBF_CANCEL_BUTTON,
TD_WARNING_ICON,
&nButtonPressed);
if (IDOK == nButtonPressed)
{
// OK button pressed
}
else if (IDCANCEL == nButtonPressed)
{
// Cancel pressed
}

Related

How to add several panels to script palette in Digital Micrograph GMS 2.x

I'm wondering how to add several UI panels to a scripting palette in GMS 2.x. I found some commands in Gatan's outdated documentation (http://digitalmicrograph-scripting.tavernmaker.de/other%20resources/Old-DMHelp/FloatingPalettes.htm) but most of them are not found in GMS 2.32.
Given a UI class "UI_class" (including an init() function that creates the interface) I've already successfully registered the script palette and opened the corresponding gadget using:
object UI=Alloc(UI_class).init()
RegisterScriptPalette(UI,"Type","Display")
OpenGadgetPanel("Display")
Unfortunately, the command GadgetWindowAddGadgetPanel() which seemed to be the most promising to me in the first instance in order to add a second UI to the same palette cannot be found.
Does anybody know a working equivalent?
Thank you in advance for sharing your experience, I'm looking forward to see some nicely designed floating palettes!
I'm not 100% sure I understood the question correctly. Each UI derived object forms it's own "dialog", which can be displayed either as
Modal Dialog - using Pose( DlgObj )
Modeless Dialog - using Display( DlgObj, "name" )
Gadget panel - by first 'registering' it and then displaying a registed dialog
If you have multiple gadget of same width, you can add them together by drag-and drop. This is not part of the dialog - just part of how the UI interaction with dialogs. ( You drag a palette to the side of the screen to attach it, and then you can dragg other palettes on top of it. )
|
|
Example script to produce some dialogs:
Object CreateDlgObj()
{
TagGroup DialogTG = DLGCreateDialog("My Dialog")
TagGroup StrFieldTG = DLGCreateStringField("Some string",20)
DialogTG.DLGAddElement(StrFieldTG)
Object DialogObj = Alloc(UIFrame)
DialogOBJ.Init(DialogTG)
return DialogObj
}
// MAIN
// Show dialog as modal dialog
// CreateDLGObj().Pose()
// Show dialog as modeless dialog
// CreateDLGObj().Display( "NewName" )
// Register dialog as gadget and then display
// RegisterScriptPalette( CreateDLGObj(),"", "RegDlg" )
// OpenGadgetPanel( "RegDlg" )
// Create, register and display multiple dialogs as palettes
number nMax = 2
For( number i =0; i<nMax; i++)
{
string name = "MyDlg_" + i
RegisterScriptPalette( CreateDLGObj(),"", name )
OpenGadgetPanel( name )
}
Note that "Gadget panels" or "tool palettes" or whatever you call them are different in GMS 1.x, GMS 2.x and GMS 3.x as it is always the "main program" which decides how to handle the dialogs. GMS 2.x is the only version, where the program "remembers" the layout of multiple such palettes. These layouts can be saved and loaded. GMS 3 no longer has this, as all UI palettes are fixed in position. (Old custom dialogs still appear, but in a somewhat 'out-of-style' way.
GMS 2.x's "Floating Window Layout" tool:
The "layout" information stored with this tool is actually stored in the Windows registry.
But I have never used that so far. (And it also only applies to GMS 2.x.)
It is also worth mentioning, that RegisterScriptPalette does exactly what is says: It adds the UI permanently to the list of palettes the appliction knows of.
So running the script twice, you get two identical palettes. You usually don't want that! Just use OpenGadgetPanel to show any already registered palette.
However, 'registered' palettes will only remain 'registered' as long as the application is running, so they are gone at restart of DM.
If you want somethng more persistent, you need to install the code as a Library via the File menu. Note, that any executeable part of the code will be automatically run on startup. So you want to have the RegisterScriptPalette be part of that code, but not the OpenGadgetPanel.

Getting the menu handle

I created menus and sub menus using the resource editor in visual studio. I now want to add items to one of the menus during run time. I was going to use the InsertMenuItem function but I don't know how to get access to the HMENU variable.
LoadMenu seems is what you need. Use it to load menu from resource editor, something like this:
HMENU yourMenu = LoadMenu( hInst, // variable where you stored your HINSTANCE
MAKEINTRESOURCE(IDM_MENU1) ); // replace IDM_MENU1 with the ID of your menu
Here are lots of useful examples, you may find very useful. Some of them address your issue, and some might be useful to you in the future. I would study the Example of Menu-Item Bitmaps section if I were you...
If you need a menu handle that is already assigned to a window then use GetMenu as member arx said. Something like this:
HMENU yourMenu = GetMenu(hWnd); // hWnd is the HWND of the window that owns your menu
Do not forget to destroy the menu when it is no longer needed ( usually upon window destruction ) with DestroyMenu.
This example might help you as well. This is very good introductory tutorial for Win32, I suggest you to read it ( just go to the home page and download both PDF and .zip file with code examples ).
As I have said before, your question is not entirely clear, so if you have further questions leave me a comment.
Hopefully this answer solved your problems. Best regards.

How to simulate modal dialog UI behaviour in a modeless dialog?

Is there an easy way to display a dialog modelessly while retaining the UI blocking a modal dialog provides?
I want to stop a user interacting with other dialogs/controls when the dialog is shown, but let the application carry on running. Is there a way to set a dialog as "exclusive focus" or something like that?
No, there is no easy way to do what you want.
If you really want to go the route you describe, I recommend first reading the whole 'modality' series on Raymond Chen's blog. First installment is on http://blogs.msdn.com/b/oldnewthing/archive/2005/02/18/376080.aspx .
However, this seems like an instance of the XY problem. What is it that you are trying to do? Get the main application to keep updating itself? If so, I think (with the information we are given) that calling AfxPumpMessage() will do what you want. Or do you want to continue processing data in the main application? Then you'll save yourself a world of hurt by using a worker thread.
Untested, but you can try do disable the owner window (the app. main window), create a modeless dialog, and then, when the dialog is closed, enable it again:
To disable the main window:
AfxGetMainWnd()->EnableWindow(FALSE);
To create the modal/non-blocking dialog:
dlg->Create(resId)
And to enable it again, on the OnClose event, or similar:
AfxGetMainWnd()->EnableWindow(TRUE);
There may be other details in a modal dialog that I'm not aware of. If you are willing to investigate, read the source code of MFC's CDialog::DoModal(). If I remember correctly, this MFC function simulates a modal-blocking dialog using the modeless Win32 API CreateDialog*() in order to implement global accelerators, hooks, messages and the like.
Here is possible answer to your question:
You could disable all the other controls in the application then re-enable them after the dialog has finished.
Use this callback
BOOL CALLBACK EnableDisableAllChildren ( HWND hwnd, LPARAM lp )
{
::EnableWindow ( hwnd, (BOOL)lp );
return TRUE;
}
With a Call to
EnumChildWindows ( HWNDToYourApp, EnableDisableAllChildren, true );
Do Modaless Dialog
EnumChildWindows ( HWNDToYourApp, EnableDisableAllChildren, false );
Something different to think about.

How do I let the user copy arbitrary text?

I have an app, where I generate text (about 500 characters), and I would like the user to have some means of copying that text for use outside of the application.
I don't want to use any capabilities for this app (like web, or contacts).
Here's what I've tried (and why it's failed)
TextBox. IsReadOnly = true; SelectAll();
Can't SelectAll a read only text box
Turn off read only, hide the SIP
Can't hide the SIP on a (non-read-only) TextBox that the user is interacting with (I want to enable the user to copy, so needs to interact with the control)
allow edits, show sip, SelectAll()
The "copy" icon doesn't appear unless the user chose to select text
On selection changed (actually changed), SelectAll()
The "copy" icon doesn't appear unless the user selected the text? The copy icon appears erratically, nothing I would call an acceptable user experience.
So at this point, I'm quite far from what I want in a user experience, and I still don't have anything that works. Any suggestions?
Some other possible ways to answer my question include:
"How do I force the copy button to appear above text I programatically selected?"
"How do I change the selection behavior of a tap in a text box?"
Afaik there are some limitations to the Windows Phone 7 Clipboard:
Works only in TextBox and can only copy text upon users wish
Text is only kept until device gets locked. If your device gets locked, the clipboard will be wiped clean
Even if you try Clipboard.SetText Method, you will notice the SecurityException if you call this method without the users interaction. This is to keep the users data under control so that no rogue app can copy unrecognized Text.
But you could try Matt Laceys WP7Clipboard. It saves the clipboard content inside an image and can even copy bitmaps.
Try restyling the textbox as per http://mobileworld.appamundi.com/blogs/peterfoot/archive/2011/02/08/copyable-textblock-for-windows-phone.aspx
Here's what I eventually got mostly working
private void Export(StackPanel stacker)
{
var exportHeader = new TextBlock();
exportHeader.Text = "Export";
stacker.Children.Add(exportHeader);
var exportBox = new TextBox();
stacker.Children.Add(exportBox);
//exportBox.IsReadOnly = true; // hides SIP, but causes an exception with SelectAll() (pre-Mango, I haven't tried on Mango yet)
exportBox.FontSize = 1;
exportBox.Text = textToExport;
exportBox.GotFocus += new System.Windows.RoutedEventHandler((send, ev) =>
{
((TextBox)send).SelectAll();
});
exportBox.Focus();
}
Apparently, making the font size 1 makes the difference here, maybe because all of the text can appear on the screen at once? Who knows.
I accepted this answer, because no one else posted a better solution. I would appreciate a better solution. If you can get the SIP to go away, that would be awesome.

Big problems with MFC/WinAPI

I need to create a SDI form with a formview that has two tabs, which encapsulate multiple dialogs as the tab content. But the form has to have a colored background.
And things like these makes me hate programming.
First, I tried CTabControl, via resource editor, tried different things, but the undocumented behavior and the quirks with no answers led me into a roadblock.
After many hours of searching, I found that there is a control called property sheet, which is actually what I need.
Some more searching later, I found that property sheet can even be actually embedded onto CFormView like so: http://www.codeguru.com/Cpp/controls/propertysheet/article.php/c591
And that the dialog classes derived from CPropertyPage can be directly added as pages via AddPage method of CPropertySheet.
Great! Not quite so... Some of the controls didn't worked, and were not created, ran into weird asserts. Turns out the DS_CONTROL style was missing from the dialogs. Found it completely accidentaly on Link, no word about that on MSDN!!!! Property page must have: DS_3DLOOK | DS_CONTROL | WS_CHILD | WS_TABSTOP, and can have: DS_SHELLFONT | DS_LOCALEDIT | WS_CLIPCHILDREN styles! Not any other, which are created by default with resource editor. Sweet, super hidden information for software developers!
The quote in comments on that page: "OMG. That's where that behavior came from...
It turns out that the PlaySound API relied on that behavior when playing sounds on 64bit machines." by Larry Osterman, who as I understand works for Microsoft for 20 years, got me laughing out loud.
Anyway, fixed that, the dialog-controls(CPropertyPages) are created as expected now, and that part looks something remotely promising, but the next part with color is dead end again!
Normally you override WM_CTLCOLOR, check for control ID or hwnd and supply the necessary brush to set the color you need. Not quite so with CPropertySheet, the whole top row stays gray! For CTabCtrl it somehow works, for CPropertySheet it doesn't.
Why? Seems that the CPropertySheet is kinda embedded inside CTabControl or something, because if I override WM_ERASEBKGND, only the internal part changes the color.
Now it seems that there is a GetTabControl() method in the CPropertySheet, that returns the actual CTabCtrl* of the CPropertySheet. But since it's constructed internally, I can't find how to override it's WM_CTLCOLOR message processing.
There seems to be a way to subclass the windowproc, but after multiple tries I can't find any good source on how to do it. SubclassWindow doc on MSDN says: "The window must not already be attached to an MFC object when this function is called."?! What's that?
I tried creating a custom CCustomTabCtrl class based on CTabCtrl via MFC wizard, created an instance of it, called SubclassWindow from one of the CCustomPropertySheet handlers to override the internal CTabCtrl, but nothing works, mystical crashes deep inside MFC.
Tried setting WindowLong with GCL_HBRBACKGROUND for the internal CTabCtrl, nothing changed.
And worst of all, I can't find any sort of useful documentation or tutorials on the topic.
Most I can find is how to ownerdraw the tab control, but this is seriously wrong on so many ways, I want a standard control behavior minus background color, I don't want to support different color schemes, windows versions, IAccesible interfaces and all this stuff, and none of the ownerdraw samples I've seen can get even 10% of all the standard control behavior right. I have no illusion that I will create something better, I wont with the resources at hand.
I stumbled upon this thread, and I can't agree with the author more: http://arstechnica.com/civis/viewtopic.php?f=20&t=169886&sid=aad002424e80121e514548d428cf09c6 owner draw controls are undocumented PITA, that are impossible to do right, and there is NULL information on MSDN to help.
So is there anything I have missed or haven't tried yet? How to change the top strip background color of the CPropertySheet? Anyone?
Your only option is to ownerdraw the tab control. It's not that hard. Well, it is frustrating because MFC doesn't tell you how to make the necessary Win32 calls.
In your CPropertySheet-derived class, overwrite OnInitDialog() and add:
GetTabControl()->ModifyStyle(0,TCS_OWNERDRAWFIXED);
This puts your CPropertySheet-derived class in charge of drawing the tab control. Add a handler for WM_DRAWITEM (OnDrawItem) and change backgroundColor and textColor to match whatever colors you wanted. Code for OnDrawItem follows:
void CPropSht::OnDrawItem(int nIDCtl, LPDRAWITEMSTRUCT lpDrawItemStruct)
{
if (ODT_TAB != lpDrawItemStruct->CtlType)
{
CPropertySheet::OnDrawItem(nIDCtl, lpDrawItemStruct);
return;
}
// prepare to draw the tab control
COLORREF backgroundColor = RGB(0,255,0);
COLORREF textColor = RGB(0,0,255);
CTabCtrl *c_Tab = GetTabControl();
// Get the current tab item text.
TCHAR buffer[256] = {0};
TC_ITEM tcItem;
tcItem.pszText = buffer;
tcItem.cchTextMax = 256;
tcItem.mask = TCIF_TEXT;
if (!c_Tab->GetItem(c_Tab->GetCurSel(), &tcItem )) return;
// draw it
CDC aDC;
aDC.Attach(lpDrawItemStruct->hDC);
int nSavedDC = aDC.SaveDC();
CBrush newBrush;
newBrush.CreateSolidBrush(backgroundColor);
aDC.SelectObject(&newBrush);
aDC.FillRect(&lpDrawItemStruct->rcItem, &newBrush);
aDC.SetBkMode(TRANSPARENT);
aDC.SetTextColor(textColor);
aDC.DrawText(tcItem.pszText, &lpDrawItemStruct->rcItem, DT_CENTER|DT_VCENTER|DT_SINGLELINE);
aDC.RestoreDC(nSavedDC);
aDC.Detach();
}
Thank you for this solution but...
The above solution works well with one tab, but when you have multiple tabs it seems rename the wrong tabs. I needed to change the if statement for GetItem to:
if (!c_Tab->GetItem(lpDrawItemStruct->itemID, &tcItem )) return;
Needed lpDrawItemStruct->itemID to get the tabs named correctly

Resources