Introduction
The common dialog boxes on Windows are sufficient for most users. However there are always requirements where these dialog boxes need customization. I wanted users to choose not only color but specify the alpha values from the ColorDialog
box. I customized the default ColorDialog
box using Win32 API. There are some hacks, some dirty C# code (I am new to C# so any suggestions to improve code is very much welcome), but finally I have a customized C# ColorDialog
box control where the user can not only choose the color but also specify the alpha value.
Using the Code
To use this CustomColorDialog
, there are some fairly simple steps as listed below:
- Add the CustomColorDialog.dll in references
- Setup the
namespace
use:
using CustomCommonDialog;
- Create the
CustomColorDialog
object and call show
:
CustomColorDialog colDlg = new CustomColorDialog(Handle);
bool f = colDlg.Show();
- Get the color component values from the dialog box:
if( f )
{
System.Drawing.Color col = colDlg.SelectedColor;
int a = col.A;
int r = col.R;
int g = col.G;
int b = col.B;
}
The Background
To customize a Color
dialog box, you can use any of the following methods:
- Specify values in the
CHOOSECOLOR
structure when you create the dialog box
- Provide a custom template
- Provide a hook procedure
You can modify the appearance and behavior of the Color
dialog box by setting flags in the Flags
member of the CHOOSECOLOR
structure. For example, you can set the CC_SOLIDCOLOR
flag to direct the dialog box to display only solid colors. To cause the dialog box to initially select a color other than black, set the CC_RGBINIT
flag and specify a color in the rgbResult
member.
You can provide a custom template for the Color
dialog box, for example, if you want to include additional controls that are unique to your application. The ChooseColor
function uses your custom template in place of the default template.
To Provide a Custom Template for the Color Dialog Box
- Create the custom template by modifying the default template specified in the Color.dlg file. The control identifiers used in the default
Color
dialog template are defined in the Color.dlg file.
- Use the
CHOOSECOLOR
structure to enable the template as follows:
- If your custom template is a resource in an application or dynamic link library, set the
CC_ENABLETEMPLATE
flag in the Flags
member. Use the hInstance
and lpTemplateName
members of the structure to identify the module and resource name.
-Or-
- If your custom template is already in memory, set the
CC_ENABLETEMPLATEHANDLE
flag. Use the hInstance
member to identify the memory object that contains the template.
You can provide a CCHookProc hook procedure for the Color
dialog box. The hook procedure can process messages sent to the dialog box. It can also use registered messages to control the behavior of the dialog box. If you use a custom template to define additional controls, you must provide a hook procedure to process input for your controls.
To Enable a Hook Procedure for the Color Dialog Box
- Set the
CC_ENABLEHOOK
flag in the Flags
member of the CHOOSECOLOR
structure
- Specify the address of the hook procedure in the
lpfnHook
member
After processing its WM_INITDIALOG message, the dialog box procedure sends a WM_INITDIALOG
message to the hook procedure. The lParam
parameter of this message is a pointer to the CHOOSECOLOR
structure used to initialize the dialog box.
The dialog box sends the COLOROKSTRING registered message to the hook procedure when the user clicks the OK button. The hook procedure can reject the selected color and force the dialog box to remain open by returning zero when it receives this message. The hook procedure can force the dialog box to select a particular color by sending the SETRGBSTRING registered message to the dialog box. To use these registered messages, you must pass the COLOROKSTRING
and SETRGBSTRING
constants to the RegisterWindowMessage function to get a message identifier. You can then use the identifier to detect and process messages sent from the dialog box, or to send messages to the dialog box.
The Classes
There are following three classes in the CustomCommonDialog namespace
that must be mentioned:
CustomColorDialog
- The main class that wraps up all the code for launching and modifying default choose color dialog box
NativeMethods
- This class wraps all the WinAPI function calls and data types
AlphaSlider
- Custom user control to show the slider control for alpha values
Inside the Main CustomColorDialog Class
The following variables are used in the class:
private ChooseColor _cc;
private AlphaSlider _sliderAlpha;
private TextBox _textAlpha;
private Panel _panelAlpha;
private System.Drawing.Color _color;
The constructor initializes the ChooseColor
structure:
_cc.lStructSize = Marshal.SizeOf( _cc );
_cc.lpfnHook = new CCHookProc(MyHookProc);
_cc.Flags = ChooseColorFlags.FullOpen | ChooseColorFlags.EnableHook;
_cc.hwndOwner = handle;
Int32 temp = 0;
IntPtr CustColor = Marshal.AllocCoTaskMem( 16 * Marshal.SizeOf(temp));
_cc.lpCustColors = CustColor;
The main window hook for the dialog box places the custom controls on the dialog box in WM_INITDIALOG
message:
POINT topLeft = new POINT();
POINT bottomRight = new POINT();
IntPtr ipNotify = new IntPtr( lParam );
ChooseColor cc = (ChooseColor)Marshal.PtrToStructure( ipNotify,
typeof(ChooseColor) );
IntPtr hWndParent = NativeMethods.GetParent( hWnd );
NativeMethods.SetParent( _panelAlpha.Handle, hWnd);
RECT rc = new RECT();
NativeMethods.GetWindowRect( hWnd, ref rc );
topLeft.X = rc.right;
topLeft.Y = rc.top;
NativeMethods.ScreenToClient( hWnd, ref topLeft );
bottomRight.X = rc.right;
bottomRight.Y = rc.bottom;
NativeMethods.ScreenToClient( hWnd, ref bottomRight );
Rectangle rcClient = _panelAlpha.ClientRectangle;
NativeMethods.MoveWindow( hWnd, rc.left, rc.top,
bottomRight.X+rcClient.Width+10, bottomRight.Y+28, true );
The Final OnOk
This is a total hack. I could not find the message posted by ChooColor
dialog when user presses OK. I found the last message sent and use it.
TO DO: Find the correct message as documented i.e. COLOROKSTRING
.
if( msg == 0xC072 )
{
IntPtr ipNotify = new IntPtr( lParam );
ChooseColor cc = (ChooseColor)Marshal.PtrToStructure( ipNotify,
typeof(ChooseColor) );
IntPtr hWndParent = NativeMethods.GetParent( hWnd );
int R = (char )(cc.rgbResult);
int G = (char )(((int)(cc.rgbResult)) >> 8);
int B = (char )(cc.rgbResult >> 16);
if( R > 256 )
R = R/257;
if( G > 256 )
G /= 257;
_color = Color.FromArgb(_sliderAlpha.Value,R, G, B);
Trace.WriteLine(_color);
}
To Do
Add the following CustomDialogBox
es in the CustomCommonDialog namespace
:
- Font Dialog Box
- Open and Save As Dialog Box
- Print Dialog Box
I hope this article would help many to customize the color dialog boxes according to their need. Writing this article gave me a chance to learn C#.
History
- 15th March, 2006: Initial post