
Introduction
Have you ever written a configuration dialog where the user had to enter a filename or a path for your application to do something with?
If you have, then you've probably added a button to browse for a file or folder with some rather trivial Click
event handler: instantiate the appropriate FileDialog
or FolderBrowserDialog
, set its properties, call ShowDialog()
and if the DialogResult
is DialogResult.OK
, then fill the associated TextBox
with the filename or folder in the dialog.
No big deal, you might say.
You're right.
A standard procedure, not difficult at all, but something you have to do for the configuration dialog to be user-friendly. And when you have several configuration dialogs or several file paths/folders to configure, you'll end up writing the same trivial piece of code over and over again.
So I thought I could simplify this repetitive task a bit and brush up my knowledge of extender providers...
The File/FolderBrowserExtenderProvider component
An IExtenderProvider
-based approach appeared appropriate: binding two controls together, one acting as a "Browse"-button and the other one to receive the file or folder selected.
I chose the approach to let the extender provider extend the control to receive the selected file/folder and to provide the button to start browsing.
Writing the extender provider
To develop such a component, you'll have to inherit IExtenderProvider
and specify the name of the property you want to provide in a ProvidePropertyAttribute
, like this:
[ProvideProperty("BrowseButton", typeof(Control))]
public class FolderBrowserExtenderProvider : Component, IExtenderProvider
That way, I've specified that my extender provider will add an additional property named "BrowseButton
" to some components and that this property is a Control
.
The next step is to say which components can be extended by the new extender provider.
That's done by implementing CanExtend()
. Given an extendee object (i.e., an object that is to be extended), your extender provider must tell whether it can provide the given property for this object.
At first, I returned true
if the extendee derived from TextBoxBase
(that's what you usually have: you enter a filename/folder into a TextBox
), but after a while, I thought I don't have to restrict the developer to this type. Any control has a Text
property that can receive the selected file/folder name, so now this function returns true
for Control
s.
Next, you'll have to handle setting and retrieving the new property.
The extender provider doesn't really add a new property to an extendee in a way that you could actually write:
myTextBox.BrowseButton = myButton;
Instead, its responsibility is to keep a list of extendees and their assigned properties. Such a list entry is created by calling:
Set<PropertyName>(extendee, property value)
and queried by calling:
Get<PropertyName>()
In my case, the signatures for these methods look like this:
public Control GetBrowseButton(Control extendee);
public void SetBrowseButton(Control extendee, Control browseButton);
I think most extender providers will use a HashTable
to keep the assignments, that way it's really easy to keep track. My components do just this.
Adding functionality to the extender provider
So far, our extender provider doesn't do anything but remember which control is assigned as a BrowseButton
to which other control. In order to actually start browsing when an assigned BrowseButton
is clicked, we'll have to add a Click
event handler to the BrowseButton
once it's assigned.
Although you'll rarely remove or reassign an extendee, it's a good idea to remove the event handler before the BrowseButton
is reassigned or you'll end up with several dialogs popping up.
The Click
event handler finally is responsible for showing the appropriate File
- or FolderBrowserDialog
. In order to be able to visually design this dialog, each extender provider simply has a public readonly property FileDialog
and FolderBrowserDialog
, resp.
One little catch here: in order for the designer to correctly serialize the dialog's properties, I had to set the DesignerSerializationVisibilityAttribute
to DesignerSerializationVisibility.Content
, otherwise the reference to the dialog itself is serialized and not the dialog's properties.
Using the extender providers
To use the extender providers in your forms, you should add them to your Toolbox. Then drag them to your Form and each of your controls will show an additional property BrowseButton
with None
as default value.
Simply select the control you want to click on for the appropriate browse dialog to appear and transfer the selected folder/file to the first control's text.
A basic scenario would be to have a TextBox
(textBox1
) and a Button
(button1
) on your Form
.
You add a FolderBrowserExtenderProvider
to the Form
and set textBox1
's BrowseButton
property to button1
and you're done.
You can assign a control to be its own BrowseButton
, by the way. Unfortunately, because of a bug in the code generation of VS, you can't do this visually, or you'll get bogus code. Since there's no workaround or fix up to date, I'm throwing an exception when you try to make such an assignment in the designer.
You can assign the BrowseButton
in code without problems, though.
If you're unsure, just take a look at the sample application included in the source code.
Modification History
- 05.01.2004 - First release.