Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

WPF Folder Browser

0.00/5 (No votes)
4 May 2012 1  
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

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.

        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.

        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.

    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.:

            <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 has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here