午後わてんのブログ

ベランダ菜園とWindows用アプリ作成(WPFとC#)

WPFでフォルダ選択のダイアログボックスみたいなの作りたい

TreeViewとTreeViewItemを使ってフォルダをツリー表示して、選択フォルダのパスを取得したい、バインディングとか無しで

f:id:gogowaten:20191010113812g:plain

TreeViewItemを使ってフォルダをツリー表示、パスを取得



TreeView.Items

                  ┣TreeViewItem.Items

                  ┃              ︙

                  ┣TreeViewItem.Items

                  ┃                          ┣TreeViewItem.Items

                  ┃                          ┃                 ︙

                  ┃                          ┗TreeViewItem.Items

                  ┗TreeViewItem.Items

                                                ┣TreeViewItem.Items

                                                ┣TreeViewItem.Items

                                                ┃             ︙

                                                ┗TreeViewItem.Items

 

TreeView、TreeViewItem、DirectoryInfo

この3つを使って作った

 

TreeViewのItemsには

TreeViewItemをたくさん入れることができる

 

TreeViewのItemsに何か一つでも入っていると▷が表示される

▷をクリックでツリーが展開される

 

DirectoryInfoクラス

System.IO.DirectoryInfo

フォルダのフルパスやサブフォルダ一覧とかを使える

 

 

github.com

 

xaml

  <Grid>
    <Grid.ColumnDefinitions>
      <ColumnDefinition/>
      <ColumnDefinition Width="100"/>
    </Grid.ColumnDefinitions>

    <TreeView Name="MyRoot"/>
    <StackPanel Grid.Column="1">
      <Button Content="test" Name="ButtonTest" Click="ButtonTest_Click"/>
    </StackPanel>
  </Grid>

 gridを縦分割して、TreeViewとButtonを追加しただけ

 

cs

using System.Windows;
using System.Windows.Controls;

namespace _20191010_TreeView3
{
    /// <summary>
    /// MainWindow.xaml の相互作用ロジック
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();

            //TreeViewにルートで表示するフォルダを追加
            var item = new DirectoryTreeItem(new System.IO.DirectoryInfo(@"D:\ブログ用"));
            MyRoot.Items.Add(item);
        }

        //確認用
        private void ButtonTest_Click(object sender, RoutedEventArgs e)
        {
            var neko = (DirectoryTreeItem)MyRoot.Items[0];
            var inu = (TreeViewItem)neko.Items[0];
            var item = (DirectoryTreeItem)MyRoot.SelectedItem;
            if (item == null) return;
            string str = item.ToString();
            string dir = item.DirectoryInfo.FullName;
        }
    }



    public class DirectoryTreeItem : TreeViewItem
    {
        public readonly System.IO.DirectoryInfo DirectoryInfo;
        private bool IsAdd;//サブフォルダを作成済みかどうか
        private TreeViewItem Dummy;//ダミーアイテム


        public DirectoryTreeItem(System.IO.DirectoryInfo info)
        {
            DirectoryInfo = info;
            Header = info.Name;

            //サブフォルダが1つでもあれば
            if (info.GetDirectories().Length > 0)
            //展開できることを示す▷を表示するためにダミーのTreeViewItemを追加する
            {
                Dummy = new TreeViewItem();
                Items.Add(Dummy);
            }

            //イベント、ツリー展開時
            //サブフォルダを追加
            this.Expanded += (s, e) =>
            {
                if (IsAdd) return;//追加済みなら何もしない
                AddSubDirectory();
            };
        }

        //サブフォルダツリー追加
        public void AddSubDirectory()
        {
            Items.Remove(Dummy);//ダミーのTreeViewItemを削除

            //すべてのサブフォルダを追加
            System.IO.DirectoryInfo[] directories = DirectoryInfo.GetDirectories();
            for (int i = 0; i < directories.Length; i++)
            {
                //隠しフォルダ、システムフォルダは除外する
                var fileAttributes = directories[i].Attributes;
                if ((fileAttributes & System.IO.FileAttributes.Hidden) == System.IO.FileAttributes.Hidden ||
                        (fileAttributes & System.IO.FileAttributes.System) == System.IO.FileAttributes.System)
                {
                    continue;
                }
                //追加
                Items.Add(new DirectoryTreeItem(directories[i]));
            }
            IsAdd = true;//サブフォルダ作成済みフラグ
        }


        public override string ToString()
        {
            return DirectoryInfo.FullName;
        }
    }
}
//ファイルの属性を取得、設定する - .NET Tips(VB.NET, C#...)
//https://dobon.net/vb/dotnet/file/fileattributes.html
//WPF4.5入門 その26 「TreeViewコントロール その2」 - かずきのBlog @hatena
//https://blog.okazuki.jp/entry/20130409/1365479109
//WPF TreeViewを使ってみた(2) - Qiita
//https://qiita.com/kuro4/items/552b780bb2832a8de5a6

 

ルートに表示したいフォルダパスからDirectoryInfoを作成して、これをTreeViewItemを継承したDirectoryTreeItemクラスに渡して作成したのを、TreeViewのItemsに追加する

 

今回はDドライブにあるブログ用って名前のフォルダを表示するので

16行目で

var item = new DirectoryTreeItem(new System.IO.DirectoryInfo(@"D:\ブログ用"));

17行目で

MyRoot.Items.Add(item);

これで表示される

 

サブフォルダ用のTreeViewItemの作成するタイミング

▷をクリックして展開したときにしたかったので、展開時のイベントExpandedで行っているのが56行目

サブフォルダ用のItemは1回作れば十分なので、2回目以降の展開時は作成しないようにフラグでチェック

 

サブフォルダの存在を示す▷の表示

サブフォルダ用のItemを作成するのは、展開前だけど▷の表示だけはしたいので、仮(dummy)のTreeViewItemを1つ作成して、入れておくことで表示している、展開したときにdummyを削除

 

f:id:gogowaten:20191010095629p:plain

この選択状態で確認

 

f:id:gogowaten:20191010111723p:plain

フォルダのフルパス取得

TreeViewのSelectedItemプロパティをキャストして、DirectoryInfoのFullNameプロパティで選択フォルダのパス

"D:\\ブログ用\\作物\\スイートバジル"

が取得できた、28行目

 

 

参照したところ

dobon.net

blog.okazuki.jp

 

qiita.com

ダミーのTreeViewItemで▷を表示する方法はこちらから

 

 

日記

WPFってファイル選択のダイアログボックスはあるんだけど、フォルダ選択のダイアログはないんだよねえ、んでWPF フォルダダイアログとかでググると、Windows API Code Packっていうのを使う方法が見つかるので試してみると

f:id:gogowaten:20191010114624p:plain

Windows API Code Pack

こんなふうに見つかるので、ライセンスの表示をクリックすると

f:id:gogowaten:20191010114751p:plain

ライセンスの表示結果

表示できません、ページが消滅しているみたいなので

web.archiveで見たら

web.archive.org

英語かあ…Google翻訳してみても

f:id:gogowaten:20191010115606p:plain

なんかいっぱい書いてある、フォルダ選択したいだけなのに…

 

 

他の方法でWindowsフォームを参照に追加してそれを使うとかもあるけど、フォルダ選択したいだけなのに、そんなめんどくさいことしたくない

良さそうなのはTreeViewとバインディングを使った方法があるけど、難しくてわからん

ってことで今回の方法になった、これでもフォルダ選択のダイアログボックスみたいなのできるんじゃないかなあ

 

 

関連記事

2019/10/13は3日後

gogowaten.hatenablog.com