File Upload Custom Control in WPF

Posted by: Mahesh Sabnis , on 9/8/2010, in Category WPF
Views: 93518
Abstract: In this article, I will discuss how to create a custom control using WPF. WPF allows us to define our controls using either User Control (UC) or Custom Control (CC).
In this article, I will discuss how to create a custom control using WPF. WPF allows us to define our controls using either User Control (UC) or Custom Control (CC). Before creating either a UC or CC, as a developer the following points must be considered:
What functionality will be performed by the control?
This will help in selecting Visual Elements for designing UI for the control, along with the business logic for it.
What data will be either consumed by the control or published from the control?
This will help in defining dependency properties for the control. These properties will be used in DataBinding.
When will this control publish the data to its container?
This will help in defining routed event for the controls.
Differences between WPF User Control and Custom Control
 
There are many differences between User Control(UC) and Custom Control(CC). I will focus on some main ones.
UC provides XAML designer and all the visual controls are pre-constructed, which means these controls can be used to raise events. In case of CC, ‘Generic.xaml’ is provided which provides only template. WPF Visual controls can be put in this XAML file, but these controls needs to be reconstructed by overriding ‘OnApplyTemplate()’ method of the ‘Control’ base class. CC supports theming whereas UC does not.
In this article, I will explain creating Custom Control for performing FileUpload operations for Image as well as Docs Files. Note: In this article I have used only images. I have chosen to do this because recently I have come across a Line-of-Business (LOB) application, where images for Employees, Products had to be saved in a database. While performing the insert operation for Employee and Products, it was needed to select images from the disk and save it in database. The application was demanding Image upload operation repeatedly in both the insert operations. This code repetition pushed me to come up with a custom control for image upload.
Please note that to keep this article simple and to the point, I have not covered uploading images to the database. This article only covers how to create and use a custom control in your WPF applications.
Creating a Custom Control in WPF
 
Step 1: Open VS 2010 and create a blank solution and  name it as ‘WPF_CustomControl’..
Step 2: To this solution, add a new WPF Custom Control Library Project and name it as ‘WPF40_FileUploadCustomControl’. This project will have ‘Themes’ folder containing ‘Generic.Xaml’. Also this project will contain ‘CustomControl1.cs’ code file. This file will contain necessary business logic of the control.
Step 3: Rename ‘CustomControl1.cs’ to ‘FileUploadCustomControl.cs’. Add the following code in ‘Generic.xaml’:
<ResourceDictionary
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:WPF40_FileUploadCustomControl">
    <Style TargetType="{x:Type local:FileUploadCustomControl}">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type local:FileUploadCustomControl}">
                    <Border Background="{TemplateBinding Background}"
                            BorderBrush="{TemplateBinding BorderBrush}"
                            BorderThickness="{TemplateBinding BorderThickness}">
                        <StackPanel Orientation="Horizontal"
                                          Width="200">
                            <TextBox Name="TXT_FILE_NAME" Width="150"></TextBox>
                            <Button Name="BTN_BROWSE_FILE" Width="50" Content="Browse"></Button>
                        </StackPanel>
                    </Border>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</ResourceDictionary>
 
The above code defines only the control template which contains ‘StackPanel’, ‘TextBox’ and ‘Button’ visual elements. One important here is that since this is just a resource dictionary, you cannot define any event on the elements.
Step 4: Now it’s time to define the business logic. Make sure that in the ‘FileUploadCustomControl.cs’ file, the class ‘CustomControl1’ is renamed to ‘FileUploadCustomControl’. Also rename the constructor. The code in the constructor will be as below:
C#
static FileUploadCustomControl()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(FileUploadCustomControl), new FrameworkPropertyMetadata(typeof(FileUploadCustomControl)));
}
 
VB.NET (Converted Code)
Shared Sub New()
      DefaultStyleKeyProperty.OverrideMetadata(GetType(AddressOf FileUploadCustomControl), New FrameworkPropertyMetadata(GetType(AddressOf FileUploadCustomControl)))
End Sub
Define TextBox and Button at class level as shown below. This will be instantiated using the controls names set in the template XAML above in Step 3.
C#
TextBox txtFileName = null;
Button btnBrowse = null;
 
VB.NET (Converted Code)
Dim txtFileName As TextBox = Nothing
Dim btnBrowse As Button = Nothing
 
Since this control is used to upload the image and this will be displayed in the container of this control, I have declared the following dependency property name ‘FileName’.
C#
public string FileName
{
    get { return (string)GetValue(FileNameProperty); }
    set { SetValue(FileNameProperty, value); }
}
 
// Using a DependencyProperty as the backing store for FileName. This enables animation, styling, binding, etc...
public static readonly DependencyProperty FileNameProperty =
    DependencyProperty.Register("FileName", typeof(string), typeof(FileUploadCustomControl),
        new FrameworkPropertyMetadata(string.Empty, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));
 
 
VB.NET (Converted Code)
Public Property FileName() As String
      Get
            Return CStr(GetValue(FileNameProperty))
      End Get
      Set(ByVal value As String)
            SetValue(FileNameProperty, value)
      End Set
End Property
 
' Using a DependencyProperty as the backing store for FileName. This enables animation, styling, binding, etc...
Public Shared ReadOnly FileNameProperty As DependencyProperty = DependencyProperty.Register("FileName", GetType(String), GetType(FileUploadCustomControl), New FrameworkPropertyMetadata(String.Empty, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault))
 
Since the control has to expose the ‘FileName’ property to its container application, the CC should declare the following routed event:
C#
public event RoutedEventHandler FileNameChanged
{
    add { AddHandler(FileNameChangedEvent, value); }
    remove { RemoveHandler(FileNameChangedEvent, value); }
}
 
public static readonly RoutedEvent FileNameChangedEvent =
    EventManager.RegisterRoutedEvent("FileNameChanged",
    RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(FileUploadCustomControl));
 
 
 
VB.NET (Converted Code)
Public Custom Event FileNameChanged As RoutedEventHandler
      AddHandler(ByVal value As RoutedEventHandler)
            MyBase.AddHandler(FileNameChangedEvent, value)
      End AddHandler
 
      RemoveHandler(ByVal value As RoutedEventHandler)
            MyBase.RemoveHandler(FileNameChangedEvent, value)
      End RemoveHandler
 
      RaiseEvent(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs)
      End RaiseEvent
End Event
 
Public Shared ReadOnly FileNameChangedEvent As RoutedEvent = EventManager.RegisterRoutedEvent("FileNameChanged", RoutingStrategy.Bubble, GetType(RoutedEventHandler), GetType(FileUploadCustomControl))
 
Now it’s time to construct the control, so override ‘OnApplyTemplate()’ method as below:
C#
public override void OnApplyTemplate()
{
    base.OnApplyTemplate();
 
    txtFileName = this.Template.FindName("TXT_FILE_NAME", this) as TextBox;
 
    btnBrowse = this.Template.FindName("BTN_BROWSE_FILE", this) as Button;
    btnBrowse.Click += new RoutedEventHandler(btnBrowse_Click);
    txtFileName.TextChanged += new TextChangedEventHandler(txtFileName_TextChanged);
}
 
VB.NET (Converted Code)
Public Overrides Sub OnApplyTemplate()
      MyBase.OnApplyTemplate()
 
      txtFileName = TryCast(Me.Template.FindName("TXT_FILE_NAME", Me), TextBox)
 
      btnBrowse = TryCast(Me.Template.FindName("BTN_BROWSE_FILE", Me), Button)
      AddHandler btnBrowse.Click, AddressOf btnBrowse_Click
      AddHandler txtFileName.TextChanged, AddressOf txtFileName_TextChanged
End Sub
The above method shows how the Button and TextBox objects are created using the respective control name from the ‘Generic.xaml’.
Write the following code in the Browse button event. This code will launch the open dialog box and then the file can be selected.
C#
void btnBrowse_Click(object sender, RoutedEventArgs e)
{
    OpenFileDialog fileDIalog = new OpenFileDialog();
    fileDIalog.Filter = "Image files (*.bmp, *.jpg)|*.bmp;*.jpg|Doc Files (*.doc;*.docx)|*.doc;*.docx";
    fileDIalog.AddExtension = true;
    if (fileDIalog.ShowDialog() == true)
    {
        FileName = fileDIalog.FileName;
        txtFileName.Text = FileName;
    }
}
 
VB.NET (Converted Code)
Private Sub btnBrowse_Click(ByVal sender As Object, ByVal e As RoutedEventArgs)
      Dim fileDIalog As New OpenFileDialog()
      fileDIalog.Filter = "Image files (*.bmp, *.jpg)|*.bmp;*.jpg|Doc Files (*.doc;*.docx)|*.doc;*.docx"
      fileDIalog.AddExtension = True
      If fileDIalog.ShowDialog() = True Then
            FileName = fileDIalog.FileName
            txtFileName.Text = FileName
      End If
End Sub
Write the following code in the TextChanged event of the textbox, here e.Handled = True’ will stop the event at the control level itself and the ‘FileNameChanged’ is raised. This event is then handled in the container WPF application.
C#
void txtFileName_TextChanged(object sender, TextChangedEventArgs e)
{
    e.Handled = true;
 
    base.RaiseEvent(new RoutedEventArgs(FileNameChangedEvent));
}
 
VB.NET (Converted Code)
Private Sub txtFileName_TextChanged(ByVal sender As Object, ByVal e As TextChangedEventArgs)
                  e.Handled = True
                  MyBase.RaiseEvent(New RoutedEventArgs(FileNameChangedEvent))
End Sub
 
That’s it. Build the control.
 
Create a WPF Application for using the Custom Control.
 
Step 1: In the same solution, add a new WPF Application and name it as ‘WPF40_FileUploadContainer’.
Step 2: In this project, add the reference of the ‘WPF40_FileUploadCustomControl’ library so that the control can be used.
Step 3: Open MainWindow.Xaml and register the control in XAML (Gray Marked). Use the control in XAML and subscribe the ‘FileNameChanged’ event (Green Marked) as shown below :
<Window x:Class="WPF40_FileUploadContainer.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:ccFileUpload="clr-namespace:WPF40_FileUploadCustomControl;assembly=WPF40_FileUploadCustomControl"
        Title="MainWindow" Height="481" Width="667">
    <Grid>
        <Grid.RowDefinitions>
           <RowDefinition Height="112*" />
            <RowDefinition Height="330*" />
        </Grid.RowDefinitions>
        <ccFileUpload:FileUploadCustomControl x:Name="fileUpload"
                                              Width="600" Margin="12,29,34,0" Height="56"
                                              VerticalAlignment="Top"
                                               FileNameChanged="fileUpload_FileNameChanged"></ccFileUpload:FileUploadCustomControl>
        <Image Grid.Row="1" Height="264" HorizontalAlignment="Left" Margin="30,24,0,0" Name="imgUploaded" Stretch="Fill" VerticalAlignment="Top" Width="589" />
    </Grid>
</Window>
 
Step 4: Open MainWindow.Xaml.cs and write the following code in it:
C#
private void fileUpload_FileNameChanged(object sender, RoutedEventArgs e)
{
    ImageSource imgSrc = new BitmapImage(new Uri(fileUpload.FileName));
    imgUploaded.Source = imgSrc;
}
 
VB.NET (Converted Code)
Private Sub fileUpload_FileNameChanged(ByVal sender As Object, ByVal e As RoutedEventArgs)
      Dim imgSrc As ImageSource = New BitmapImage(New Uri(fileUpload.FileName))
      imgUploaded.Source = imgSrc
End Sub
The above code represents the event handled on the container for the Custom Control.
Step 5: Run the application.
Once the WPF window runs, click on ‘Browse’ button and the open dialog box will be displayed. Select a ‘.jpg’ file and click on ‘Open’ button. The selected image will be displayed as shown below:
WPF Custom Control
Conclusion:  
WPF allows us to develop specialty controls using Custom Control. Before deciding to use a Custom Control, one must think about the questions asked in the beginning of this article.
The entire source code of this article can be downloaded over here

This article has been editorially reviewed by Suprotim Agarwal.

Absolutely Awesome Book on C# and .NET

C# and .NET have been around for a very long time, but their constant growth means there’s always more to learn.

We at DotNetCurry are very excited to announce The Absolutely Awesome Book on C# and .NET. This is a 500 pages concise technical eBook available in PDF, ePub (iPad), and Mobi (Kindle).

Organized around concepts, this Book aims to provide a concise, yet solid foundation in C# and .NET, covering C# 6.0, C# 7.0 and .NET Core, with chapters on the latest .NET Core 3.0, .NET Standard and C# 8.0 (final release) too. Use these concepts to deepen your existing knowledge of C# and .NET, to have a solid grasp of the latest in C# and .NET OR to crack your next .NET Interview.

Click here to Explore the Table of Contents or Download Sample Chapters!

What Others Are Reading!
Was this article worth reading? Share it with fellow developers too. Thanks!
Share on LinkedIn
Share on Google+

Author
Mahesh Sabnis is a DotNetCurry author and a Microsoft MVP having over two decades of experience in IT education and development. He is a Microsoft Certified Trainer (MCT) since 2005 and has conducted various Corporate Training programs for .NET Technologies (all versions), and Front-end technologies like Angular and React. Follow him on twitter @maheshdotnet or connect with him on LinkedIn


Page copy protected against web site content infringement 	by Copyscape




Feedback - Leave us some adulation, criticism and everything in between!
Comment posted by santos on Wednesday, December 15, 2010 4:49 PM
the article is very good. thank you by shared with guys
Comment posted by anton genis on Thursday, February 3, 2011 7:14 AM
Nice post!!!
Please change:
FileUploadCustonControl to FileUploadCustomControl.
Comment posted by Carol on Friday, February 4, 2011 6:49 PM
Thanks anton. The typo has been corrected.
Comment posted by Joby Thomas on Thursday, February 24, 2011 4:53 AM
Thanks!!!!
Comment posted by Seema on Tuesday, November 15, 2011 12:29 AM
Thanks !!! very good article.
Comment posted by vema on Friday, August 10, 2012 2:44 AM
while opening Generic file geting error like this

Undefined CLR namespace. The 'clr-namespace' URI refers to a namespace 'WPF40_FileUploadCustomControl' that is not included in the assembly.   E:\Visual studio 010\projects\Practice\WPF40_FileUploadCustomControl\WPF40_FileUploadCustomControl\Themes\Generic.xaml   4   17   WPF40_FileUploadCustomControl

what is the problem  am using vs2010 frmwrk4.0 only

Comment posted by vema on Friday, August 10, 2012 3:57 AM
while opening Generic file geting error like this

Undefined CLR namespace. The 'clr-namespace' URI refers to a namespace 'WPF40_FileUploadCustomControl' that is not included in the assembly.   E:\Visual studio 010\projects\Practice\WPF40_FileUploadCustomControl\WPF40_FileUploadCustomControl\Themes\Generic.xaml   4   17   WPF40_FileUploadCustomControl

what is the problem  am using vs2010 frmwrk4.0 only

Comment posted by Ray on Tuesday, August 14, 2012 1:23 PM
<Style TargetType="{x:Type local:FileUploadCustomControl}">
converted code to VB and can't get past this error
Cannot find type 'local:FileUploadCustomControl'. Note name are case sentative line 5 pos 12.
Any ideas??
Comment posted by Ray on Wednesday, August 15, 2012 9:40 AM
Solved my issue, my fault
Comment posted by Prashant Bhopale on Friday, July 12, 2013 4:24 AM
Thanks,Its works nicely!!!