If you used WinForms, chances are that at some point you wanted to extend the OpenFileDialog or SaveFileDialog, but you gave up because there is no easy way to do it, especially if you wanted to add some new graphical elements. Even in the MFC days, such a task was not as daunting as it is for .NET because these classes are sealed, exposing only 16 properties, 5 methods and 3 events that can be used to customize the dialogs. Martin Parry's article on MSDN can give you an insight into how you can customize the OpenFileDialog using the OFNHookProc function and PInvoke. It would look like we have to roll back the clock to early 1990's way of programming and add quite a bit of PInvoke and marshalling to do anything. This is probably enough to give up or look to alternatives like third party libraries. If you are developing for WPF rather than Windows Forms, I suggest that you jump directly to my WPF article on the same topic. However, CastorTiu's article made life A LOT EASIER on those who chose to customize these dialogs using Forms. Using his great work on this topic, I tried to take it even further and make customizing of these two dialogs even more painless. I will focus only on my refactoring and improvements to his original hard work, so if you need details please check CastorTiu's article. While this article uses only C# snippets, I’ve included the equivalent VB.NET code in the downloadable zip file for the VB folks.

To make extending as easy as possible, I've added some extra properties and events to the base control as well as some design features. Probably the most appreciated property will be the FileDlgType that would allow selecting between OpenFileDialog and SaveFileDialog at design time. The extra properties and events are displayed below:

It would be nice to have some visual clue about how the control will look like. I did not want to dig too much design time architecture so I used a simple OnPaint override to draw a red line or a dot where the FileDialog touches the extension. Notice that it is painting only in Design mode.

When you call ShowDialog public method of your control, the messages start flowing only after you called .NET's Open(Save)FileDialog.ShowDialog(). You have to watch for WM_ACTIVATE, and since an Application filter won't catch it, you have to rely on the parent's window WndProc. Instead of using a dummy form, I found out that a message pump window will do just as fine with less overhead. I created it inside WholeDialogWrapper's constructor calling AssignDummyWindow().

Once we got the real dialog handle as _FileDialogHandle, we can forget about the dummy Window and start listening to what really matters. Notice how I released the dummy Window handle and assigned the new one. When the same WndProc catches
the WM_SHOWWINDOW message, we can finally arrange our control and set the parent:

privatevoid InitControls()
{
mInitializated = true;
// Lets get information about the current open dialog
NativeMethods.GetClientRect(new HandleRef(this,_hFileDialogHandle),
ref _DialogClientRect);
NativeMethods.GetWindowRect(new HandleRef(this,_hFileDialogHandle),
ref _DialogWindowRect);
// Lets borrow the Handles from the open dialog control
PopulateWindowsHandlers();
switch (_CustomControl.FileDlgStartLocation)
{
case AddonWindowLocation.Right:
// Now we transfer the control to the open dialog
_CustomControl.Location = new Point((int)
(_DialogClientRect.Width - _CustomControl.Width), 0);
break;
case AddonWindowLocation.Bottom:
// Now we transfer the control to the open dialog
_CustomControl.Location = new Point(0,
(int)(_DialogClientRect.Height - _CustomControl.Height));
break;
case AddonWindowLocation.BottomRight:
// We don't have to do too much in this case, just the default thing
_CustomControl.Location =
new Point((int)(_DialogClientRect.Width - _CustomControl.Width),
(int)(_DialogClientRect.Height - _CustomControl.Height));
break;
}
// Everything is ready, now lets change the parent
NativeMethods.SetParent(new HandleRef(_CustomControl,_CustomControl.Handle),
new HandleRef(this,_hFileDialogHandle));
// Send the control to the back
// NativeMethods.SetWindowPos(_CustomControl.Handle,
(IntPtr)ZOrderPos.HWND_BOTTOM, 0, 0, 0, 0, UFLAGSZORDER);
_CustomControl.MSDialog.Disposed += new EventHandler(DialogWrappper_Disposed);
}

You might think that this code is exhaustive relating to the properties and events you can use. Well... not quite! I've added several properties and events to the original work, but you might still want to add more to it.

Just in the above code snippet, you see how the events from the Open(Save)FileDialog.Dispose are hooked up, but this is an easy one. I'll describe how you can add new events to FileDialogControlBase based on the one I've added as an example.
There is still another helper class called MSFileDialogWrapper that monitors the Open(Save)FileDialog object through the WndProc as below:

Once we marshal the internal pointers into the right structures, we call the OnFilterChanged method of FileDialogControBase object, which is _CustomCtrl in this case. If we dug deeper into this method, we find something as below:

To set the properties of the Open(Save)FileDialog object, you have to override the OnPrepareMSDialog() if the design time settings are not right. However changing the appearance of the Open(Save)FileDialog is more elaborate. As an example, I'll show how to change the Text on the Ok button - that's the Save or Open button. We start with
exposing the property from the FileDialogControlBase and then use PInvoke to set the text as below:

As you see the comment in the code above, realize that we are dealing with a black box and sometimes we can't know the right way to update. This is one of those cases when we can change it on OpenFileDialog but not on SaveFileDialog. If you wonder how we got the _hOKButton or _dlgWrapper.Handle, you would have to look at FileDialogEnumWindowCallBack that is invoked when the WM_SHOWWINDOW is captured in DialogWrapper's WndProc.

Until .NET 3.5 introduced FileDialogCustomPlacesCollection for Windows Vista and up, there was no API that I know of to allow you to modify the places bar on the dialog. There is a way to do it on Windows 2000 and XP as described by Dino Esposito in his MSDN article. That requires modifying the registry and it will affect all instances of these dialogs as long as the user is logged on. Since this looks more like a native dialog feature, I’ve used extension methods applied to the base class FileDialog. The static class is shown below and it exposes two public methods to set the places and to restore the registry.

SetPlaces takes an argument as an array of up to five objects that can be numbers as special folders or strings as regular folders. The Disposed event set on the FileDialog makes an automatic call to the ResetPlaces that restores the registry. As a convenience, I’ve included the Places helper enumeration for predefined special folders. Be aware that this could fail on Vista or newer Windows OSes due to UAC.
If you are running your application on Vista or later, ignore this class and use Microsoft’s FileDialogCustomPlacesCollection class instead, or better yet, use some logic to select the method based on the OS version.

Now, let's put it to work. To start using it, you can drop the code in your project or just add a reference to the FileDlgExtenders.dll assembly or to FileDlgExtenders project. If you choose the latter, build the solution before you move forward, because you need the base class at design time. To make things as easy as possible, select 'Add User Control' to your project, than pick 'Inherited User Control' and finally select FileDialogControlBase from the list. As an example, I've added a control called MySaveDialogControl that is just converting images to thumbnails of the desired dimensions, orientation and file format. Next you will probably set the properties and events in design mode. There are 2 ways to display the control.

To set up FileDialog's data at runtime, override the virtual method OnPrepareMSDialog() in your subclass. You should call base.OnPrepareMSDialog() firstly, so your own changes won't be wiped away. Below is an example of how I change FileDialog's properties at runtime in my derived control.

A similar precaution is needed if you choose to override OnLoad. Always call base.OnLoad, otherwise the Load event won't get called. As a tip, if Visual Studio can't render the new control, clean the solution and rebuild it again. If it still does not work, you might have to manually purge the bin folders, and check what objects you initialize at Design time.
Finally, here is the caller displaying it:

This is version 1.0 if you disregard the work it's based on. For me, it works fine on 32 bit Windows XP SP3. I hope you'll enjoy it.

Version 1.1 fixes a bug associated with the destruction of the file list and keeps the handle and the view mode up to date. I've added a new design time property to enable/disable the OK button called 'FileDlgEnableOkBtn'.
Below is an example of how this property can be used based on the conversion result:

Comments and Discussions

If you double click on the title bar of the file open dialog, it becomes a full screen window. If you close the dialog and reopen it, then the custom portion of the dialog is not initialized.

I have just downloaded the current release version. The behavior is exactly the same as in my application.

If I find a solution to this problem, I will post it, but if somebody else knows how to fix it, I would be grateful.

UPDATE 1

I have found out a couple of things.(1) The OnPaint method in FileDialogControlBase is not entered if the file open dialog is initialised in full screen mode. It looks like hooking into the window procedure has not worked.(2) The WM_ACTIVATE message is first received with LParam==0, so WholeDialogWrapper saves a null handle. I have added a check for this, and I think it is now storing the correct window handle, but it still isn't working. The OnPaint method is still not called.

UPDATE 2

I have now found the problem, but not a solution.Two messages are critical for the initialization:- WM_ACTIVATE- WM_SHOWWINDOW

If the window is initialized in the maximized state, then the message WM_SHOWWINDOW is not sent.The documentation of the message clearly states:

Quote:

The WM_SHOWWINDOW message is not sent under the following circumstances:

- When a top-level, overlapped window is created with the WS_MAXIMIZE or WS_MINIMIZE style. - When the SW_SHOWNORMAL flag is specified in the call to the ShowWindow function.

So the second part of the initialization does not happen. I'm still looking for a solution.

I know its the control boxes behaviour to handle the enter key as OK. But i have a multi line text box to add comments to a saved file. I've looked at key preview and a number of other key press handles for both the Dialogue and the control. The enter key press never makes it out. Is there a way to disable or inhibit this? I tried disabling the save button whilst editing text, sadly you just get the windows notification that you cant press enter whilst save disabled and that's it.

I have a current project where we added a group box and a checkbox to the file dialog. When I double click the title bar the dialog gets maximized and all is well. When I close the dialog and later open it my groupbox and checkbox are gone. When I hook up the paint and load events I see they aren't called in my subclassed form. When I then again make my dialog smaller. My controls are still gone. When I close and open it again the dialog is the same non maximized size and my controls are back.

When I try to catch the double click in the titlebar event in the process messaging, it is filtered I can only catch the resize event or process messages.

If you have any clue as to how I can solve or prevent this it would be great.

I am trying to implement this solution, and I have noticed that when displayed the UI looks different than if I were to just create a OpenFileDialog on Windows 7, which includes a preview pane and updated icons and style. Is there a way to get the updated style/content?

hey hi, can you give some idea on how to add splitter bar between openfiledialog and the extension(usercontrol) or if you can provide some sample code then it will be helpful.Whenever i'll move splitter then accordingly the image in picturebox will be zoomed in and out.

This is a really good project, and works well except for a couple of small issues I've been having.

1. When I create the dialog, it can be resized to make it larger, but not smaller. And it retains the larger size when reopened, even after cleaning the project. I've tried setting the size to a value, and it seems to be being overridden at some point.

2. (Very minor issue here, but thought I mention it) The combo box always closes immediately after being opened for the first time.

If anyone could help me with these issues it's be appreciated. I'm using Windows 10, and Visual Studio 2013.

I tried this and it works really well. The only question I have is that, the save dialog does not follow the exact style of what the windows save dialog has. For example, I am on Windows 7 machine and a normal save dialog would have a link to "Hide Folders" or "Browse folders". Also the left side ( Places ) is a list with folders expanded ( In custom dialog its just the four options ). But this custom dialog does not follow the underlying OS dialog. I hope this question is clear enough. Is there any way to achieve this?

Very Nice article.I found out with spy++ how to change the filter inside the combo box to another selected filter (CB_SETCURSEL).But the problem I have now: is that the listview is not updated when changed...Anyone have an idea which messages I need to send so my problem get solved?

My problem is that the custom control part of the dialog is an MS SQL Server explorer treeview that shows available SQL server databases. What I want to do is when any of the databases is selected, and a custom "select" button is clicked, close the custom dialog box. I have tried using the FileOk event handler to to this, but this just refreshes the list of files in the currently selected folder (as no file has been selected in the File name combobox. How do I get the dialog to close? I have set both the MSDialog.CheckFileExists and MSDialog.CheckPathExists properties = false in the OnPrepareMSDialog method to no avail.

When the User Control Loads, it displays a list of relevant SQL Server databases in a TreeView control if a connection to SQL Server is available. When any of the databases are selected, some information about the selected database is displayed in some Text controls, which are also hosted in the User Control. Selecting a database also enables a "Select" Button Control.

In the Click Event Handler for the Select Button, I call Dispose() on the derived control after setting some properties to be made available for the calling code.

I'm trying to use this to build a list of file names. As an example in MyOpenFileDialogControl I added a ListBox and a Button and set .Multiselect = true. When I click the button it simply adds the currently Selected file(s) (saved from the MyBase.EventFileNameChanged handler routine) to my ListBox. (for multiple files it is in the form path "filename 1" "filename 2" "filename n" ). Or if I select just one folder (in the OpenFileDialog's Listview) then I get returned the Folder name, but if I select more then one Folder I just get one of the Folders I selected. So this method is not working well because what I would like to do is if Folder(s) are selected then add all the files from all the folders (and their sub folders) into my ListBox.

Bottom line: Is there a way I can get all of the items selected in the OpenFileDialog's Listview (and determine/know if they are files or folders)?

Seems like to do this I need to watch for messages from the OpenFileDialog's Listview, but I'm not sure how to do this or if it is right way to do it.

I would like to ask you something: is it possible to listen/watch for opened save file dialog by any program (like word etc.) and replace that by a modified one?

I am working on a local Version Control System. I really would like to achieve that a checkbox at the bottom of the save file dialog could be checked / unchecked to use / not to use my file versioning. If the checkbox is checked, another window should pop-up where the user could enter some additional metadata. After that the file is stored in a local database.Is such a save file dialog replacement possible with your approach?

I've run into an interesting problem. When I display my extension form from a Console application or any class that does not have a window handle, it appears appropriately. When I launch the dialog from a form, the typical Windows 7 save-as dialog appears with none of my extra functionality (as if the AutoUpgradeEnabled is forced to true even though it is set to false). Has anyone else run into this problem?

This issue occurs when the host Window to the custom save dialog is created through Side by Side COM Interop that does not make use of the registry. The context is created as an APARTMENT_THREADED context.

I have tried the solution from 27-Jul-09 3:18 (passing the host window into the ShowDialog function call) with no success.

I wonder how to change to a different folder/path, if the user select one of recent used files from the "File name" listbox. I have already found out how to react on changes in "File Name" Listbox, but do not know what window message to send in order to change folder and update the list view. Can you help me please? Thanks.

In general, it is possible to drop down the listBox "file name" and you will see the recent opened files. Therefore, you can chose one of the entries. However, the preview was not updated in that case. You can fix it, if you add the following code in the "WndProc()" method of the "WholeDialogWrapper" class:

Unfortunately, the solution above works only, if nothing is selected within the list view. As soon as some element is selected the following code returns the path of selected element, but not the current path of the FileName listbox:

I have extended the file dialog with additional information on the right hand side. At present, the extended part of the dialog has a fixed width.It would be cool if I could add a splitter bar between the original file open dialog and the extended part. Is this possible? Does anybody have some sample code?

In my application, the extended part of the dialog shows a list of items in the file.I would like to select an item from this list and close the dialog with a double click on the item.Is there any way to close the file open dialog from my DoubleClick event?

I really like the work you have done, but as it's not possible to use the newer Vista / windows 7 Look & Feel (AutoUpgradeEnabled = true) I can't use this. Is it possible to get this somehow working with the new Dialogs?

If the user is manually changing the text in the file name field (edit box) and then presses enter, it doesn"t have the same behaviour as the stock OpenFileDialog: it doesn't change the current folder, it just 'beeps'.

Has any one come across this bug? It happens in my application, and I verified that it also happens in the CustomOpenFileDialog.exe app.

(1) Launch CustomOpenFileDialog.exe(2) Click "Save"*Click some images see that it is working.(3) Double click in the title bar to maximize the Save dialog*Click some images see that it is working.(4) Close the save dialog(5) Click "Save" to reopen the dialog* Notice that the preview controls are absent from the right. Click on the images and see that nothing is happening.(6) Double click in the title bar to restore the Save dialog to its windowsed state* Notice that the preview controls are absent from the right. Click on the images and see that nothing is happening.(7) Close the save dialog(8) Click "Save" to reopen the dialog*Click some images see that it is working.

It appears that if the dialog is opened in a maximized state, it doesn't work properly. When this occurs the file changed event is not being fired.

It appears that WM_SHOWWINDOW is not called when the dialog is maximized. I removed the WM_SHOWWINDOW handling in WholeDialogWrapper.WndProc and changed the WM_PAINT handler to
<prelang="vb">If (Not mInitializated) Then
InitControls()
End If</pre>
This appears to have resolved the issue.