Click here to Skip to main content
15,898,769 members
Articles / Desktop Programming / WPF
Alternative
Article

WPF Folder Browser

Rate me:
Please Sign up or sign in to vote.
3.67/5 (2 votes)
4 May 2012CPOL2 min read 30.9K   999   9   8
This is an alternative for "WPF Folder Browser"

Introduction 

This version of the WPF Folder Browser introduces new basic functionality. 

It offers

  • Selection on double click 
  • automatic treeview folder opening and scrolling when the SelectedFolder is changed programatically or via user input in the text box.
  • Update of textbox binding on EnterKey down 

Background

Sean Ireson liked the original solution so much that he went through the hassle of improving it and offer the code to the original poster for updating. Years later he was kind enough to email me his new code base to save me some time. Now I've made another tiny update which makes it worth to reshare.

NOTE! 

Before you go and download this tool, you should ask yourself if you can live with just a folder browser with the Standard Windows look and feel - because if you can Mark Salsbery has provided a WPF wrapper around the SHBrowseForFolder Function. I wish I was a better searcher so I had found this - but for other poor searchers here is a link to Marks cool tool post A Folder Browser Common Dialog for WPF. Anyway if you want a Dialog that you can style to your heart content read on. 

Using the code

The changes to the original code done by Sean: 

Introduction of the TreeViewItemBehaviour class introducing the DependencyProperty IsBroughtIntoViewWhenSelected.0

C#
public static class TreeViewItemBehaviour
	    {
        #region IsBroughtIntoViewWhenSelected
 
        public static bool GetIsBroughtIntoViewWhenSelected(TreeViewItem treeViewItem)
        {
            return (bool)treeViewItem.GetValue(IsBroughtIntoViewWhenSelectedProperty);
        }
 
        public static void SetIsBroughtIntoViewWhenSelected(
          TreeViewItem treeViewItem, bool value)
        {
            treeViewItem.SetValue(IsBroughtIntoViewWhenSelectedProperty, value);
        }
 
        public static readonly DependencyProperty IsBroughtIntoViewWhenSelectedProperty =
            DependencyProperty.RegisterAttached(
            "IsBroughtIntoViewWhenSelected",
            typeof(bool),
            typeof(TreeViewItemBehaviour),
            new UIPropertyMetadata(false, OnIsBroughtIntoViewWhenSelectedChanged));
 
        static void OnIsBroughtIntoViewWhenSelectedChanged(
          DependencyObject depObj, DependencyPropertyChangedEventArgs e)
        {
            TreeViewItem item = depObj as TreeViewItem;
            if (item == null)
                return;
 
            if (e.NewValue is bool == false)
                return;
 
            if ((bool)e.NewValue)
            {
                item.Selected += item_Selected;
            }
            else
            {
                item.Selected -= item_Selected;
            }
        }
 
        static void item_Selected(object sender, RoutedEventArgs e)
        {
            TreeViewItem item = e.OriginalSource as TreeViewItem;
            if (item != null)
                item.BringIntoView();
        }  
        #endregion // IsBroughtIntoViewWhenSelected 
} 

Modification to the BrowserViewModel by introducing the OnSelectedFolderChanged and Expand functions.

C#
    public string SelectedFolder
    {
        get
        {
            return _selectedFolder;
        }
        set
        {
            _selectedFolder = value;
            OnPropertyChanged("SelectedFolder");
            OnSelectedFolderChanged();// Introduced by Sean to trigger action

        }
    }
    private void OnSelectedFolderChanged()
    {
        if (!_expanding)
        {
            try
            {
                _expanding = true;
                FolderViewModel child = Expand(Folders, SelectedFolder);
                child.IsSelected = true;
            }
            finally
            {
                _expanding = false;
            }
        }
    }

    private FolderViewModel Expand(ObservableCollection<FolderViewModel> childFolders, string path)
    {
        if (String.IsNullOrEmpty(path) || childFolders.Count == 0)
        {
            return null;
        }

        string folderName = path;
        if (path.Contains('/') || path.Contains('\\'))
        {
            int idx = path.IndexOfAny(new char[] { '/', '\\' });
            folderName = path.Substring(0, idx);
            path = path.Substring(idx + 1);
        }
        else
        {
            path = null;
        }

        var results = childFolders.Where<FolderViewModel>(folder => folder.FolderName == folderName);
        if (results != null && results.Any())
        {
            FolderViewModel fvm = results.First();
            fvm.IsExpanded = true;

            var retVal = Expand(fvm.Folders, path);
            if (retVal != null)
            {
                return retVal;
            }
            else
            {
                return fvm;
            }
        }

        return null;
}

Addition of a text_box click event to the code behind in FolderBrowserDialog that selects and closes the form on double click on either text or treeview node.

C#
private void TextBlock_MouseDown(object sender, MouseButtonEventArgs e)
{
    if (e.ClickCount == 2 && e.LeftButton == MouseButtonState.Pressed)
    {
        // close the dialog on a double-click of a folder
        DialogResult = true;
    }
}

I added the InputBindingManager class blatantly borrowed from this StackOverflow answer by Samuel Jack.

C#
public static class InputBindingsManager
{
    public static readonly DependencyProperty UpdatePropertySourceWhenEnterPressedProperty =
        DependencyProperty.RegisterAttached("UpdatePropertySourceWhenEnterPressed", typeof (DependencyProperty),
                                            typeof (InputBindingsManager),
                                            new PropertyMetadata(null,
                                                                 OnUpdatePropertySourceWhenEnterPressedPropertyChanged));

    static InputBindingsManager()
    {
    }

    public static void SetUpdatePropertySourceWhenEnterPressed(DependencyObject dp, DependencyProperty value)
    {
        dp.SetValue(UpdatePropertySourceWhenEnterPressedProperty, value);
    }

    public static DependencyProperty GetUpdatePropertySourceWhenEnterPressed(DependencyObject dp)
    {
        return (DependencyProperty) dp.GetValue(UpdatePropertySourceWhenEnterPressedProperty);
    }

    private static void OnUpdatePropertySourceWhenEnterPressedPropertyChanged(DependencyObject dp,
                                                                              DependencyPropertyChangedEventArgs e)
    {
        UIElement element = dp as UIElement;
        if (element == null)
        {
            return;
        }
        if (e.OldValue != null)
        {
            element.PreviewKeyDown -= HandlePreviewKeyDown;
        }
        if (e.NewValue != null)
        {
            element.PreviewKeyDown += new KeyEventHandler(HandlePreviewKeyDown);
        }
    }

    private static void HandlePreviewKeyDown(object sender, KeyEventArgs e)
    {
        if (e.Key == Key.Enter)
        {
            DoUpdateSource(e.Source);
        }
    }

    private static void DoUpdateSource(object source)
    {
        DependencyProperty property = GetUpdatePropertySourceWhenEnterPressed(source as DependencyObject);
        if (property == null)
        {
            return;
        }
        UIElement elt = source as UIElement;
        if (elt == null)
        {
            return;
        }
        BindingExpression binding = BindingOperations.GetBindingExpression(elt, property);
        if (binding != null)
        {
            binding.UpdateSource();
        }
    }
}

This enables the textbox to update the binding on enter key down events by creating an attached behaviour to the textbox in the XAML code.:

C#
<TextBox Text="{Binding Path=SelectedFolder, UpdateSourceTrigger=PropertyChanged}"
         local:InputBindingsManager.UpdatePropertySourceWhenEnterPressed="TextBox.Text"
         MinHeight="25"
         Margin="5"
         VerticalContentAlignment="Center" />

Points of Interest

The Attached Behavior is quite practical for use on templated controls.

History

None so far.

License

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


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

Comments and Discussions

 
QuestionUnhandled Exception when editing FolderBrowseDialog text. Pin
phill_mn20-Apr-15 11:01
phill_mn20-Apr-15 11:01 
AnswerRe: Unhandled Exception when editing FolderBrowseDialog text. Pin
Erik Rude22-Apr-15 5:38
Erik Rude22-Apr-15 5:38 
QuestionRe-used your article in a different place Pin
Dirk Bahle16-Apr-14 13:04
Dirk Bahle16-Apr-14 13:04 
AnswerRe: Re-used your article in a different place Pin
Erik Rude22-Apr-14 23:37
Erik Rude22-Apr-14 23:37 
GeneralMy vote of 1 Pin
SaurabhSavaliya1-May-12 0:22
SaurabhSavaliya1-May-12 0:22 
GeneralRe: My vote of 1 Pin
Erik Rude2-May-12 3:53
Erik Rude2-May-12 3:53 
BugHanging problem with DirectoryInfo.Attributes Pin
Erik Rude29-Apr-12 23:16
Erik Rude29-Apr-12 23:16 
GeneralRe: Hanging problem with DirectoryInfo.Attributes Pin
Erik Rude31-Aug-12 1:59
Erik Rude31-Aug-12 1:59 

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.