一键导入
dotnet-wpf-pdf-preview
こんなときに使う: Use when adding PDF upload and inline WebView2 preview to a WPF app with MVVM file selection and async initialization.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
こんなときに使う: Use when adding PDF upload and inline WebView2 preview to a WPF app with MVVM file selection and async initialization.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
基于 SOC 职业分类
モダン C#(12+)で record、パターンマッチング、合成、Result 型エラーハンドリングを使った 慣用的で高性能なコードを書く。こんなときに使う: 新規 C# コードの作成、API 設計、 または C# 12+ イディオムへのリファクタリング。
こんなときに使う: WPFアプリケーションに社員番号入力ダイアログを追加し、 DPAPIで暗号化された設定として社員IDを安全に保存したいとき。
こんなときに使う: 繰り返し発生する業務問い合わせ(監査、アンケート、コンプライアンス調査)に対して、 過去の回答実績とエビデンスを活用して回答するためのテンプレートスキル。 再利用可能な7ステップワークフローとスキャフォールディングファイルを提供する。
こんなときに使う: PDFテキスト抽出、Optical Character Recognition (OCR)、結合/分割、フォーム処理をuvベースの再現可能コマンドで実行したいときに使う。
こんなときに使う: Migrate Access SQL to Oracle, generate .NET C# code. Use when converting Access queries to Oracle.
こんなときに使う: .NETドメイン層で汎用的な重み付きフィールドマッチングとスコアリングを実装。
| name | dotnet-wpf-pdf-preview |
| description | こんなときに使う: Use when adding PDF upload and inline WebView2 preview to a WPF app with MVVM file selection and async initialization. |
| license | MIT |
| metadata | {"author":"RyoMurakami1983","tags":["dotnet","wpf","csharp","mvvm","webview2","pdf"],"invocable":false} |
.NET WPFアプリケーションにPDFファイルアップロードとインラインプレビューを追加するためのエンドツーエンドワークフロー:WebView2ベースのPDFレンダリング、CommunityToolkit.MvvmによるMVVMファイル選択、イベントベースのViewModel→View通信、エラーハンドリング付き非同期WebView2初期化。
以下の場合にこのスキルを使用してください:
dotnet-wpf-secure-config — DPAPI暗号化基盤(認証情報の保存用)dotnet-wpf-dify-api-integration — アップロードしたPDFをDify APIに送信してOCR抽出dotnet-oracle-wpf-integration — 抽出したPDFデータをOracleデータベースに保存git-commit-practices — 各ステップをアトミックな変更としてコミットWebView2 NuGetパッケージを追加し、分割パネルのXAMLレイアウトを作成するときに使用します。
WebView2パッケージをインストールし、左側にPDFプレビュー、右側にコンテンツエリアの2カラムGridを作成します。
# Install WebView2 NuGet package
Install-Package Microsoft.Web.WebView2
YourApp/
├── Views/
│ └── MainWindow.xaml # 🆕 WebView2付き2カラムレイアウト
│ └── MainWindow.xaml.cs # 🆕 WebView2初期化 + ナビゲーション
└── ViewModels/
└── MainViewModel.cs # 🆕 ファイル選択 + パス管理
XAMLレイアウトテンプレート — 2カラム分割パネル:
<Window x:Class="YourApp.Views.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:wv2="clr-namespace:Microsoft.Web.WebView2.Wpf;assembly=Microsoft.Web.WebView2.Wpf"
Title="PDF Preview" Height="700" Width="1200">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*"/> <!-- Left: PDF Preview -->
<ColumnDefinition Width="1.5*"/> <!-- Right: Content -->
</Grid.ColumnDefinitions>
<Grid Grid.Column="0" Margin="5">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/> <!-- Upload Button -->
<RowDefinition Height="*"/> <!-- PDF Preview -->
</Grid.RowDefinitions>
<Button Grid.Row="0" Content="Upload PDF"
Command="{Binding UploadPdfCommand}"
Background="#2196F3" Foreground="White" FontWeight="Bold"/>
<Border Grid.Row="1" BorderBrush="#CCCCCC" BorderThickness="1">
<wv2:WebView2 x:Name="PdfWebView" />
</Border>
</Grid>
<!-- Right column: your content area -->
<Grid Grid.Column="1" Margin="5">
<!-- Add your application content here -->
</Grid>
</Grid>
</Window>
WebView2でx:Nameを使用する理由: WebView2は命令的な初期化(EnsureCoreWebView2Async)とナビゲーション(CoreWebView2.Navigate)が必要です。これらのAPIにはバインド可能な代替がないため、x:NameはMVVMの許容される例外です。
Values: 基礎と型 / 継続は力
OpenFileDialogによるPDFファイル選択を処理し、Viewに通知するViewModelを作成するときに使用します。
CommunityToolkit.Mvvmを使用してMainViewModelを作成し、[ObservableProperty]で状態管理、[RelayCommand]でアップロードアクションを実装します。PdfPathChangedイベントがViewModel→View通信を橋渡しします。
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using System;
namespace YourApp.ViewModels
{
public partial class MainViewModel : ObservableObject
{
/// <summary>
/// Event to notify View when a new PDF is selected.
/// Code-behind subscribes to this for WebView2 navigation.
/// </summary>
public event EventHandler<string>? PdfPathChanged;
[ObservableProperty]
private string pdfFilePath = string.Empty;
[ObservableProperty]
private bool isPdfLoaded;
[RelayCommand]
private void UploadPdf()
{
var dialog = new Microsoft.Win32.OpenFileDialog
{
Filter = "PDF files (*.pdf)|*.pdf",
Title = "Select PDF file"
};
if (dialog.ShowDialog() == true)
{
PdfFilePath = dialog.FileName;
IsPdfLoaded = true;
PdfPathChanged?.Invoke(this, PdfFilePath);
}
}
}
}
バインディングではなくイベントパターンを使用する理由: WebView2のSourceプロパティはローカルファイルURLに対する信頼性の高い双方向バインディングをサポートしていません。イベントパターンによりナビゲーションのタイミングとエラーハンドリングを明示的に制御できます。
Values: 基礎と型 / ニュートラル
ウィンドウのcode-behindでWebView2の初期化とPDFナビゲーションを配線するときに使用します。
WebView2を非同期で初期化し、ViewModelのPdfPathChangedイベントをサブスクライブしてナビゲーションする最小限のcode-behindを作成します。
using System;
using System.Windows;
namespace YourApp.Views
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
var viewModel = new MainViewModel();
DataContext = viewModel;
InitializeWebView();
viewModel.PdfPathChanged += OnPdfPathChanged;
}
private async void InitializeWebView()
{
try
{
await PdfWebView.EnsureCoreWebView2Async(null);
}
catch (Exception ex)
{
MessageBox.Show(
$"WebView2 Runtime not found.\n\n" +
$"Please install the WebView2 Runtime from:\n" +
$"https://developer.microsoft.com/microsoft-edge/webview2/\n\n" +
$"Error: {ex.Message}",
"WebView2 Error",
MessageBoxButton.OK,
MessageBoxImage.Warning);
}
}
private void OnPdfPathChanged(object? sender, string pdfPath)
{
if (PdfWebView.CoreWebView2 != null)
{
PdfWebView.CoreWebView2.Navigate($"file:///{pdfPath}");
}
}
}
}
ここでasync voidが許容される理由: InitializeWebViewはファイア・アンド・フォーゲットのUI初期化です。try/catchがすべての失敗ケースを処理します。これはasync voidが適切な数少ないケースの一つ — イベント的なUI起動です。
Values: ニュートラル / 基礎と型
すべてのXAML名前空間とバインディングが正しく接続されていることを確認するときに使用します。
WebView2のXAML名前空間が宣言され、ボタンコマンドがViewModelにバインドされていることを確認します。
必須XAML名前空間(Windowタグ内):
xmlns:wv2="clr-namespace:Microsoft.Web.WebView2.Wpf;assembly=Microsoft.Web.WebView2.Wpf"
バインディングチェックリスト:
<!-- ✅ 正しい — ボタンがViewModelコマンドにバインド -->
<Button Command="{Binding UploadPdfCommand}" Content="Upload PDF" />
<!-- ❌ 間違い — code-behindのクリックハンドラ -->
<Button Click="OnUploadClick" Content="Upload PDF" />
<!-- ✅ 正しい — WebView2はx:Nameを使用(MVVM例外) -->
<wv2:WebView2 x:Name="PdfWebView" />
<!-- ❌ 間違い — Sourceを直接バインドしようとしている -->
<wv2:WebView2 Source="{Binding PdfFileUri}" />
Values: 基礎と型 / ニュートラル
デプロイメントと実運用シナリオの堅牢性を追加するときに使用します。
3つの主な障害シナリオを処理します:ランタイム不在、大きなファイル、再アップロード状態。
WebView2 Runtimeが見つからない場合:
// ✅ Graceful fallback when WebView2 Runtime is not installed
private async void InitializeWebView()
{
try
{
await PdfWebView.EnsureCoreWebView2Async(null);
}
catch (Exception)
{
// Show fallback UI or download link
PdfWebView.Visibility = Visibility.Collapsed;
// Show a TextBlock with download instructions instead
}
}
再アップロード(状態リセット):
[RelayCommand]
private void UploadPdf()
{
var dialog = new Microsoft.Win32.OpenFileDialog
{
Filter = "PDF files (*.pdf)|*.pdf",
Title = "Select PDF file"
};
if (dialog.ShowDialog() == true)
{
// ✅ Reset state before loading new PDF
PdfFilePath = dialog.FileName;
IsPdfLoaded = true;
PdfPathChanged?.Invoke(this, PdfFilePath);
}
}
大きなPDFファイル — WebView2はChromium内蔵のPDFビューアを通じて大きなPDFをネイティブに処理します。特別な処理は不要ですが、ローディングインジケータの表示を検討してください:
private void OnPdfPathChanged(object? sender, string pdfPath)
{
if (PdfWebView.CoreWebView2 != null)
{
// Chromium PDF viewer handles large files with streaming
PdfWebView.CoreWebView2.Navigate($"file:///{pdfPath}");
}
}
Values: ニュートラル / 継続は力
生成されたコードを本番デプロイ用に準備するときに使用します。
出荷前にこれらのプレースホルダーを置き換えてください:
| 項目 | ファイル | 変更内容 | スキップした場合の影響 |
|---|---|---|---|
| 名前空間 | 全.csファイル | YourApp → 実際の名前空間 | ビルドエラー |
| ウィンドウタイトル | MainWindow.xaml | "PDF Preview" → 実際のタイトル | 汎用的なウィンドウタイトル |
| カラム比率 | MainWindow.xaml | 1* / 1.5* → 希望の比率 | レイアウトの不一致 |
| ボタンスタイル | MainWindow.xaml | テーマに合わせた色とフォント | UIの不統一 |
| アップロードフィルタ | MainViewModel.cs | 他のファイル種別を受け付ける場合のフィルタ | 不正なファイル種別 |
カスタマイズチェックリスト:
# Verify all placeholders are replaced
Select-String -Path "Views/*.xaml","Views/*.cs","ViewModels/*.cs" -Pattern "YourApp" -SimpleMatch
# Expected: 0 matches after customization
Values: 基礎と型 / 成長の複利
What: WebView2の初期化とナビゲーションはMainWindow.xaml.csに配置し、ViewModelには置かない。
Why: WebView2はx:Nameと命令的なAPI呼び出し(EnsureCoreWebView2Async、CoreWebView2.Navigate)が必要です。これはMVVMの許容される例外 — code-behindがViewModelイベントとWebView2 API間の薄いアダプタとして機能します。
Values: 基礎と型(MVVM例外の型)
What: ViewModelがPdfPathChangedイベントを発行し、code-behindがサブスクライブしてWebView2をナビゲート。
Why: ViewModelをテスト可能に保ちつつ(UI依存なし)、Viewにナビゲーションタイミングの明示的な制御を与えます。代替手段(Messenger、Behavior)はこのユースケースではメリットなく複雑さが増します。
Values: ニュートラル / 基礎と型
What: EnsureCoreWebView2AsyncをコンストラクタまたはLoadedイベントで呼び出し、最初のPDFアップロード時には呼び出さない。
Why: WebView2の初期化には100〜500msかかります。事前に実行することで、ユーザーが最初にアップロードをクリックしたときの目に見える遅延を回避します。
Values: 継続は力(先回りの準備)
EnsureCoreWebView2Async でWebView2を初期化する。WebView2.Source を直接バインドしない。命令的にナビゲートする。Problem: WebView2 RuntimeはWindows 11にはプリインストールされていますが、Windows 10やロックダウンされた企業マシンでは欠落している場合があります。
Solution: インストーラにWebView2 Evergreen Bootstrapperを含めるか、ダウンロードリンクを検出してユーザーに提示します。EnsureCoreWebView2Asyncは常にtry/catchで囲みます。
// ❌ 間違い — エラーハンドリングなし、Runtimeのないマシンでクラッシュ
await PdfWebView.EnsureCoreWebView2Async(null);
// ✅ 正しい — グレースフルフォールバック
try { await PdfWebView.EnsureCoreWebView2Async(null); }
catch (Exception ex) { ShowWebView2MissingMessage(ex); }
Problem: ローカルファイルURLに対して<wv2:WebView2 Source="{Binding PdfUri}" />を使用しても確実に動作しない。
Solution: イベントパターン(Step 2〜3)とCoreWebView2.Navigate()を使用して、ローカルファイルの確実なナビゲーションを実現します。
// ❌ 間違い — ローカルファイルにSourceをバインド
<wv2:WebView2 Source="{Binding PdfFileUri}" />
// ✅ 正しい — イベント経由の命令的ナビゲーション
PdfWebView.CoreWebView2.Navigate($"file:///{pdfPath}");
Problem: WebView2 Runtimeが見つからないか破損している場合、EnsureCoreWebView2Asyncが例外をスローし、未処理の例外クラッシュを引き起こす。
Solution: 常にtry/catchで囲み、RuntimeダウンロードURLを含むユーザーフレンドリーなメッセージを表示します。
What: MainWindow.xaml.csのボタンクリックハンドラでOpenFileDialogを直接開く。
Why It's Wrong: MVVM分離に違反。ファイル選択はアプリケーションロジックであり、UIレンダリングではありません。code-behindのファイルダイアログロジックはユニットテスト不可能です。
Better Approach: ViewModelの[RelayCommand]でファイル選択を処理。ダイアログ結果がViewModelプロパティを更新し、Viewがそれを監視します。
What: TextBox、Button、DataGridにx:Nameを追加してcode-behindで操作する。
Why It's Wrong: データバインディングをバイパスし、UIがcode-behindに密結合になります。すべてのx:Name参照はバインディングの機会を逃しています。
Better Approach: すべての標準WPFコントロールに{Binding}を使用。命令的APIが必要なコントロール(WebView2)にのみx:Nameを限定使用します。
// ❌ 間違い — 名前でコントロールを操作
StatusLabel.Text = "PDF loaded";
UploadButton.IsEnabled = false;
// ✅ 正しい — ViewModelプロパティにバインド
[ObservableProperty] private string statusText = "Ready";
[ObservableProperty] private bool canUpload = true;
Microsoft.Web.WebView2 NuGetパッケージをインストールwv2 XAML名前空間を追加x:Name="PdfWebView"でWebView2コントロールを追加[RelayCommand]を持つViewModelを作成PdfPathChangedイベントを追加CoreWebView2.Navigate()でPDFにナビゲート| ファイル | 目的 | レイヤー |
|---|---|---|
MainWindow.xaml | 2カラムレイアウト + WebView2 | View |
MainWindow.xaml.cs | WebView2初期化 + ナビゲーション | View(code-behind) |
MainViewModel.cs | ファイル選択 + 状態管理 | ViewModel |
| シナリオ | コード |
|---|---|
| ローカルPDFファイル | CoreWebView2.Navigate($"file:///{path}") |
| 空白ページ | CoreWebView2.Navigate("about:blank") |
| 準備状態の確認 | if (PdfWebView.CoreWebView2 != null) |
[RelayCommand]と[ObservableProperty])