もっと詳しく

Windowsでフォルダやファイルを探すときに使う、おなじみのフォルダエクスプローラーですが、WndowsApiCodePackを使えば簡単に自作プログラムに組み込むことが可能です。

今回は、WndowsApiCodePackを用いたフォルダエクスプローラーの実現方法と使い方について、サンプルプログラムを使って解説したいと思います。

フォルダエクスプローラーとは

Windowsでパソコン上のフォルダやファイルを探すときに使うやつです。

下記はWindows11のスクリーンショットになります。

今回のデモプログラムについて

今回紹介するデモプログラムは、以下の様になります。

簡単に説明すると、フォルダのパスを入力して「移動」ボタンをクリックすると、そのフォルダに移動します。

フォルダ階層を移動したり、ファイルをクリックすることでイベントが発生するのですが、そのイベントがリアルタイムで一番下に表示されるようになっています。

基本的にWondows上のフォルダエクスプローラーが画面に張り付いているだけなので、ファイルのコピーや削除など、Windowsが備えている機能はそのまま利用できます。

WndowsApiCodePackのインストール方法

インストール方法は、こちらの記事で紹介した内容と全く同じで、 Visual Studioの Nuget でライブラリを選択し、インストールします。

Nugetの検索機能で ”WindowsAPICodePack”を検索し、表示される一覧の中かから「Microsoft-WindowsAPICodePack-Shell」を選び、インストールします。

WindowsAPICodePack-Shell というよく似た名前のライブラリが表示されていますが、すこし挙動が異なるので、間違わないようご注意ください。

インストールを開始すると、依存関係のあるMicrosoft-WindowsAPICodePack-Core も一緒にインストールされます。

インストールが成功すると、ExplorerBrowser というコントロールが使えるようになります。

ただ、ツールボックスには表示されないので、XAML上で直接指定する必要があります。

使うために必要なプログラム上の記述

まず、このフォルダエクスプローラーはWindowsForm専用のものなので、WPFでは少しだけ考慮が必要です。

XAML上の記述

WPFでは、WindowsForm専用のコントロールを使うためのコントロール(WindowsFormsHost)が用意されていますので、これを使います。

まず最初に、XAMLの参照設定の部分に以下の1行を挿入します。

xmlns:wfc="clr-namespace:Microsoft.WindowsAPICodePack.Controls.WindowsForms;assembly=Microsoft.WindowsAPICodePack.Shell"

次に、ExplorerBrowser コントロールを、 <WindowsFormsHost></<WindowsFormsHost>のタグで挟みます。

<WindowsFormsHost x:Name="uxWindowsFormsHost">
     <wfc:ExplorerBrowser x:Name="uxBrowser"></wfc:ExplorerBrowser>
</WindowsFormsHost>

ついでにコントロールに名前を付けておきましょう。

こうすることで、この名前を使ってプログラムから操作することが可能になります。

今回は uxBrowser という名前を付けました。

C#コード上の記述

最後に、C#のソースコードの冒頭に次の参照設定を記述します。

using Microsoft.WindowsAPICodePack.Controls;
using Microsoft.WindowsAPICodePack.Controls.WindowsForms;
using Microsoft.WindowsAPICodePack.Shell;

プログラムからの制御方法

プログラムに組み込んだフォルダエクスプローラーは、張り付けるだけでは何も表示されません。

まず最初にNavigate メソッドを使って何らかのフォルダパスを表示しなければなりません。

一旦何らかのフォルダが表示された後は、Windowsのフォルダエクスプローラーと同様に画面から操作出来るようになります。

任意のフォルダを表示する

Navigateメソッドの引数にパスを指定すれば、そのパスが表示されます。

但し、Navigateは ShellObjects という型でしかフォルダパスを受取ってくれないので、次の記述によりNavigate が受け取れる型に変換してから渡すようにします。

   ShellFileSystemFolder.FromFolderPath(path)

uxBrowser.Navigate(ShellFileSystemFolder.FromFolderPath(@"C:\"));

選択(クリック)されたフォルダやファイルを取得する

フォルダエクスプローラー上でフォルダやファイルを選択(クリック)すると、Explorer_SelectionChanged イベントが発行されます。

イベントハンドラの sender を使うと、選択されたフォルダやファイルを取得出来ます。

sender を ExplorerBrowser にキャストすると、Items プロパティが参照出来るようになります。

ここには、表示中フォルダにあるフォルダとファイルの全てが配列(Collection)で格納されており、先頭の要素(Items[0])にクリックしたフォルダ又はファイルが格納されています。

例えば、選択されたフォルダ又はファイルをテキストボックスに表示するには、次の様に記述します。

private void Explorer_SelectionChanged(object sender, EventArgs e)
{
    ShellObjectCollection items = ((ExplorerBrowser)sender).Items;
    uxTextBox.Text = items[0].ParsingName;
}

ちなみに、ファイル名だけを取得したい場合は、ParsingNameの代わりにName を参照します。

//フルパスを取得
items[0].ParsingName;
//ファイル名のみを取得
items[0].Name;

重要なイベントハンドラ

フォルダエクスプローラーを自作プログラムに組み込んで使う場合、何が選択(クリック)されのかを把握したくなります。

また、大量のファイルやフォルダが格納されていたり、ネットワークドライブ経由でフォルダ移動を行うような場合は数秒間待たされることもよくあるので、移動し終わったかが把握できれば便利です。

フォルダエクスプローラーにはいろいろなイベントがサポートされていますが、その中から重要な3種類について紹介しておきます。

イベントハンドラ名 内容
Load コントロールが表示された初回時に発生
NavigationComplete 指定したフォルダへの移動が完了した時に発生
Explorer_SelectionChanged フォルダ又はファイルが選択(クリック)された時に発生

SelectionChangedの挙動について

実は、SelectionChanged イベントはフォルダ又はファイルを選択(クリック)するごとに3回発行され、Itemsの内容がそれぞれについて異なります。

1回目は現在選択されている内容が、2回目と3回目は今回選択した内容が Items に格納されます。

2回目のイベントが発行された後で、NavigationComplete イベントが発行されてから、3回目のSelectionChanged が発行されるため、タイミングを厳密に管理する必要がある場合は、NavigationComplete イベント後のSelectionChanged から Items を取得する必要があります。

また、フォルダの階層を移動(フォルダをダブルクリック)する場合は、SelectionChanged いベントが4回発行されます。

1~3回目までは前述の挙動と同じですが、4回目はフォルダ階層に移動した後の内容がItemsに格納されています。

デモプログラムのソースコード

それでは、今回の解説で使ったデモプログラムを紹介しておきます。

まず、XAMLのソースコードです。

<Window x:Class="WindowsApiCodePackSample.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WindowsApiCodePackSample"
        xmlns:wfc="clr-namespace:Microsoft.WindowsAPICodePack.Controls.WindowsForms;assembly=Microsoft.WindowsAPICodePack.Shell"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="50"/>
            <RowDefinition Height="71*"/>
            <RowDefinition Height="25*"/>
        </Grid.RowDefinitions>
        <WindowsFormsHost x:Name="uxWindowsFormsHost" Grid.Row="1">
            <wfc:ExplorerBrowser x:Name="uxBrowser"></wfc:ExplorerBrowser>
        </WindowsFormsHost>
        <TextBox x:Name="uxTextBox" HorizontalAlignment="Left" Margin="35,0,0,0" TextWrapping="Wrap" VerticalAlignment="Center" Width="660" Height="18"/>
        <Button Content="移動" HorizontalAlignment="Left" Margin="700,0,0,0" VerticalAlignment="Center" Width="69" Height="20" Click="Button_Click"/>
        <ListBox x:Name="uxListBox" Grid.Row="2" Background="#555555" Foreground="White" Margin="0,10,0,0"/>
    </Grid>
</Window>

以下はC#のソースコードになります。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.IO;

//ExplorerBrowserを表示する場合に必要
using Microsoft.WindowsAPICodePack.Controls;
using Microsoft.WindowsAPICodePack.Controls.WindowsForms;
using Microsoft.WindowsAPICodePack.Shell;

namespace WindowsApiCodePackSample
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        private int _lineCnt = 0;
        public MainWindow()
        {
            InitializeComponent();

            //各種イベントハンドラの設定
            uxBrowser.NavigationComplete += Explorer_NavigationComplete;
            uxBrowser.Load += Explorer_Load;
            uxBrowser.SelectionChanged += Explorer_SelectionChanged;

        }

        /// <summary>
        /// "移動"ボタンクリック処理
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Button_Click(object sender, RoutedEventArgs e)
        {
            if (uxTextBox.Text != "")
            { 
                Navigate(uxTextBox.Text);
            }
        }

        /// <summary>
        /// ナビゲート
        /// 指定したパスへの移動
        /// </summary>
        /// <param name="path"></param>
        public void Navigate(string path)
        {
            uxBrowser.Navigate(ShellFileSystemFolder.FromFolderPath(path));
        }

        /// <summary>
        /// ロードイベント
        /// コントロール表示時に1度だけ呼び出される
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Explorer_Load(object sender, EventArgs e)
        {
            WriteLine("Loadイベント");
        }

        /// <summary>
        /// ナビゲート完了イベント
        /// 指定したフォルダへの移動が完了した時に呼び出される
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Explorer_NavigationComplete(object sender, NavigationCompleteEventArgs e)
        {
            WriteLine("NavigationCompleteイベント");
        }

        /// <summary>
        /// 選択項目(ファイル/フォルダ)の変更イベント
        /// ファイル/フォルダが選択された時に呼び出される
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Explorer_SelectionChanged(object sender, EventArgs e)
        {
            ShellObjectCollection items = ((ExplorerBrowser)sender).Items;
            uxTextBox.Text = items[0].ParsingName;
            WriteLine("SelectionChangedイベント");
        }

        /// <summary>
        /// メッセージ出力
        /// </summary>
        /// <param name="message"></param>
        private void WriteLine(string message)
        {
            uxListBox.Items.Add(string.Format("{0:D3}:  {1}",++_lineCnt,message));
            uxListBox.ScrollIntoView(uxListBox.Items.GetItemAt(uxListBox.Items.Count - 1));
        }
    }
}

ちなみに、上記ソースコードを Microsoft .NET Framework で動作させる分には特に問題ありませんが、Visual Studio 2022 で.NET 6 を使う場合、C#のバージョンが10.0 になることで以下の警告が表示されます。

これは気にせず実行してもらっても大丈夫なのですが、もし警告が気になる場合は、以下の様にビックリマーク(!)を末尾に付加すると解決します。

まとめ

今回はWndowsApiCodePackに含まれるフォルダエクスプローラーを、自作プログラムに組み込んで使うための方法について解説しました。

今回解説した内容はごく一部ですが、これだけ把握しておけば、おおよその事が出来るのではないかと思います。

例えば、複数のフォルダをタブで切り替えられるファイラーのようなプログラムは、今回の知識があれば開発できます。

私自身、自作のファイラーに色々な便利機能を組み込んで使っています。

いずれ機会があれば、自作ファイラーについても紹介したいなと思っています。

皆さんも、今回の内容をもとに、自分だけのオリジナルファイラーを作ってみてはいかがでしょうか?

今回の記事が皆様のプログラミングのお役に立てれば幸いです。