A few days ago, I wanted to start creating an Icon Editor application to make use of my IconLib library. I created my main form and I though "Where do I need to start?" Then, I decided to create a menu with the Open functionality. I though the Open should have a preview screen to see the icon before opening it.
If you've reached this paragraph, it's probably because you know that .NET has an OpenFileDialog class, but it cannot be customized.
The objective of this control is to allow you to add some functionality to the OpenFileDialog .NET class. The main reason you can't customize the OpenFileDialog in .NET is because the class is declared sealed; this means that you can't inherit from it. If you go to the base class FileDialog, it will allow you to inherit from it, but is has an internal abstract method, "RunFileDialog." Because it is internal and abstract, it only allows inheriting from it inside the same assembly.
How many times have you wanted to put some extra control in the OpenFileDialog control and you can't?
Searching for code on the Internet, I found a couple of places where MFC was used, but I found nothing for .NET. OpenFileDialog is not natively implemented in .NET; instead, it makes use of a Win32 API, "GetOpenFileName."
At this point, I had three choices:
- Create my own OpenFileDialog from scratch.
- Create my own OpenFileDialog by re-using resources, use the "GetOpenFileName" API, and provide my own template.
- Hack on the .NET OpenFileDialog and add the functionality I need for it.
Choice 1 was not an option because it could require a lot of development time when there is a lot more to be done. Later, when the product is finished, I can review this.
Choice 2 required me to provide my own template using calls to Win32 API and resources.
Choice 3 was the more viable option at this time. Don't think of this as a bad hack. Basically, a hack is when you want make the control do some extra functionality and you must obtain this from different thread or process.
So, because I like the challenges, I decided to "hack" the OpenFileDialog class to create my own customizable control.
What It Can Do for You
I could have hacked the control to do what I needed and that's it, but I ran with this problem many times from .NET 1.0 and no one so far came with a solution for it, so I decided to create an interface to this control where it can be used in different applications.
Also, I wanted to create something that didn't require changing or adding code to the current project and would still be capable of adding multiple controls without knowing the details of how it works. It needed to be a standalone control that can be added as any control in the IDE. I created this control and I called "OpenFileDialogEx."
How to Do It
I imaged OpenFileDialogEx as an abstract class. The only reason I didn't make this class abstract is because the VS IDE can't create an instance of one abstract class that avoids rendering on the screen.
You could use the OpenFileDialogEx class like it is, but makes no sense because it contains no extra functionality, just an empty UserControl. So, you must inherit OpenFileDialogEx to create your own customized version of the Open File Dialog.
After you inherit OpenFileDialogEx, you have created a custom control where you can add controls to it. You could add extra buttons, panels, and groupboxes. Basically, it is a controls container; later, this container will be "appended" to the .NET Open File Dialog object on the fly.
There are three extra properties in this control—three methods and two events—that are different from any UserControl. These are explained in the following sections.
This property lets you choose which view the OpenFileDialog should start. By default, an OpenFileDialog is on the "Details view;" here, you can specify a different default view, such as as "Icons," "List," "Thumbnail," or "Detail."
This property tells whether the control created should be stacked on the right, bottom, or behind the classic OpenFileDialog. Usually, this property will be on the right to expand the OpenFileDialog horizontally. If, instead, you need to add extra controls to the current OpenFileDialog, you can specify "None" and the controls inside OpenFileDialogEx will share the same client area with the original OpenFileDialog.
This property is the embedded OpenFileDialog inside the control. Here, you can set up the standards property, such as "InitialDir," "AddExtension," "Filters," and so forth.
OpenFileDialog, by default, is resizable. OpenFileDialogEx will help you with that automatically; the user control "OpenFileDialogEx" will be resized automatically when the user expands or shrinks the window. It will behave differently, depending on the StartLocation property.
- Right: The user control will be resized vertically.
- Bottom: The user control will be resized horizontally.
- None: The user control will be resized horizontally and vertically.
Then, basically, when you add your controls—such as buttons, panels, groupbox, and so on—you have to set the Anchor property of every control. Then, you can control where your control will be when the user resizes the OpenFileDialog window. For example, to have an image preview, you could set the start location at the right, add a PictureBox to your inherited OpenFileDialogEx, and set the Anchor property for the PictureBox to be Left, Top, Right, Bottom. This will resize the picture box dynamically when the user resize the OpenFileDialog.
The following methods are virtual methods that you will override to interact with the original OpenFileDialog.
This method is called every time the user clicks on any file inside the view.
This method is called every time the user changes a folder from any control inside the OpenFileDialog.
This method is called when the OpenFileDialog is closing; this is useful to release any resource allocated.
The two events are FileNameChanged and FolderNameChanged. Those events are fired from their respective virtual methods, "OnFileNameChanged" and "OnFolderNameChanged." Instead, use the events I recommend and override the methods because it is cleaner code and it doesn't have another level of indirection.