WPF (Windows Presentation Foundation)

Microsoft純正のWindows用デスクトップアプリケーションフレームワーク。XAML、データバインディング、豊富なコントロール、アニメーション機能を提供。MVVMパターンによる保守性の高いアプリケーション設計が可能。

デスクトップC#XAMLWindows.NETMicrosoft

GitHub概要

dotnet/wpf

WPF is a .NET Core UI framework for building Windows desktop applications.

スター7,392
ウォッチ291
フォーク1,216
作成日:2018年10月19日
言語:C#
ライセンス:MIT License

トピックス

dotnethelp-wantedwpf

スター履歴

dotnet/wpf Star History
データ取得日時: 2025/7/15 23:10

デスクトップフレームワーク

WPF (Windows Presentation Foundation)

概要

WPF(Windows Presentation Foundation)は、Microsoftが開発したWindows向けのデスクトップアプリケーション開発フレームワークです。.NET Framework/.NET(旧.NET Core)の一部として提供され、XAMLによる宣言的UI定義、豊富なコントロール、データバインディング、MVVMパターンなどの機能を提供します。2006年の登場以来、Windowsエンタープライズアプリケーション開発の標準的な選択肢となっています。

詳細

WPFは、従来のWin32 APIによる開発と比較して、宣言的なUI定義、強力なデータバインディング、豊富なレイアウトシステム、スタイリングとテンプレート機能など、現代的なアプリケーション開発に必要な機能を包括的に提供します。

主な特徴として、XAMLによる宣言的UI定義、MVVMアーキテクチャパターンのサポート、強力なデータバインディング、豊富な標準コントロール、カスタムコントロール作成機能、ベクターベースのグラフィックス、アニメーションとトランジション、多彩なレイアウトシステムなどがあります。

特徴

  • XAML: 宣言的なUI定義によるデザインとロジックの分離
  • データバインディング: 双方向データバインディングとプロパティ通知
  • MVVMサポート: Model-View-ViewModel パターンの標準サポート
  • 豊富なコントロール: 100以上の標準UIコントロール
  • カスタマイズ性: スタイル、テンプレート、カスタムコントロール
  • グラフィックス: ベクターベースの高品質レンダリング

アーキテクチャ

  • MVVMパターン: Model-View-ViewModel設計パターン
  • コマンドパターン: ICommandインターフェースによるアクション処理
  • 依存関係プロパティ: 高度なプロパティシステム
  • リソースシステム: 効率的なリソース管理と共有
  • レイアウトシステム: 柔軟で強力なレイアウト管理
  • ルーティングイベント: 高度なイベント処理システム

最新状況

2025年現在も、.NET 8/.NET 9を含む最新の.NETでサポートされており、Windows 11環境での動作も完全にサポートされています。Visual Studio 2022での開発サポートも継続されており、企業のレガシーシステム維持や新規Windowsアプリケーション開発で重要な位置を占めています。

メリット・デメリット

メリット

  • 成熟したエコシステム: 15年以上の開発・運用実績
  • 豊富なリソース: 大量のドキュメント、書籍、チュートリアル
  • 強力な開発ツール: Visual Studio, Expression Blendの充実したサポート
  • エンタープライズ対応: 大規模アプリケーション開発に適した機能
  • .NET統合: .NETエコシステムとの完全な統合
  • パフォーマンス: Windowsネイティブの高いパフォーマンス
  • 安定性: 長期間の実績による高い安定性
  • カスタマイズ性: 高度なUI カスタマイズとテーマ対応

デメリット

  • Windows専用: Windows以外のプラットフォーム未対応
  • 学習コスト: XAML、MVVM、データバインディングなど習得項目が多い
  • パッケージサイズ: .NET Frameworkのサイズが大きい
  • モダンデザイン: フラットデザインやマテリアルデザインの実現が困難
  • モバイル非対応: スマートフォン、タブレットには対応しない
  • 新機能開発の停滞: 新機能追加よりも維持に重点
  • 高DPI対応: 4K、高解像度ディスプレイでの表示問題
  • Webトレンド: Web技術の普及により相対的な重要度低下

参考ページ

書き方の例

Hello World

<Window x:Class="WpfApp.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="Hello WPF" Height="300" Width="400">
    <Grid Margin="20">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>
        
        <TextBlock Grid.Row="0" Text="Hello, WPF World!" 
                   FontSize="24" FontWeight="Bold" 
                   HorizontalAlignment="Center" Margin="0,20"/>
        
        <Button Grid.Row="1" Content="クリックしてください" 
                Name="HelloButton" HorizontalAlignment="Center" 
                Padding="20,10" Margin="0,20" Click="HelloButton_Click"/>
        
        <TextBlock Grid.Row="2" Name="MessageTextBlock" 
                   Text="ボタンをクリックしてメッセージを表示" 
                   HorizontalAlignment="Center" VerticalAlignment="Center"
                   FontSize="14" Foreground="Gray"/>
    </Grid>
</Window>
using System.Windows;

namespace WpfApp
{
    public partial class MainWindow : Window
    {
        private int clickCount = 0;

        public MainWindow()
        {
            InitializeComponent();
        }

        private void HelloButton_Click(object sender, RoutedEventArgs e)
        {
            clickCount++;
            MessageTextBlock.Text = $"こんにちは! {clickCount} 回クリックされました。";
            
            if (clickCount >= 5)
            {
                MessageBox.Show("5回以上クリックされました!", "メッセージ", 
                              MessageBoxButton.OK, MessageBoxImage.Information);
            }
        }
    }
}

データバインディングとMVVM

<Window x:Class="WpfApp.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MVVMデータバインディング例" Height="400" Width="500">
    <Grid Margin="20">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>
        
        <TextBlock Grid.Row="0" Text="顧客情報管理" FontSize="20" FontWeight="Bold" 
                   HorizontalAlignment="Center" Margin="0,0,0,20"/>
        
        <!-- 入力フォーム -->
        <StackPanel Grid.Row="1" Orientation="Horizontal" Margin="0,5">
            <Label Content="名前:" Width="60" VerticalAlignment="Center"/>
            <TextBox Text="{Binding CustomerName, UpdateSourceTrigger=PropertyChanged}" 
                     Width="150" Margin="5,0"/>
            <Label Content="年齢:" Width="40" VerticalAlignment="Center" Margin="10,0,0,0"/>
            <TextBox Text="{Binding Age, UpdateSourceTrigger=PropertyChanged}" 
                     Width="50" Margin="5,0"/>
        </StackPanel>
        
        <StackPanel Grid.Row="2" Orientation="Horizontal" Margin="0,5">
            <Label Content="メール:" Width="60" VerticalAlignment="Center"/>
            <TextBox Text="{Binding Email, UpdateSourceTrigger=PropertyChanged}" 
                     Width="200" Margin="5,0"/>
            <CheckBox Content="VIP顧客" IsChecked="{Binding IsVip}" 
                      VerticalAlignment="Center" Margin="10,0"/>
        </StackPanel>
        
        <!-- ボタン -->
        <StackPanel Grid.Row="3" Orientation="Horizontal" HorizontalAlignment="Center" Margin="0,20">
            <Button Content="追加" Command="{Binding AddCustomerCommand}" 
                    Padding="15,5" Margin="5"/>
            <Button Content="削除" Command="{Binding DeleteCustomerCommand}" 
                    Padding="15,5" Margin="5"/>
            <Button Content="クリア" Command="{Binding ClearCommand}" 
                    Padding="15,5" Margin="5"/>
        </StackPanel>
        
        <!-- 顧客リスト -->
        <GroupBox Grid.Row="4" Header="顧客一覧" Margin="0,10">
            <DataGrid ItemsSource="{Binding Customers}" 
                      SelectedItem="{Binding SelectedCustomer}"
                      AutoGenerateColumns="False" CanUserAddRows="False">
                <DataGrid.Columns>
                    <DataGridTextColumn Header="名前" Binding="{Binding Name}" Width="120"/>
                    <DataGridTextColumn Header="年齢" Binding="{Binding Age}" Width="60"/>
                    <DataGridTextColumn Header="メール" Binding="{Binding Email}" Width="180"/>
                    <DataGridCheckBoxColumn Header="VIP" Binding="{Binding IsVip}" Width="50"/>
                </DataGrid.Columns>
            </DataGrid>
        </GroupBox>
    </Grid>
</Window>
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Windows;
using System.Windows.Input;

namespace WpfApp
{
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            DataContext = new MainWindowViewModel();
        }
    }

    public class MainWindowViewModel : INotifyPropertyChanged
    {
        private string _customerName = "";
        private int _age;
        private string _email = "";
        private bool _isVip;
        private Customer? _selectedCustomer;

        public string CustomerName
        {
            get => _customerName;
            set { _customerName = value; OnPropertyChanged(); }
        }

        public int Age
        {
            get => _age;
            set { _age = value; OnPropertyChanged(); }
        }

        public string Email
        {
            get => _email;
            set { _email = value; OnPropertyChanged(); }
        }

        public bool IsVip
        {
            get => _isVip;
            set { _isVip = value; OnPropertyChanged(); }
        }

        public Customer? SelectedCustomer
        {
            get => _selectedCustomer;
            set
            {
                _selectedCustomer = value;
                OnPropertyChanged();
                if (value != null)
                {
                    CustomerName = value.Name;
                    Age = value.Age;
                    Email = value.Email;
                    IsVip = value.IsVip;
                }
            }
        }

        public ObservableCollection<Customer> Customers { get; } = new();

        public ICommand AddCustomerCommand => new RelayCommand(AddCustomer, CanAddCustomer);
        public ICommand DeleteCustomerCommand => new RelayCommand(DeleteCustomer, CanDeleteCustomer);
        public ICommand ClearCommand => new RelayCommand(Clear);

        private void AddCustomer()
        {
            var customer = new Customer
            {
                Name = CustomerName,
                Age = Age,
                Email = Email,
                IsVip = IsVip
            };
            Customers.Add(customer);
            Clear();
        }

        private bool CanAddCustomer()
        {
            return !string.IsNullOrWhiteSpace(CustomerName) && 
                   Age > 0 && 
                   !string.IsNullOrWhiteSpace(Email);
        }

        private void DeleteCustomer()
        {
            if (SelectedCustomer != null)
            {
                Customers.Remove(SelectedCustomer);
                Clear();
            }
        }

        private bool CanDeleteCustomer()
        {
            return SelectedCustomer != null;
        }

        private void Clear()
        {
            CustomerName = "";
            Age = 0;
            Email = "";
            IsVip = false;
            SelectedCustomer = null;
        }

        public event PropertyChangedEventHandler? PropertyChanged;
        protected void OnPropertyChanged([CallerMemberName] string? propertyName = null)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    }

    public class Customer
    {
        public string Name { get; set; } = "";
        public int Age { get; set; }
        public string Email { get; set; } = "";
        public bool IsVip { get; set; }
    }

    public class RelayCommand : ICommand
    {
        private readonly Action _execute;
        private readonly Func<bool>? _canExecute;

        public RelayCommand(Action execute, Func<bool>? canExecute = null)
        {
            _execute = execute;
            _canExecute = canExecute;
        }

        public bool CanExecute(object? parameter) => _canExecute?.Invoke() ?? true;
        public void Execute(object? parameter) => _execute();

        public event EventHandler? CanExecuteChanged
        {
            add { CommandManager.RequerySuggested += value; }
            remove { CommandManager.RequerySuggested -= value; }
        }
    }
}

スタイルとテンプレート

<Window x:Class="WpfApp.StyledWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="スタイルとテンプレート例" Height="450" Width="600">
    
    <Window.Resources>
        <!-- カスタムボタンスタイル -->
        <Style x:Key="PrimaryButtonStyle" TargetType="Button">
            <Setter Property="Background" Value="#007ACC"/>
            <Setter Property="Foreground" Value="White"/>
            <Setter Property="FontSize" Value="14"/>
            <Setter Property="FontWeight" Value="Bold"/>
            <Setter Property="Padding" Value="15,8"/>
            <Setter Property="Margin" Value="5"/>
            <Setter Property="BorderThickness" Value="0"/>
            <Setter Property="Cursor" Value="Hand"/>
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="Button">
                        <Border Background="{TemplateBinding Background}"
                                CornerRadius="5" Padding="{TemplateBinding Padding}">
                            <ContentPresenter HorizontalAlignment="Center" 
                                            VerticalAlignment="Center"/>
                            <Border.Effect>
                                <DropShadowEffect ShadowDepth="2" BlurRadius="5" 
                                                Opacity="0.3" Color="Gray"/>
                            </Border.Effect>
                        </Border>
                        <ControlTemplate.Triggers>
                            <Trigger Property="IsMouseOver" Value="True">
                                <Setter Property="Background" Value="#005A9E"/>
                            </Trigger>
                            <Trigger Property="IsPressed" Value="True">
                                <Setter Property="Background" Value="#004A85"/>
                            </Trigger>
                        </ControlTemplate.Triggers>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>

        <!-- カードスタイル -->
        <Style x:Key="CardStyle" TargetType="Border">
            <Setter Property="Background" Value="White"/>
            <Setter Property="BorderBrush" Value="#E0E0E0"/>
            <Setter Property="BorderThickness" Value="1"/>
            <Setter Property="CornerRadius" Value="8"/>
            <Setter Property="Padding" Value="20"/>
            <Setter Property="Margin" Value="10"/>
            <Setter Property="Effect">
                <Setter.Value>
                    <DropShadowEffect ShadowDepth="3" BlurRadius="10" 
                                    Opacity="0.15" Color="Black"/>
                </Setter.Value>
            </Setter>
        </Style>

        <!-- ヘッダースタイル -->
        <Style x:Key="HeaderStyle" TargetType="TextBlock">
            <Setter Property="FontSize" Value="22"/>
            <Setter Property="FontWeight" Value="Bold"/>
            <Setter Property="Foreground" Value="#333333"/>
            <Setter Property="Margin" Value="0,0,0,15"/>
        </Style>
        
        <!-- アニメーション -->
        <Storyboard x:Key="FadeInAnimation">
            <DoubleAnimation Storyboard.TargetProperty="Opacity" 
                           From="0" To="1" Duration="0:0:0.5"/>
        </Storyboard>
    </Window.Resources>

    <Grid Background="#F5F5F5">
        <ScrollViewer>
            <StackPanel Margin="20">
                <TextBlock Text="WPF スタイルとテンプレート" Style="{StaticResource HeaderStyle}"
                           HorizontalAlignment="Center"/>
                
                <!-- プロフィールカード -->
                <Border Style="{StaticResource CardStyle}">
                    <Border.Triggers>
                        <EventTrigger RoutedEvent="Loaded">
                            <BeginStoryboard Storyboard="{StaticResource FadeInAnimation}"/>
                        </EventTrigger>
                    </Border.Triggers>
                    <Grid>
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition Width="Auto"/>
                            <ColumnDefinition Width="*"/>
                        </Grid.ColumnDefinitions>
                        
                        <Ellipse Grid.Column="0" Width="80" Height="80" 
                                Fill="#007ACC" Margin="0,0,20,0"/>
                        
                        <StackPanel Grid.Column="1">
                            <TextBlock Text="田中 太郎" FontSize="18" FontWeight="Bold"/>
                            <TextBlock Text="シニアソフトウェアエンジニア" 
                                     FontSize="14" Foreground="Gray" Margin="0,5"/>
                            <TextBlock Text="WPFとC#を専門とする開発者。10年以上の経験を持ち、エンタープライズアプリケーションの設計・開発に従事。"
                                     TextWrapping="Wrap" Margin="0,10,0,15"/>
                            
                            <StackPanel Orientation="Horizontal">
                                <Button Content="プロフィール" Style="{StaticResource PrimaryButtonStyle}"/>
                                <Button Content="連絡先" Style="{StaticResource PrimaryButtonStyle}"/>
                            </StackPanel>
                        </StackPanel>
                    </Grid>
                </Border>
                
                <!-- 設定カード -->
                <Border Style="{StaticResource CardStyle}">
                    <StackPanel>
                        <TextBlock Text="アプリケーション設定" FontSize="16" FontWeight="Bold" Margin="0,0,0,15"/>
                        
                        <Grid>
                            <Grid.RowDefinitions>
                                <RowDefinition/>
                                <RowDefinition/>
                                <RowDefinition/>
                                <RowDefinition/>
                            </Grid.RowDefinitions>
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition Width="200"/>
                                <ColumnDefinition Width="*"/>
                            </Grid.ColumnDefinitions>
                            
                            <Label Grid.Row="0" Grid.Column="0" Content="テーマ:"/>
                            <ComboBox Grid.Row="0" Grid.Column="1" Margin="5">
                                <ComboBoxItem Content="ライト"/>
                                <ComboBoxItem Content="ダーク" IsSelected="True"/>
                                <ComboBoxItem Content="自動"/>
                            </ComboBox>
                            
                            <Label Grid.Row="1" Grid.Column="0" Content="言語:"/>
                            <ComboBox Grid.Row="1" Grid.Column="1" Margin="5">
                                <ComboBoxItem Content="日本語" IsSelected="True"/>
                                <ComboBoxItem Content="English"/>
                            </ComboBox>
                            
                            <Label Grid.Row="2" Grid.Column="0" Content="自動保存:"/>
                            <CheckBox Grid.Row="2" Grid.Column="1" Content="有効" IsChecked="True" 
                                    VerticalAlignment="Center" Margin="5"/>
                            
                            <StackPanel Grid.Row="3" Grid.Column="1" Orientation="Horizontal" 
                                      HorizontalAlignment="Right" Margin="5,15,5,5">
                                <Button Content="保存" Style="{StaticResource PrimaryButtonStyle}"/>
                                <Button Content="リセット" Style="{StaticResource PrimaryButtonStyle}"/>
                            </StackPanel>
                        </Grid>
                    </StackPanel>
                </Border>
            </StackPanel>
        </ScrollViewer>
    </Grid>
</Window>

カスタムコントロール

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

namespace WpfApp.Controls
{
    public class ProgressCard : Control
    {
        public static readonly DependencyProperty TitleProperty =
            DependencyProperty.Register("Title", typeof(string), typeof(ProgressCard));

        public static readonly DependencyProperty DescriptionProperty =
            DependencyProperty.Register("Description", typeof(string), typeof(ProgressCard));

        public static readonly DependencyProperty ProgressProperty =
            DependencyProperty.Register("Progress", typeof(double), typeof(ProgressCard));

        public static readonly DependencyProperty IconProperty =
            DependencyProperty.Register("Icon", typeof(ImageSource), typeof(ProgressCard));

        static ProgressCard()
        {
            DefaultStyleKeyProperty.OverrideMetadata(typeof(ProgressCard), 
                new FrameworkPropertyMetadata(typeof(ProgressCard)));
        }

        public string Title
        {
            get { return (string)GetValue(TitleProperty); }
            set { SetValue(TitleProperty, value); }
        }

        public string Description
        {
            get { return (string)GetValue(DescriptionProperty); }
            set { SetValue(DescriptionProperty, value); }
        }

        public double Progress
        {
            get { return (double)GetValue(ProgressProperty); }
            set { SetValue(ProgressProperty, value); }
        }

        public ImageSource Icon
        {
            get { return (ImageSource)GetValue(IconProperty); }
            set { SetValue(IconProperty, value); }
        }
    }
}
<!-- Themes/Generic.xaml -->
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                    xmlns:local="clr-namespace:WpfApp.Controls">

    <Style TargetType="{x:Type local:ProgressCard}">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type local:ProgressCard}">
                    <Border Background="White" BorderBrush="#E0E0E0" BorderThickness="1" 
                            CornerRadius="8" Padding="20" Margin="10">
                        <Border.Effect>
                            <DropShadowEffect ShadowDepth="3" BlurRadius="10" 
                                            Opacity="0.15" Color="Black"/>
                        </Border.Effect>
                        
                        <Grid>
                            <Grid.RowDefinitions>
                                <RowDefinition Height="Auto"/>
                                <RowDefinition Height="Auto"/>
                                <RowDefinition Height="Auto"/>
                            </Grid.RowDefinitions>
                            
                            <StackPanel Grid.Row="0" Orientation="Horizontal" Margin="0,0,0,10">
                                <Image Source="{TemplateBinding Icon}" Width="24" Height="24" 
                                       Margin="0,0,10,0" VerticalAlignment="Center"/>
                                <TextBlock Text="{TemplateBinding Title}" FontSize="16" 
                                         FontWeight="Bold" VerticalAlignment="Center"/>
                            </StackPanel>
                            
                            <TextBlock Grid.Row="1" Text="{TemplateBinding Description}" 
                                     TextWrapping="Wrap" Margin="0,0,0,15" Foreground="Gray"/>
                            
                            <Grid Grid.Row="2">
                                <Grid.ColumnDefinitions>
                                    <ColumnDefinition Width="*"/>
                                    <ColumnDefinition Width="Auto"/>
                                </Grid.ColumnDefinitions>
                                
                                <ProgressBar Grid.Column="0" Value="{TemplateBinding Progress}" 
                                           Height="8" Maximum="100" Background="#F0F0F0"/>
                                <TextBlock Grid.Column="1" Text="{TemplateBinding Progress, StringFormat={}{0:F0}%}" 
                                         FontSize="12" Margin="10,0,0,0" VerticalAlignment="Center"/>
                            </Grid>
                        </Grid>
                    </Border>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</ResourceDictionary>

ファイル操作とダイアログ

using Microsoft.Win32;
using System.IO;
using System.Windows;

namespace WpfApp
{
    public partial class FileOperationWindow : Window
    {
        public FileOperationWindow()
        {
            InitializeComponent();
        }

        private void OpenFileButton_Click(object sender, RoutedEventArgs e)
        {
            var openFileDialog = new OpenFileDialog
            {
                Title = "ファイルを開く",
                Filter = "テキストファイル (*.txt)|*.txt|すべてのファイル (*.*)|*.*",
                InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments)
            };

            if (openFileDialog.ShowDialog() == true)
            {
                try
                {
                    string content = File.ReadAllText(openFileDialog.FileName);
                    ContentTextBox.Text = content;
                    StatusTextBlock.Text = $"ファイルを読み込みました: {Path.GetFileName(openFileDialog.FileName)}";
                }
                catch (Exception ex)
                {
                    MessageBox.Show($"ファイルの読み込みに失敗しました:\n{ex.Message}", "エラー", 
                                  MessageBoxButton.OK, MessageBoxImage.Error);
                }
            }
        }

        private void SaveFileButton_Click(object sender, RoutedEventArgs e)
        {
            var saveFileDialog = new SaveFileDialog
            {
                Title = "ファイルを保存",
                Filter = "テキストファイル (*.txt)|*.txt",
                DefaultExt = "txt",
                InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments)
            };

            if (saveFileDialog.ShowDialog() == true)
            {
                try
                {
                    File.WriteAllText(saveFileDialog.FileName, ContentTextBox.Text);
                    StatusTextBlock.Text = $"ファイルを保存しました: {Path.GetFileName(saveFileDialog.FileName)}";
                    MessageBox.Show("ファイルが正常に保存されました。", "保存完了", 
                                  MessageBoxButton.OK, MessageBoxImage.Information);
                }
                catch (Exception ex)
                {
                    MessageBox.Show($"ファイルの保存に失敗しました:\n{ex.Message}", "エラー", 
                                  MessageBoxButton.OK, MessageBoxImage.Error);
                }
            }
        }

        private void ShowCustomDialogButton_Click(object sender, RoutedEventArgs e)
        {
            var dialog = new CustomInputDialog
            {
                Owner = this,
                WindowStartupLocation = WindowStartupLocation.CenterOwner
            };

            if (dialog.ShowDialog() == true)
            {
                MessageBox.Show($"入力された情報:\n名前: {dialog.UserName}\n年齢: {dialog.Age}\nメモ: {dialog.Notes}", 
                              "入力結果", MessageBoxButton.OK, MessageBoxImage.Information);
            }
        }
    }

    public partial class CustomInputDialog : Window
    {
        public string UserName { get; private set; } = "";
        public int Age { get; private set; }
        public string Notes { get; private set; } = "";

        public CustomInputDialog()
        {
            InitializeComponent();
        }

        private void OkButton_Click(object sender, RoutedEventArgs e)
        {
            if (string.IsNullOrWhiteSpace(NameTextBox.Text))
            {
                MessageBox.Show("名前を入力してください。", "入力エラー", 
                              MessageBoxButton.OK, MessageBoxImage.Warning);
                return;
            }

            if (!int.TryParse(AgeTextBox.Text, out int age) || age < 0 || age > 150)
            {
                MessageBox.Show("有効な年齢を入力してください(0-150)。", "入力エラー", 
                              MessageBoxButton.OK, MessageBoxImage.Warning);
                return;
            }

            UserName = NameTextBox.Text;
            Age = age;
            Notes = NotesTextBox.Text;
            DialogResult = true;
        }

        private void CancelButton_Click(object sender, RoutedEventArgs e)
        {
            DialogResult = false;
        }
    }
}

アニメーションと視覚効果

<Window x:Class="WpfApp.AnimationWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="アニメーションと視覚効果" Height="500" Width="600">
    
    <Window.Resources>
        <Storyboard x:Key="ButtonHoverAnimation">
            <DoubleAnimation Storyboard.TargetProperty="(UIElement.RenderTransform).(ScaleTransform.ScaleX)"
                           To="1.1" Duration="0:0:0.2"/>
            <DoubleAnimation Storyboard.TargetProperty="(UIElement.RenderTransform).(ScaleTransform.ScaleY)"
                           To="1.1" Duration="0:0:0.2"/>
        </Storyboard>
        
        <Storyboard x:Key="ButtonLeaveAnimation">
            <DoubleAnimation Storyboard.TargetProperty="(UIElement.RenderTransform).(ScaleTransform.ScaleX)"
                           To="1.0" Duration="0:0:0.2"/>
            <DoubleAnimation Storyboard.TargetProperty="(UIElement.RenderTransform).(ScaleTransform.ScaleY)"
                           To="1.0" Duration="0:0:0.2"/>
        </Storyboard>
        
        <Storyboard x:Key="FadeInAnimation">
            <DoubleAnimation Storyboard.TargetProperty="Opacity" 
                           From="0" To="1" Duration="0:0:1"/>
            <DoubleAnimation Storyboard.TargetProperty="(UIElement.RenderTransform).(TranslateTransform.Y)"
                           From="50" To="0" Duration="0:0:1">
                <DoubleAnimation.EasingFunction>
                    <QuadraticEase EasingMode="EaseOut"/>
                </DoubleAnimation.EasingFunction>
            </DoubleAnimation>
        </Storyboard>
        
        <Storyboard x:Key="RotateAnimation">
            <DoubleAnimation Storyboard.TargetProperty="(UIElement.RenderTransform).(RotateTransform.Angle)"
                           From="0" To="360" Duration="0:0:2" RepeatBehavior="Forever"/>
        </Storyboard>
    </Window.Resources>

    <Grid Background="LinearGradientBrush">
        <Grid.Background>
            <LinearGradientBrush StartPoint="0,0" EndPoint="1,1">
                <GradientStop Color="#4A90E2" Offset="0"/>
                <GradientStop Color="#7B68EE" Offset="1"/>
            </LinearGradientBrush>
        </Grid.Background>
        
        <ScrollViewer>
            <StackPanel Margin="40">
                <TextBlock Text="WPF アニメーション例" FontSize="28" FontWeight="Bold" 
                          Foreground="White" HorizontalAlignment="Center" Margin="0,0,0,30">
                    <TextBlock.RenderTransform>
                        <TranslateTransform/>
                    </TextBlock.RenderTransform>
                    <TextBlock.Triggers>
                        <EventTrigger RoutedEvent="Loaded">
                            <BeginStoryboard Storyboard="{StaticResource FadeInAnimation}"/>
                        </EventTrigger>
                    </TextBlock.Triggers>
                </TextBlock>
                
                <!-- ホバーアニメーションボタン -->
                <Button Content="ホバーでスケールアニメーション" 
                        Background="White" Foreground="#4A90E2" 
                        FontSize="16" FontWeight="Bold"
                        Padding="20,10" Margin="0,10" 
                        HorizontalAlignment="Center"
                        BorderThickness="0" Cursor="Hand">
                    <Button.RenderTransform>
                        <ScaleTransform/>
                    </Button.RenderTransform>
                    <Button.Triggers>
                        <EventTrigger RoutedEvent="MouseEnter">
                            <BeginStoryboard Storyboard="{StaticResource ButtonHoverAnimation}"/>
                        </EventTrigger>
                        <EventTrigger RoutedEvent="MouseLeave">
                            <BeginStoryboard Storyboard="{StaticResource ButtonLeaveAnimation}"/>
                        </EventTrigger>
                    </Button.Triggers>
                </Button>
                
                <!-- 回転アニメーション -->
                <Border Background="White" Width="100" Height="100" 
                        CornerRadius="50" Margin="0,30" 
                        HorizontalAlignment="Center">
                    <Border.RenderTransform>
                        <RotateTransform/>
                    </Border.RenderTransform>
                    <Border.Triggers>
                        <EventTrigger RoutedEvent="MouseLeftButtonDown">
                            <BeginStoryboard Storyboard="{StaticResource RotateAnimation}"/>
                        </EventTrigger>
                    </Border.Triggers>
                    
                    <TextBlock Text="🎯" FontSize="40" HorizontalAlignment="Center" 
                              VerticalAlignment="Center"/>
                </Border>
                
                <!-- プログレスバーアニメーション -->
                <Border Background="White" CornerRadius="10" Padding="20" Margin="0,20">
                    <StackPanel>
                        <TextBlock Text="プログレス表示" FontSize="16" FontWeight="Bold" 
                                  Margin="0,0,0,10"/>
                        <ProgressBar Name="AnimatedProgressBar" Height="20" 
                                   Background="#F0F0F0" Foreground="#4A90E2"/>
                        <Button Content="アニメーション開始" Click="StartProgressAnimation" 
                              Margin="0,10,0,0" HorizontalAlignment="Center"/>
                    </StackPanel>
                </Border>
            </StackPanel>
        </ScrollViewer>
    </Grid>
</Window>
using System.Windows;
using System.Windows.Media.Animation;

namespace WpfApp
{
    public partial class AnimationWindow : Window
    {
        public AnimationWindow()
        {
            InitializeComponent();
        }

        private void StartProgressAnimation(object sender, RoutedEventArgs e)
        {
            var animation = new DoubleAnimation
            {
                From = 0,
                To = 100,
                Duration = TimeSpan.FromSeconds(3),
                EasingFunction = new QuadraticEase { EasingMode = EasingMode.EaseInOut }
            };
            
            AnimatedProgressBar.BeginAnimation(ProgressBar.ValueProperty, animation);
        }
    }
}