A UserControl is a perfectly acceptable solution, but I would be more likely to use either 1) a custom control, or 2) a RoutedUICommand.
Building a Custom Control
Create a simple control derived from TextBox:
public class TextBoxWithFileDialog : TextBox
{
static TextBoxWithFileDialog()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(TextBoxWithFileDialog), new FrameworkPropertyMetadata(typeof(TextBoxWithFileDialog)));
CommandManager.RegisterClassCommandBinding(typeof(TextBoxWithFileDialog), new CommandBinding(
ApplicationCommands.Open,
(obj, e) => { e.Handled = true; ((TextBoxWithFileDialog)obj).ShowFileOpenDialog(); },
(obj, e) => { e.CanExecute = true; }));
}
void ShowFileOpenDialog()
{
var dialog = new Microsoft.Win32.OpenFileDialog
{
DefaultExt = ".txt"
};
if(dialog.ShowDialog()==true)
Text = dialog.FileName;
}
}
then in themes/Generic.xaml or a resource dictionary included from it add a style containing an appropriate ControlTemplate:
<Style TargetType="{x:Type TextBoxWithFileDialog}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TextBoxWithFileDialog}">
<DockPanel>
<Button Content="..." Command="Open" DockPanel.Dock="Right" />
<!-- copy original TextBox control template contents here -->
</DockPanel>
... close rest of tags ...
You can copy TextBox's existing ControlTemplate using Expression Blend or Reflector/BamlViewer).
For my own projects I would prefer to add a solution like this to my control library so I can use it anywhere I want. However this may be overkill if you're only going to use it once. In that case I would just:
Using a RoutedUICommand
public partial class MyWindow : Window
{
public Window()
{
InitializeComponent();
...
CommandManager.RegisterClassCommandBinding(typeof(TextBoxWithFileDialog), new CommandBinding(
ApplicationCommands.Open,
(obj, e) =>
{
e.Handled = true;
((MyWindow)obj).ShowFileOpenDialog((TextBox)e.Parameter);
},
(obj, e) => { e.CanExecute = true; }));
}
void ShowFileOpenDialog(TextBox textBox)
{
var dialog = new Microsoft.Win32.OpenFileDialog
{
DefaultExt = ".txt"
};
if(dialog.ShowDialog()==true)
textBox.Text = dialog.FileName;
}
}
This does not require a style or an additional class. Just name your textbox, and have the button refer to the textbox as its command parameter:
<TextBox x:Name="Whatever" ... />
<Button Content="..." Command="Open" CommandParameter="{Binding ElementName=Whatever}" />
That's all there is to it. Unfortunately it only works in one window, and putting it somewhere else would require cut-and-paste. That's why I prefer a custom control.
Note
If you're already using ApplicationCommands.Open elsewhere in your application, you might select a different command, or create your own:
public static readonly RoutedUICommand MyCommand = new RoutedUICommand(...)