65.9K
CodeProject 正在变化。 阅读更多。
Home

WPF 文件夹浏览器

starIconstarIconstarIcon
emptyStarIcon
starIcon
emptyStarIcon

3.67/5 (2投票s)

2012 年 4 月 26 日

CPOL

2分钟阅读

viewsIcon

31748

downloadIcon

1022

这是“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" />  

关注点

对于模板化控件来说,附加行为非常实用。

历史

目前还没有。

© . All rights reserved.