Introduction
It is a common problem that we want to use Common Dialogs (like the Font dialog), but we do not want to allow users to select all the properties offered by the dialogs. This article shows how we can do that, with the font dialog as an example.
Background
I definitely needed a font dialog without the font size property, for a drawing application which manages font size automatically, so we do not want users to be able to alter it. There are two options to select from:
- Rewriting the whole Font dialog.
- Hacking it.
Since it offers a nice Font preview, I decided to take the second approach.
Hacking
If we have the handle of the window to be hacked, the whole problem is very easy. A few native function calls should be added, like:
[DllImport("user32.dll")]
static extern IntPtr GetDlgItem(IntPtr hDlg, int nIDDlgItem);
[DllImport("user32.dll")]
static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
const int SW_HIDE = 0
Each UI component of a Common Dialog has a fixed ID. Spy++ can help us identify them. In the case of Font Size, these are 1090 and 1138; so, we can hide the unwanted components by calling:
ShowWindow(GetDlgItem(dialogHandle, 1090), SW_HIDE);
ShowWindow(GetDlgItem(dialogHandle, 1138), SW_HIDE);
Finding the dialog handle
The key problem is identifying the dialogHandle
. The sample shows three possible solutions, two of which are popular but have their own problems.
FindWindow
: Let’s find the font dialog after it is shown, using a FindWindow()
native call. Before showing the dialog, a timer is started, which finishes if the dialog is found. This approach has the following problems:
- What is the right interval of the timer? If a large amount of time is selected, users can see the unwanted UI elements which later disappear.
FindWindow
takes the title of the window as an input parameter. If Windows isn’t English, the title of the Font dialog isn’t "Font".
- If it cannot find the right dialog, it will continue running till eternity.
GetActiveWindow
: Let us suppose that the active window is our Font dialog; again, start a timer before showing it, and hope that the font dialog will be the active window when the timer first ticks. Again, the right timer interval is unknown, and if by any chance our Font dialog is not the topmost, we send commands to another application.
- Best solution: let’s take the approach described in this article: Customizing OpenFileDialog in .NET.
I removed all the unneeded code from it to get a simple solution to our problem. The FontDialogEx
class is to be used instead of the FontDialog
. Its ShowDialog
method creates a dummy, invisible form named DummyForm
. Invisibility is achieved by setting a flag named UFLAGSHIDE
. DummyForm
waits until it gets activated, this is to be found in its overwritten WndProc(ref Message m)
function.
protected override void WndProc(ref Message m)
{
if (mWatchForActivate && m.Msg == (int)Win32.WM_ACTIVATE)
{
mWatchForActivate = false;
mFontDialogHandle = m.LParam;
mNativeDialog = new FontDialogNative(m.LParam, mFileDialogEx);
}
base.WndProc(ref m);
}
This is the place where we can find the handle of the Font dialog. FontDialogNative
is created after the right handle is found, so our unneeded UI elements can be hidden in the constructor of FontDialogNative
.
History
This is the first version of Font Dialog hacking.