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 }
Modification to the BrowserViewModel by introducing the OnSelectedFolderChanged and Expand functions.
public string SelectedFolder
{
get
{
return _selectedFolder;
}
set
{
_selectedFolder = value;
OnPropertyChanged("SelectedFolder");
OnSelectedFolderChanged();
}
}
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)
{
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.