Can you change the default messageDlg option without creating a custom dialog? - user-interface

I am displaying a messageDlg confirmation screen with Yes and No buttons. Reversing the buttons in the code has no affect.
answer := messageDlg('Are you sure?', mtConfirmation, [mbYes, mbNO], 0);
Is there a way to change the default option to No without creating a custom dialog such as demonstrated here What is the best way in Delphi to show customized Message Dialogs?

The MessageDlg function already supports specifying the default button:
MessageDlg('Are you sure?', mtConfirmation, [mbYes, mbNo], 0, mbNo);
^^^^
(Reversing the order of mbYes and mbNo couldn't possibly have any effect, since [mbYes, mbNo] is a set, and sets have no order.)

Related

WIN32: Duplicate Standard Button Control (disabled Icon / hotkey underline) in Owner Draw Button?

So for the simple feat of wanting to put an icon on the right side of button text instead of the left resulted in having to use owner draw buttons (but someone here said Custom Draw is actually available if using visual themes). Okay, fine, but now I'm finding you can't really duplicate what Windows standard buttons do when it's not in owner draw mode.
For a normal enabled button I can get the look correct by checking if visual styles are available or not and then using either the DrawThemeBackground() / DrawThemeText() or DrawFrameControl() / DrawText(). However the hot key underline character is shown even when alt key is not pressed, the default buttons don't show it until alt pressed.
For a disabled button, I can't duplicate the disabled look of the icon placed on the button. I tried DrawState() over DrawIconEx() but that looks like the old Windows 3.1 type grey graphic not the visual style dimmed graphic. I see there is a DrawThemeIcon() for an image list, I guess I could try that (I'd have to test non visual style mode to see if DrawState() matches when not using visual styles).
Also, as you hover over the button, the state doesn't change, I understand that if using owner draw, that doesn't occur, maybe it would still work with Custom Draw?
So the two main questions are:
1 - Is there something built-in to the button / owner draw to handle the underlined hotkey only when alt was pressed?
Update to Question 1: I found DT_HIDEPREFIX in DrawText() and using Custom Draw there is the CDIS_SHOWKEYBOARDCUES flag. However with Owner Draw I'm not sure if there is a flag someplace?
2 - How do you draw an icon for a button that is disabled to match what default buttons do?
TIA!!
For shortcut underline you can use WM_QUERYUISTATE to ask if it should be hidden or visible
DWORD draw_text_flags = ...;
if ( SendMessage( control_hwnd, WM_QUERYUISTATE, 0, 0 ) & UISF_HIDEACCEL ) != 0 )
{
// hide prefix
draw_text_flags |= DT_HIDEPREFIX;
}
// some combination of PBS_DEFAULTED, PBS_DISABLED, PBS_HOT, PBS_NORMAL, PBS_PRESSED;
int state = ...;
DrawThemeText( theme, hdc, BP_PUSHBUTTON, state, text, text_len, draw_text_flags, 0, rect );
Answer to Q2: If you create an HIMAGELIST using ILC_COLOR32 | ILC_MASK, and use ILD_NORMAL|ILD_BLEND25 on ImageList_Draw() it gives the same look as windows default buttons for a disabled button.
Based on responses from #Remy-Lebeau and #Daniel-Sęk and reviewing various projects on CodeProject, I create an easy to use class to handle this. It can be found at CodeProject. Thanks Guys.

Selecting the text within a TStringGrid cell

The component I use is a descendant of TStringGrid, called TDataGrid (which can be found on Torry). Unfortunately, it has a small bug which doesn't seem to be present in the original TStringGrid component. I have the options goEditing and goAlwaysShowEditor both enabled, so I'm expecting the text of a cell to be selected when the control receives input focus. But in the case of TDataGrid this doesn't happen if I press TAB to move from one control into the grid control. It receives input focus, but nothing is selected, and the caret isn't even visible. Obviously, this is very misleading for a user.
The original TStringGrid component has similar inconsistencies, where if you have goAlwaysShowEditor enabled, there is always one cell with its text "exposed" (focused, sort of, even if the grid control itself doesn't have focus), so if you left-click in that cell, it also won't select the text, just enable a caret. I have been able to get around this however, by simply disabling goAlwaysShowEditor when the grid control doesn't have focus and subsequently enabling it when the grid receives focus.
So does a TStringGrid have any way of selecting the text of a cell? I know how to set the focus to a cell, but I haven't figured out a way to actually select the text. Really would appreciate any ideas to get around this!
FWIW, I'm using Delphi 10.3
I've found one solution that actually seems to work, but it only handles the very specific scenario when you TAB into the grid control and there may be other cases that need to be handled..
So, controlling the selection of the text within a cell IS possible, but it will not work if you try to control it during the OnEnter event. So instead, I've had to resort to checking for TAB in the OnKeyUp event, where it will work.
You need to expose protected members of a TStringGrid to access the appropriate methods. It's not the prettiest solution, but it works at least...
type TStringGridHelper = class helper for TStringGrid
procedure HHideEditor;
procedure HShowEditor;
end;
procedure TDataGridHelper.HHideEditor;
begin
HideEditor;
end;
procedure TDataGridHelper.HShowEditor;
begin
ShowEditor;
end;
And in the OnKeyUp event handler...
if Key = VK_TAB then begin
StringGrid.HHideEditor;
StringGrid.HShowEditor;
end;

Is there any way to activate the Apply button from a print propery page with PrintDlgEx?

I have custom print property sheets/pages that have been added to the dialog displayed by PrintDlgEx. These property sheets are, of course, used to change additional options. The issue is that there does not appear to be any documented way to activate the Apply button from the property sheet's dialog function, or anywhere for that matter. This seems to be a huge omission on Microsoft's part.
Is there any "official" way to change the Apply button's state? If not, are there any possible workarounds?
Is there any "official" way to change the Apply button's state? If not, are there any possible workarounds?
Not directly, no. You would have to retrieve the button's HWND manually an then manipulate it as needed.
use SetWindowHookEx() to install a local WH_CBT hook for the thread that is calling PrintDlgEx(). The dialog's HWND will be available as a parameter of the callback function when it receives a HCBT_ACTIVATE notification. Then you can locate the Apply button's HWND within the dialog (use Spy++ or similar tool to get details about the button, then have your code use GetDlgItem() or FindWindowEx() to get the button's HWND). Be sure to call UnhookWindowsHookEx() after PrintDlgEx() exits (or at least after you are done using the button HWND).
use SetWinEventHook() to register for EVENT_OBJECT_CREATE, EVENT_OBJECT_SHOW, and/or EVENT_SYSTEM_DIALOGSTART notification(s) for the thread that is calling PrintDlgEx(). The dialog and button HWNDs will be available as a parameter of the callback function. Be sure to call UnhookWinEvent() after PrintDlgEx() exits (or at least after you are done using the button HWND).
Once you have the button's HWND, you can do whatever you want with it. It is a standard button control, so any standard button message/function can be used with it.
A more close to "official" way is to call PropSheet_Changed().
The way I get the property sheet dialog is to look at the source of PSN_ notifications sent to IPrintDialogCallback::HandleMessage(). Or you can use GetParent(GetParent(generalDialog)).
Once you call PropSheet_Changed() the Apply button will activate.
You're right, it seems to be a huge omission on Microsoft's part, as it's not a simple thing to code, but it's something that most people adding property sheets would need.
I can put some code up if anyone needs it.

oddity with edit controls in property sheet

I have a property sheet with several pages. Most of the pages have one or more edit controls.
Most controls are initialized not from the page dialogs but from the dialog that created the property sheet; some however are initialized in the page dialogs and they behave the same.
Everything starts out fine. One can move between the pages. None of the controls have the input focus.
If one clicks on one of the edit controls in a property sheet page establishing input focus one can modify the control. Again all seems in order.
If one then moves to a different property page, the first edit control in that page gets the input focus AND all the text in that control gets selected! This behavior applies to all the pages except one having an edit control with read only style. After that one can move back to other pages and the initial nothing selected no input focus behavior is restored.
All of the pages handle the PSN_QUERYINITIALFOCUS notification and return zero through the SetWindowLong mechanism.
Is this expected behavior?
And why isn't some control given focus initially?
My primary interest here is to somehow kill the selection. I have tried killing the selection with EM_SETSEL in the PSN_SETACTIVE notification to no avail.
The MSDN says the following under PSN_QUERYINITIALFOCUS "Otherwise, return zero and focus will go to the default control." How do I go about setting a control as default?
I find the the actions described above bizarre! I would still like to know
if they are normal.
why no control receives the focus initially.
I was able to kill the selection by adding code to the property sheet pages to handle the WM_COMMAND/EN_SETFOCUS message for any edit controls. I do not know if other controls
send EN_SETFOCUS messages.
case EN_SETFOCUS:
{
char cn[16];
HWND H = (HWND) lParam;
GetClassName (H, cn, 15);
if (strcmp (cn,"Edit") == 0)
{
SendMessage (H, EM_SETSEL, -1, 0);
}
return true;
}
I presume it would be possible to save any selection in an EN_KILLFOCUS handler and restore it
in the EN_SETFOCUS handler but doing so for an unknown number of controls would be tedious.

Win32 custom message box

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
}

Resources