Click here to Skip to main content
15,867,686 members
Articles / Desktop Programming / XAML

How to create a UserControl (WPF) which allows the user to pick a directory

Rate me:
Please Sign up or sign in to vote.
4.00/5 (1 vote)
5 Jun 2014CPOL3 min read 15.4K   140   12   2
In this article we are going to build a UserControl in WPF which is intended to make the user choose a directory in his FIleSystem

Introduction

This article deals with the creation of a control which allows the user to pick a directory. The control might be easily put into a DialogWindow.

Background

In order to understand the tutorial you need to have a basic knowledge of WPF (what's a UserControl, a DialogWindow, ecc.), C# (collections, IO and events) and Data Binding in WPF (dynamic list binding)

Using the code

The control is designed with a listbox in the middle, a textbox in the upper part of the control and, next to it, there is a button to go up the directory tree.

Image 1

The code of the page is:

XML
<UserControl x:Class="DirectoryPicker.DPick"
             xmlns="<a href="http://schemas.microsoft.com/winfx/2006/xaml/presentation">http://schemas.microsoft.com/winfx/2006/xaml/presentation</a>"
             xmlns:x="<a href="http://schemas.microsoft.com/winfx/2006/xaml">http://schemas.microsoft.com/winfx/2006/xaml</a>"
             xmlns:mc="<a href="http://schemas.openxmlformats.org/markup-compatibility/2006">http://schemas.openxmlformats.org/markup-compatibility/2006</a>"
             xmlns:d="<a href="http://schemas.microsoft.com/expression/blend/2008">http://schemas.microsoft.com/expression/blend/2008</a>"
             mc:Ignorable="d"
             d:DesignHeight="300" d:DesignWidth="300" Loaded="UserControl_Loaded">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="30"/>
            <RowDefinition Height="1*"/>
        </Grid.RowDefinitions>
        <Grid>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="1*" />
                <ColumnDefinition Width="25" />
            </Grid.ColumnDefinitions>
            <TextBox x:Name="txtPath" Grid.Row="0" Margin="5,2" KeyUp="txtPath_KeyUp"/>
            <Button Content="Up" Grid.Column="1" Margin="2" Click="Button_Click"/>
        </Grid>
        <ListBox x:Name="lstDirs" Margin="5" Grid.Row="1" MouseDoubleClick="lstDirs_MouseDoubleClick"/>
    </Grid>
</UserControl>

and the code behind is:

C#
public partial class DPick : UserControl
    {
        ObservableCollection<string> Dirs = new ObservableCollection<string>();
        public string CPath
        {
            get { return (string)GetValue(CPathProperty); }
            set { SetValue(CPathProperty, value); }
        }

        // Using a DependencyProperty as the backing store for CPath.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty CPathProperty =
            DependencyProperty.Register("CPath", typeof(string), typeof(DPick), new PropertyMetadata(""));

        public DPick()
        {
            InitializeComponent();
        }

        private void UserControl_Loaded(object sender, EventArgs e)
        {
            this.lstDirs.ItemsSource = Dirs;
            if (CPath == String.Empty || Directory.Exists(CPath) == false)
            {
		CPath = "";
                foreach (string s in Directory.GetLogicalDrives())
                {
                    Dirs.Add(s);
                }
                this.txtPath.Text = CPath;
            }
            else
            {
                PopulateList();
            }
        }

        private void lstDirs_MouseDoubleClick(object sender, MouseButtonEventArgs e)
        {
            int SelIndex = this.lstDirs.SelectedIndex;
            CPath = System.IO.Path.Combine(CPath, Dirs[SelIndex]);
            PopulateList();
        }

        private void PopulateList()
        {
            try
            {
                Dirs.Clear();
                foreach (var dir in Directory.GetDirectories(CPath))
                {
                    Dirs.Add(GetDirName(dir));
                }
                this.txtPath.Text = CPath;
            }
            catch (Exception)
            {
                Dirs.Clear();
                Dirs.Add("Access Denied");
            }

        }
        private string GetDirName(string Path)
        {
            return Path.Substring(Path.LastIndexOf('\\') + 1);
        }

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            CPath = System.IO.Path.GetDirectoryName(CPath);
            PopulateList();
        }

        private void txtPath_KeyUp(object sender, KeyEventArgs e)
        {
            if (e.Key == Key.Enter)
            {
                string old = CPath;
                try
                {
                    CPath = this.txtPath.Text;
                    PopulateList();
                }
                catch (Exception) { CPath = old; }
            }
        }

        private void btnOK_Click(object sender, RoutedEventArgs e)
        {

        }
    }

Comment

The first line defines an ObservableCollection<string> which define the content of the current directory. It is not a list because an ObservableCollection implements from INotifyPropertyChanged and it can notify whenever it is modified. Then we have defined a DependencyProperty which name's CPath which represents the current path of the control. You can set CPath to an initial directory, but it's also the directory the control is currently in (so the directory the user has selected till now).

Then the method UserControl_Loaded is called. If the CPath property has been set and the directory exists, it's used as the initial directory for the control, otherwise the list is populated with the list of LogicalDrives. I would like to focus the first line of the method

C#
this.lstDirs.ItemsSource = Dirs;

this line indicates to the listbox that Dirs is its source of items. Thanks to the peculiar ability of the ObservableCollection class, the content of the Collection and the content of the ListBox will be always the same.

At this point, the method calls another method  called PopulateList. This method cleares the ObservableCollection (and so the ListBox) and begins to add the content of the current directory (just directory names) to it as strings. If an error occurs, or the directory does not exists anymore (the user might have deleted it while the control has been already updated) or we haven't got the access to that directory, so we add to the list the string "Access Denied"

Then the event ListBox_MouseDoubleClick is defined in the code. This event occurs whenever the user double-clicks on the listbox or on one of its child. This happens because of the event bubbling. The event bubbling says that an event can travel up the control tree and reach the main container. I.E. if an item of the listbox is double-clicked, the listbox is double-clicked as well as the UserControl. In this event, we take the selected index and we add it to the CPath string with the help of the static class Path.Combine

In order to "go up", we can use the static class path with the method GetDirectoryName, which returns the path to the directory another element is in. In this case, this element is a directory, so we can use this code in order to go up. I.E. From C:\Dir1\Dir2\Dir3 Path.GetDirectoryName(string) will return C:\Dir1\Dir2

The last little thing: if we insert a path into the textbox and the users presses the Enter button, the control automatically navigates to the specified directory if it exists.

Image 2

Points of Interest

You can write a DialogWindow with this control. Remember that's a UserControl. This window has to have a DependencyProperty called Path (or whatever you want) and this has to be passed to the CPath DependencyProperty of the control. When the user clicks Ok (so the window returns true) you can access the property Path into the Window and (if you 've written the right code in order to make your Path equal to the CPath of the control) use it for whatever you want!

History

1°Edition of the Article

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Student
Italy Italy
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
QuestionMy vote of 5 Pin
Jacob Himes8-Jun-14 13:16
professionalJacob Himes8-Jun-14 13:16 
AnswerRe: My vote of 5 Pin
LLLLGGGG9-Jun-14 8:45
LLLLGGGG9-Jun-14 8:45 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.