# 画面設計書 20-プレビューペイン

## 概要

本ドキュメントは、Roslyn（.NET Compiler Platform）のVisual Studio拡張機能における「プレビューペイン」の画面設計を記述するものである。

### 本画面の処理概要

プレビューペイン（PreviewPane）は、コードリファクタリングやクイックアクションの適用前に、変更内容をプレビュー表示するためのコントロールである。診断ID、タイトル、説明、差分ビューア、およびヘルプリンクを統合的に表示する。

**業務上の目的・背景**：
コードリファクタリングやクイックフィックスを適用する前に、変更内容を確認することは重要である。本ペインは、エラー電球やクイックアクションメニューから呼び出され、適用予定の変更を差分形式で表示する。また、診断情報へのリンクを提供することで、開発者が問題の詳細を理解できるようにする。

**画面へのアクセス方法**：
1. エディタ上でエラー/警告/提案のある箇所にカーソルを置く
2. 電球アイコンをクリックするか、Ctrl+. でクイックアクションメニューを開く
3. アクション項目にマウスを乗せると、プレビューペインが表示される

**主要な操作・処理内容**：
1. ヘッダー表示：診断ID、重大度アイコン、タイトルを表示
2. 説明展開：展開ボタンで詳細説明を表示/非表示
3. 差分プレビュー：コード変更の前後を差分形式で表示
4. ヘルプリンク：「Learn More」リンクで詳細情報ページを開く
5. オプション設定：スタイルオプション設定ページへの遷移

**画面遷移**：
- 遷移元：クイックアクションメニュー、ライトバルブメニュー
- 遷移先：ヘルプリンクからブラウザ、オプションボタンからオプションページ

**権限による表示制御**：
特別な権限制御はなし。

## 関連機能

| 機能No | 機能名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 22 | コードリファクタリング | 補助機能 | コード変更のプレビュー表示（タイトル・説明・Learn Moreリンク） |

## 画面種別

埋め込みコントロール（プレビュー表示用）

## URL/ルーティング

N/A（Visual Studio内部コントロールとして実装）

## 入出力項目

### 入力（コンストラクタパラメータ）

| 項目名 | 型 | 説明 |
|--------|------|------|
| severityIcon | Image | 重大度アイコン（エラー/警告/情報） |
| id | string | 診断ID（例：CA1000） |
| title | string | 診断タイトル |
| description | string | 詳細説明（オプション） |
| helpLink | Uri | ヘルプドキュメントへのリンク |
| helpLinkToolTipText | string | ヘルプリンクのツールチップ |
| previewContent | IReadOnlyList&lt;object&gt; | プレビューコンテンツ一覧 |
| logIdVerbatimInTelemetry | bool | テレメトリに診断IDをそのまま記録するか |
| uiShell | IVsUIShell | VS UIシェルサービス |
| optionPageGuid | Guid | オプションページGUID（オプション） |

### 出力（表示）

| 項目名 | 型 | 説明 |
|--------|------|------|
| AutomationName | string | アクセシビリティ名（"Preview pane"） |
| ParentElement | FrameworkElement | 親要素（依存関係プロパティ） |

## 表示項目

### ヘッダーセクション（HeaderStackPanel）

| 項目名 | コントロール | 説明 |
|--------|-------------|------|
| タイトルブロック | FlowDocumentScrollViewer | タイトル表示領域 |
| 展開ボタン | ToggleButton (ExpanderToggleButton) | 説明展開/折りたたみ |
| 重大度アイコン | Border (SeverityIconBorder) | エラー/警告/情報アイコン |
| 診断ID | TextBlock (IdTextBlock) + Hyperlink | クリック可能な診断ID |
| タイトルテキスト | Run (TitleRun) | 診断タイトル |

### 説明セクション（DescriptionDockPanel）

| 項目名 | コントロール | 説明 |
|--------|-------------|------|
| セパレーター | Separator | ヘッダーと説明の区切り |
| 説明パラグラフ | Paragraph (DescriptionParagraph) | 詳細説明テキスト |
| Learn Moreリンク | TextBlock + Hyperlink (LearnMoreHyperlink) | 外部ドキュメントへのリンク |

### プレビューセクション（PreviewDockPanel）

| 項目名 | コントロール | 説明 |
|--------|-------------|------|
| プレビュースクロールビューア | ScrollViewer (PreviewScrollViewer) | プレビューコンテンツ表示領域 |
| オプションボタン | Button (OptionsButton) | "Change Style Options"ボタン |

## イベント仕様

### 1-コンストラクタ初期化

1. `InitializeComponent()`でXAML初期化
2. `Loaded += PreviewPane_Loaded`でロードハンドラ登録
3. ヘッダー部分の初期化：
   - severityIcon、id、titleが有効な場合のみHeaderStackPanelを表示
   - SeverityIconBorder.Childにアイコンを設定
   - 3行分のテキスト高さを計測（_heightForThreeLineTitle）
   - TitleRun.Textにタイトルを設定
4. ヘルプリンクの初期化（helpLinkが有効な場合）
5. 説明の初期化（descriptionが有効な場合）
6. プレビュー要素の初期化：`InitializePreviewElement(previewContent)`
7. コマンドターゲットの登録：`RegisterPriorityCommandTarget`

### 2-展開ボタンチェック変更（ExpanderToggleButton_CheckedChanged）

1. `ExpanderToggleButton`のChecked/Uncheckedイベント発生
2. IsChecked == trueの場合（展開）：
   - TitleTextBlock.MaxHeightを無限大に設定
   - TextTrimmingをNoneに設定
   - HasDescriptionがtrueならDescriptionDockPanelを表示
   - LearnMoreHyperlinkを有効化
   - _isExpanded = true
3. IsChecked == falseの場合（折りたたみ）：
   - TitleTextBlock.MaxHeightを3行分に制限
   - TextTrimmingをCharacterEllipsisに設定
   - DescriptionDockPanelを非表示
   - _isExpanded = false

### 3-ハイパーリンクナビゲート（LearnMoreHyperlink_RequestNavigate）

1. Hyperlinkクリック（IdHyperlinkまたはLearnMoreHyperlink）
2. `LearnMoreHyperlink_RequestNavigate`ハンドラが発生
3. e.Uriが有効な場合、`VisualStudioNavigateToLinkService.StartBrowser`でブラウザ起動
4. e.Handled = trueでイベント処理済みに設定
5. `DiagnosticLogger.LogHyperlink`でテレメトリ記録

### 4-オプションボタンクリック（OptionsButton_Click）

1. `OptionsButton`押下
2. `OptionsButton_Click`ハンドラが発生
3. _optionPageGuidが有効な場合、`_uiShell.PostExecCommand`でオプションページを開く
4. VSConstants.VSStd97CmdID.ToolsOptionsコマンドを実行

### 5-プレビュー要素作成（InitializePreviewElement）

1. `CreatePreviewElement(previewItems)`でプレビュー要素を作成
2. プレビュー要素が存在する場合：
   - HeaderSeparatorを表示
   - PreviewDockPanelを表示
   - PreviewScrollViewer.Contentにプレビュー要素を設定
3. `AdjustWidthAndHeight`で幅と高さを調整

### 6-破棄処理（IDisposable.Dispose）

1. _differenceViewerPreviewがあれば破棄
2. _registerPriorityCommandTargetからコマンドターゲット登録解除

## データベース更新仕様

N/A（本コントロールはデータベースとの直接的なやり取りは行わない）

## メッセージ仕様

| メッセージID | 種別 | メッセージ内容 | 表示タイミング |
|-------------|------|---------------|----------------|
| AutomationName | アクセシビリティ | "Preview pane" | スクリーンリーダー用 |
| LearnMore | リンク | "More about {id}" | Learn Moreリンクテキスト |
| OptionsButton | ボタン | "Change Style Options" | オプションボタンラベル |

## 例外処理

| 例外ケース | 対処 |
|-----------|------|
| previewItemsがnullまたは空 | null返却（プレビュー表示なし） |
| previewItemsが3個超過 | 最初の3個+「...」に制限 |
| 差分ビューアが複数 | Contract.ThrowIfFalseでエラー |

## 備考

- IOleCommandTargetを実装してVSコマンドルーティングに対応
- IDisposableで差分ビューアのリソース管理
- カスタムExpanderToggleButtonStyleでVSテーマに対応した展開ボタン
- PreviewPaneRunStyleとPreviewPaneHyperlinkStyleでテーマ対応テキスト表示
- ParentElementプロパティで親要素へのバインディングをサポート
- DefaultWidth（400px）を基準とした幅調整
- タイトルが3行を超える場合は省略表示（CharacterEllipsis）と展開ボタン表示

---

## コードリーディングガイド

本画面を理解するために参照すべきファイルと、推奨する読み解き順序を以下に示す。

### 推奨読解順序

#### Step 1: データ構造を理解する

プレビューコンテンツのデータモデルを理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | DifferenceViewerPreview.cs | `src/EditorFeatures/Core/Implementation/Preview/DifferenceViewerPreview.cs` | IWpfDifferenceViewer、Dispose、QueryStatus/Exec |
| 1-2 | PreviewContentFactory.cs | `src/EditorFeatures/Core/Implementation/Preview/PreviewContentFactory.cs` | プレビューコンテンツの生成 |

**読解のコツ**:
- `DifferenceViewerPreview`はWPF差分ビューアをラップ
- `IOleCommandTarget`を実装してコマンドを差分ビューアに転送

#### Step 2: エントリーポイントを理解する

XAML定義とコードビハインドでUIの構造を把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | PreviewPane.xaml | `src/VisualStudio/Core/Def/PreviewPane/PreviewPane.xaml` | DockPanelレイアウト、HeaderStackPanel、PreviewDockPanel、カスタムスタイル |
| 2-2 | PreviewPane.xaml.cs | `src/VisualStudio/Core/Def/PreviewPane/PreviewPane.xaml.cs` | コンストラクタ、InitializePreviewElement、展開処理 |

**主要処理フロー**:
1. **行46-115**: コンストラクタでUI初期化とコマンドターゲット登録
2. **行66-92**: ヘッダー部分（アイコン、ID、タイトル、説明）の初期化
3. **行102**: InitializePreviewElementでプレビュー部分初期化
4. **行134-154**: InitializePreviewElementでプレビュー要素設定
5. **行156-222**: CreatePreviewElementでプレビュー要素作成（最大3個）
6. **行224-253**: GetPreviewElementでプレビューアイテム種別ごとの処理
7. **行394-423**: ExpanderToggleButton_CheckedChangedで展開/折りたたみ

### プログラム呼び出し階層図

```
PreviewPane (UserControl, IDisposable, IOleCommandTarget)
    │
    ├─ コンストラクタ
    │      ├─ InitializeComponent()
    │      ├─ Loaded += PreviewPane_Loaded
    │      │
    │      ├─ ヘッダー初期化（severityIcon, id, title, helpLink）
    │      │      ├─ SeverityIconBorder.Child = severityIcon
    │      │      ├─ _heightForThreeLineTitle を計測
    │      │      ├─ TitleRun.Text = title
    │      │      ├─ InitializeHyperlinks(helpLink, helpLinkToolTipText)
    │      │      └─ DescriptionParagraph.Inlines.Add(description)
    │      │
    │      ├─ InitializePreviewElement(previewContent)
    │      │      ├─ CreatePreviewElement(previewItems)
    │      │      │      └─ GetPreviewElement(previewItem)
    │      │      │             ├─ DifferenceViewerPreview → _differenceViewerPreview保持
    │      │      │             ├─ string → GetPreviewForString()
    │      │      │             └─ FrameworkElement → そのまま返却
    │      │      │
    │      │      └─ AdjustWidthAndHeight(previewElement)
    │      │
    │      └─ RegisterPriorityCommandTarget(this)
    │
    ├─ ExpanderToggleButton_CheckedChanged
    │      ├─ IsChecked == true → 展開
    │      │      ├─ TitleTextBlock.MaxHeight = Infinity
    │      │      ├─ DescriptionDockPanel.Visibility = Visible
    │      │      └─ LearnMoreHyperlink.IsEnabled = true
    │      │
    │      └─ IsChecked == false → 折りたたみ
    │             ├─ TitleTextBlock.MaxHeight = _heightForThreeLineTitle
    │             └─ DescriptionDockPanel.Visibility = Collapsed
    │
    ├─ LearnMoreHyperlink_RequestNavigate
    │      ├─ VisualStudioNavigateToLinkService.StartBrowser(e.Uri)
    │      └─ DiagnosticLogger.LogHyperlink()
    │
    ├─ OptionsButton_Click
    │      └─ _uiShell.PostExecCommand(ToolsOptions, _optionPageGuid)
    │
    ├─ IOleCommandTarget.QueryStatus / Exec
    │      └─ _differenceViewerPreview?.QueryStatus / Exec
    │
    └─ IDisposable.Dispose
           ├─ _differenceViewerPreview?.Dispose()
           └─ UnregisterPriorityCommandTarget()
```

### データフロー図

```
[入力]                      [処理]                            [出力]

previewContent ───────────▶ CreatePreviewElement() ────────▶ Grid/FrameworkElement
      │
      ├─ DifferenceViewerPreview
      │         ▼
      │   _differenceViewerPreview保持 ──────────▶ 差分表示
      │         │
      │         └─ Viewer.VisualElement ─────────▶ PreviewScrollViewer
      │
      ├─ string
      │         ▼
      │   GetPreviewForString() ─────────────────▶ TextBlock
      │
      └─ FrameworkElement
                ▼
          そのまま返却 ───────────────────────────▶ 表示

ExpanderToggleButton ─────▶ CheckedChanged() ──────────────▶ 説明展開/折りたたみ

IdHyperlink/LearnMore ────▶ RequestNavigate() ─────────────▶ ブラウザ起動
                                    │
                                    └─ DiagnosticLogger.LogHyperlink()

OptionsButton ────────────▶ Click() ───────────────────────▶ オプションページ表示
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| PreviewPane.xaml | `src/VisualStudio/Core/Def/PreviewPane/PreviewPane.xaml` | XAML | UI定義 |
| PreviewPane.xaml.cs | `src/VisualStudio/Core/Def/PreviewPane/PreviewPane.xaml.cs` | ソース | コードビハインド |
| DifferenceViewerPreview.cs | `src/EditorFeatures/Core/Implementation/Preview/DifferenceViewerPreview.cs` | ソース | 差分ビューアプレビュー |
| PreviewContentFactory.cs | `src/EditorFeatures/Core/Implementation/Preview/PreviewContentFactory.cs` | ソース | プレビューコンテンツ生成 |
| VisualStudioNavigateToLinkService.cs | `src/VisualStudio/Core/Def/Implementation/Utilities/VisualStudioNavigateToLinkService.cs` | ソース | ブラウザ起動サービス |
| DiagnosticLogger.cs | `src/CodeAnalysis/Diagnostics/Log/DiagnosticLogger.cs` | ソース | 診断テレメトリログ |
| ServicesVSResources.resx | `src/VisualStudio/Core/Def/ServicesVSResources.resx` | リソース | ローカライズ文字列 |
