WPF 文件夹浏览器
这是“WPF 文件夹浏览器”的替代方案。
介绍
这个版本的 WPF 文件夹浏览器引入了新的基本功能。
它提供了
- 双击选择功能
- 当 SelectedFolder 通过程序或通过文本框中的用户输入更改时,自动打开和滚动树状视图文件夹。
- 在按下 Enter 键时更新文本框绑定。
背景
Sean Ireson 非常喜欢原始解决方案,以至于他花费了大量精力对其进行改进,并将代码提供给原始发布者进行更新。 几年后,他很乐意通过电子邮件将他的新代码库发送给我,以节省我的时间。 现在我进行了一项小的更新,使其值得重新分享。
注意!
在下载此工具之前,你应该考虑一下是否可以只使用具有标准 Windows 外观和感觉的文件夹浏览器 - 因为如果可以,Mark Salsbery 已经提供了围绕 SHBrowseForFolder 函数 的 WPF 包装器。 我希望我能更好地搜索,这样就能找到这个 - 但对于其他搜索能力较弱的人来说,这里有一个指向 Mark 的很棒工具帖子的链接 WPF 文件夹浏览器通用对话框。 无论如何,如果你想要一个可以随意设置样式的对话框,请继续阅读。
使用代码
Sean 对原始代码所做的更改:
引入 TreeViewItemBehaviour 类,引入 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
}
通过引入 OnSelectedFolderChanged 和 Expand 函数来修改 BrowserViewModel。
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;
}
在 FolderBrowserDialog 的代码背后添加了一个文本框单击事件,该事件在双击文本框或树状视图节点时选择并关闭表单。
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;
}
}
我添加了 InputBindingManager 类,该类公然从这个 StackOverflow 答案 中借用,作者是 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();
}
}
}
这使得文本框能够在按下 Enter 键时通过在 XAML 代码中为文本框创建附加行为来更新绑定。
<TextBox Text="{Binding Path=SelectedFolder, UpdateSourceTrigger=PropertyChanged}"
local:InputBindingsManager.UpdatePropertySourceWhenEnterPressed="TextBox.Text"
MinHeight="25"
Margin="5"
VerticalContentAlignment="Center" />
关注点
对于模板化控件来说,附加行为非常实用。
历史
目前还没有。