WPFにおけるカスタムコントロールの実装方法
WPF(Windows Presentation Foundation)では、カスタムコントロールをStyle要素を使用して外観と動作を定義します。StyleはWPFの強力なリソース機能であり、開発者は一連のプロパティ値を定義して、単一のコントロールまたは複数のコントロールに適用できます。
カスタムコントロールにおけるStyleの役割
1. 視覚的デザイン
Setter要素を使用して背景色(Background)、前景色(Foreground)、フォントサイズ(FontSize)、枠線スタイル(BorderBrush、BorderThickness)などの各種プロパティを設定できます。ControlTemplateを含めることで、コントロールのレイアウトと外観を完全に再定義できます。
2. インタラクティブ動作
Triggers要素により、マウスホバーやボタンクリックなどのプロパティ変化時にコントロールの外観や動作を変更できます。- 動的効果(マウスホバー時の色変更、クリック時のアニメーションなど)を追加できます。
3. リソースの再利用
- リソースディクショナリ(
ResourceDictionary)にStyleを定義することで、複数の場所で同じスタイル定義を再利用できます。 x:Key属性を使用してスタイルに一意の識別子を指定できます。
4. スタイルの継承
BasedOn属性を使用して他のStyleをベースにすることで、継承機能を活用できます。- 親スタイルのプロパティとトリガーを継承し、独自のプロパティを追加または上書きできます。
実装例
例1:Setterを使用した基本プロパティ設定
カスタムのEnhancedButtonコントロールにスタイルを適用する例:
<Window.Resources>
<Style x:Key="EnhancedButtonTheme" TargetType="local:EnhancedButton">
<Setter Property="Background" Value="#FFE6F3FF"/>
<Setter Property="Foreground" Value="White"/>
<Setter Property="FontSize" Value="18"/>
<Setter Property="Padding" Value="12,6"/>
</Style>
</Window.Resources>
<local:EnhancedButton Style="{StaticResource EnhancedButtonTheme}" Content="実行"/>
例2:Triggersを使用した動的効果
マウスホバー時に外観を変更するトリガーの実装:
<Style x:Key="InteractiveButtonTheme" TargetType="local:EnhancedButton">
<Setter Property="Background" Value="#FFE6F3FF"/>
<Setter Property="Foreground" Value="White"/>
<Setter Property="BorderThickness" Value="2"/>
<Setter Property="BorderBrush" Value="Transparent"/>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="#FF0066CC"/>
<Setter Property="BorderBrush" Value="#FF004499"/>
</Trigger>
<Trigger Property="IsPressed" Value="True">
<Setter Property="Background" Value="#FF0055AA"/>
<Trigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="RenderTransform.ScaleX"
To="0.95" Duration="0:0:0.1"/>
<DoubleAnimation Storyboard.TargetProperty="RenderTransform.ScaleY"
To="0.95" Duration="0:0:0.1"/>
</Storyboard>
</BeginStoryboard>
</Trigger.EnterActions>
</Trigger>
</Style.Triggers>
</Style>
例3:ControlTemplateを使用した完全なカスタマイズ
コントロールの構造を完全に再定義する例:
<Style x:Key="ModernButtonTemplate" TargetType="local:EnhancedButton">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local:EnhancedButton">
<Grid x:Name="MainGrid">
<Border x:Name="OuterBorder"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
CornerRadius="8"
Padding="{TemplateBinding Padding}">
<Border.Effect>
<DropShadowEffect x:Name="ShadowEffect"
BlurRadius="8"
ShadowDepth="2"
Opacity="0.3"/>
</Border.Effect>
<ContentPresenter x:Name="ContentPresenter"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Content="{TemplateBinding Content}"/>
</Border>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="OuterBorder" Property="Background" Value="#FF0078D7"/>
<Trigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetName="ShadowEffect"
Storyboard.TargetProperty="BlurRadius"
To="12" Duration="0:0:0.2"/>
</Storyboard>
</BeginStoryboard>
</Trigger.EnterActions>
</Trigger>
<Trigger Property="IsPressed" Value="True">
<Setter TargetName="OuterBorder" Property="Background" Value="#FF106EBE"/>
</Trigger>
<Trigger Property="IsEnabled" Value="False">
<Setter TargetName="OuterBorder" Property="Opacity" Value="0.5"/>
<Setter TargetName="ShadowEffect" Property="Opacity" Value="0"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
カスタムコントロールの作成手順
1. コントロールクラスの作成
using System.Windows;
using System.Windows.Controls;
namespace CustomControls
{
public class EnhancedButton : Button
{
public static readonly DependencyProperty HoverIntensityProperty =
DependencyProperty.Register(
"HoverIntensity",
typeof(double),
typeof(EnhancedButton),
new PropertyMetadata(1.0));
public double HoverIntensity
{
get { return (double)GetValue(HoverIntensityProperty); }
set { SetValue(HoverIntensityProperty, value); }
}
static EnhancedButton()
{
DefaultStyleKeyProperty.OverrideMetadata(
typeof(EnhancedButton),
new FrameworkPropertyMetadata(typeof(EnhancedButton)));
}
}
}
2. デフォルトスタイルの定義
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:CustomControls">
<Style TargetType="{x:Type local:EnhancedButton}">
<Setter Property="Background" Value="#FFE6F3FF"/>
<Setter Property="Foreground" Value="White"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="BorderBrush" Value="#FF0078D7"/>
<Setter Property="Padding" Value="16,8"/>
<Setter Property="FontWeight" Value="SemiBold"/>
<Setter Property="Cursor" Value="Hand"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:EnhancedButton}">
<Border x:Name="ButtonBorder"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
CornerRadius="4"
Padding="{TemplateBinding Padding}">
<ContentPresenter HorizontalAlignment="Center"
VerticalAlignment="Center"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="ButtonBorder" Property="Background" Value="#FF0078D7"/>
</Trigger>
<Trigger Property="IsPressed" Value="True">
<Setter TargetName="ButtonBorder" Property="Background" Value="#FF005A9E"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
3. コントロールの使用
<Window x:Class="WpfApp.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:custom="clr-namespace:CustomControls">
<Grid>
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
<custom:EnhancedButton Content="標準ボタン"
Width="200"
Height="40"
Margin="10"/>
<custom:EnhancedButton Content="カスタム強度ボタン"
Width="200"
Height="40"
Margin="10"
HoverIntensity="1.5"/>
<custom:EnhancedButton Style="{StaticResource ModernButtonTemplate}"
Content="テンプレート適用"
Width="200"
Height="40"
Margin="10"/>
</StackPanel>
</Grid>
</Window>
x:Keyの使用について
リソース定義時のx:Keyの使用は、リソースの再利用方法によって決まります:
- 明示的キーが必要な場合:
- 複数の場所で同じリソースを参照する場合
- スタイルを特定のコントロールにのみ適用する場合
- マージされたリソースディクショナリ内のリソース
- 暗黙的キーで十分な場合:
- 特定の型のコントロールすべてに自動適用するスタイル
- 一度だけ使用するリソース
ベストプラクティス
- 依存関係プロパティの実装:
- プロパティ変更コールバックを適切に実装
- デフォルト値とメタデータを明確に定義
- テンプレートの設計:
- テンプレート内の要素に名前を付けて、トリガーから参照可能にする
- テンプレートバインディングを適切に使用
- パフォーマンス最適化:
- 不要なアニメーションや効果を避ける
- 仮想化を適切に実装
- アクセシビリティの配慮:
- キーボードナビゲーションをサポート
- スクリーンリーダー対応