WPF (Windows Presentation Foundation)
Microsoft純正のWindows用デスクトップアプリケーションフレームワーク。XAML、データバインディング、豊富なコントロール、アニメーション機能を提供。MVVMパターンによる保守性の高いアプリケーション設計が可能。
GitHub概要
dotnet/wpf
WPF is a .NET Core UI framework for building Windows desktop applications.
トピックス
スター履歴
デスクトップフレームワーク
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);
}
}
}