2026年版 WinUIアプリ開発 Tips:FileOpenPickerがシンプルに―Windows App SDK 1.8以降の新しいファイル選択

スポンサーリンク

WinUI 3で「ファイルを開く」機能を実装するとき、少し前まで多くのサンプルではWindows.Storage.Pickers.FileOpenPickerを作成し、さらにHWNDを取得してInitializeWithWindowを呼ぶ必要がありました。UWPから移行した開発者には見慣れたAPIですが、WinUI 3のデスクトップアプリでは「なぜファイル選択だけでウィンドウハンドルの初期化が必要なのか」と戸惑いやすい部分です。

Windows App SDK 1.8以降では、WinUI 3向けにMicrosoft.Windows.Storage.Pickers名前空間の新しいストレージピッカーが利用できます。WindowIdをコンストラクターへ渡す設計になり、従来のInitializeWithWindowが不要になりました。今回は新旧の違いを整理しながら、ファイルを1つ選択して内容を読み込むところまで実装します。

従来方式では、なぜHWNDの初期化が必要だったのか

従来のWindows.Storage.Pickers.FileOpenPickerはUWPでも利用されてきたWinRT APIです。ところがWinUI 3デスクトップアプリにはUWPのCoreWindowがありません。そのため、ファイル選択ダイアログを「どのデスクトップウィンドウの所有物として表示するのか」を別途教える必要があります。

Microsoftの公式ドキュメントでは、Windows App SDK 1.7以前、または従来のWindows.Storage.Pickersを利用する場合、WindowNative.GetWindowHandleでHWNDを取得し、InitializeWithWindow.Initializeでピッカーと関連付ける方法が示されています。この処理を忘れると、WinUI 3では例外になったり、正常に表示されなかったりする可能性があります。

従来のコード

var picker = new Windows.Storage.Pickers.FileOpenPicker();

picker.FileTypeFilter.Add(".txt");

var hwnd = WinRT.Interop.WindowNative.GetWindowHandle(this);
WinRT.Interop.InitializeWithWindow.Initialize(picker, hwnd);

var file = await picker.PickSingleFileAsync();

if (file is not null)
{
    var text = await Windows.Storage.FileIO.ReadTextAsync(file);
}

この方式が間違いというわけではありません。既存アプリやWindows App SDK 1.7以前を対象にする場合には現在も必要です。ただ、新しくWinUI 3アプリを作るなら、Windows App SDK 1.8以降のピッカーを先に検討できます。

Windows App SDK 1.8以降ではWindowIdを直接渡せる

新しいAPIはMicrosoft.Windows.Storage.Pickers名前空間にあります。ポイントは、ピッカー生成時にAppWindow.Idを渡すことです。WinUI 3のWindowではAppWindowからWindowIdを取得できるため、HWNDを自分で取り出して初期化する処理が不要になります。

using Microsoft.Windows.Storage.Pickers;

private async void OpenFile_Click(object sender, RoutedEventArgs e)
{
    var picker = new FileOpenPicker(this.AppWindow.Id)
    {
        SuggestedStartLocation = PickerLocationId.DocumentsLibrary,
        CommitButtonText = "開く",
        ViewMode = PickerViewMode.List
    };

    picker.FileTypeFilter.Add(".txt");

    var result = await picker.PickSingleFileAsync();

    if (result is null)
    {
        return;
    }

    string text = await File.ReadAllTextAsync(result.Path);
    FileContentTextBox.Text = text;
}

この例では、テキストファイルだけを選択対象にし、選ばれたファイルの内容をFile.ReadAllTextAsyncで読み込んでいます。コード例はAPIの組み合わせを示すもので、この記事では実機での実行結果を保証するものではありません。利用しているWindows App SDKのバージョンと対象環境を確認して試してください。

コードを順番に見てみる

new FileOpenPicker(this.AppWindow.Id)が新方式の中心です。ピッカーが属するウィンドウをWindowIdで最初から指定します。従来方式のように、生成後にHWNDを使って関連付ける必要がありません。

SuggestedStartLocationでは、ピッカーを開いたときの候補位置を指定できます。この例ではドキュメントライブラリを指定しています。指定場所がユーザー環境に存在しない場合は、公式ドキュメント上、ドキュメントライブラリへフォールバックします。

FileTypeFilterは選択対象の拡張子です。複数形式を扱うなら.txt、.jsonなどを追加できます。フィルターを指定しなければ既定ではすべてのファイルが対象になります。

PickSingleFileAsyncの戻り値はPickFileResultです。従来APIのStorageFileとは異なり、新しいピッカーは軽量なパスベースの結果を返します。選択をキャンセルした場合はnullになるため、ファイルを読む前に必ず確認します。

Before / Afterで見ると違いが分かりやすい

従来方式では「FileOpenPickerを生成 → 拡張子を設定 → HWNDを取得 → InitializeWithWindowで関連付け → PickSingleFileAsync」という流れでした。新方式では「WindowIdを渡してFileOpenPickerを生成 → 条件を設定 → PickSingleFileAsync」となります。

削減されたのは数行ですが、重要なのはコード量だけではありません。「デスクトップアプリのウィンドウへWinRT UIを後付けで関連付ける」というinteropの知識を、通常のファイル選択処理から切り離せます。WinUI 3向けのAPIとして意図がコードに現れるため、新しく参加した開発者にも読みやすくなります。

複数ファイルやフォルダー選択にも同じ考え方を使える

複数ファイルならPickMultipleFilesAsyncを利用できます。戻り値の各PickFileResultからPathを取得できます。フォルダーを選択する場合はFolderPickerをAppWindow.Id付きで生成し、PickSingleFolderAsyncを呼びます。

つまり、ファイルを1つ開く処理だけの専用改善ではありません。インポート、エクスポート、ログ保存先の選択、バックアップフォルダー指定など、業務アプリでよく使うファイル・フォルダー選択を同じ設計で揃えられます。

注意点1:Windows App SDK 1.8以降が必要

Microsoft.Windows.Storage.PickersのAPIはWindows App SDK 1.8で導入されました。1.7以前を参照するプロジェクトへ、そのまま今回のコードをコピーしても利用できません。既存プロジェクトでは、まずNuGetで参照しているWindows App SDKのバージョンを確認してください。

また、2026年9月24日にWindows App SDK 1.8.12が公開され、1.8系列ではこれが最終servicing releaseとされています。新規開発では「記事に書かれたバージョンを固定的に選ぶ」のではなく、利用時点でサポート対象となっている安定版と、そのリリースノートを確認することが重要です。

注意点2:新旧のFileOpenPickerを混同しない

名前がどちらもFileOpenPickerなので、usingディレクティブには注意が必要です。新方式はMicrosoft.Windows.Storage.Pickers、従来方式はWindows.Storage.Pickersです。検索で古いサンプルを見つけたときは、クラス名だけで判断せず名前空間を確認すると混乱を避けられます。

さらに、従来APIはStorageFileを返すのに対し、新APIはPickFileResultからパスを受け取る設計です。既存コードがStorageFile固有のAPIへ強く依存している場合は、単純な置換ではなく周辺処理も含めて移行を検討してください。

注意点3:管理者権限が必要なアプリでは要件を確認する

Microsoftのドキュメントでは、新しいWindows App SDKピッカーはWinUI 3向けの推奨方式として案内されています。一方、UWP向けドキュメントでは、昇格されたデスクトップアプリなどでは従来のWindows.Storage.Pickersを利用するケースも示されています。すべての既存アプリを機械的に新APIへ置き換えるのではなく、アプリの実行権限や既存処理を確認して判断してください。

実務では「ファイル選択」と「読み込み処理」を分ける

サンプルではボタンイベント内でファイルを読みましたが、実務ではピッカー処理とファイル解析処理を分離すると扱いやすくなります。ピッカーはユーザーにパスを選んでもらう責務、サービス層はそのパスからJSONやCSVなどを読み込んで検証する責務、と分けます。

こうしておけば、将来ドラッグ&ドロップや「最近使ったファイル」から開く機能を追加しても、ファイル解析部分を再利用できます。新しいピッカーでinteropコードを減らすことと同じように、UI固有の処理を小さく保つことが、長く保守できるWinUI 3アプリにつながります。

まとめ

Windows App SDK 1.8以降では、WinUI 3向けのMicrosoft.Windows.Storage.Pickersを使うことで、WindowIdを直接指定してファイル・フォルダー選択ダイアログを開けます。従来のWindows.Storage.Pickersで必要だったHWND取得とInitializeWithWindowを通常の実装から外せるのが大きな違いです。

覚えておきたいのは、「FileOpenPicker」という名前だけでは新旧を区別できないことです。Windows App SDKのバージョン、名前空間、戻り値の型をセットで確認してください。新規のWinUI 3アプリなら新しいピッカーを第一候補にし、既存アプリでは依存しているAPIや実行条件を見ながら段階的に移行するのが安全です。

参考資料

Please follow and like us:

コメント

タイトルとURLをコピーしました