WPF에서 아이콘 버튼 구현 방법

아이콘과 텍스트를 결합한 버튼을 구현할 경우 일반적으로 버튼 템플릿을 재정의하는 방식을 사용합니다. 하지만 일반화된 구현을 원한다면 두 가지 방법이 있습니다:

  1. 부착 속성(Attached Property) 활용
  2. 커스텀 컨트롤 개발

부착 속성을 사용하는 방식이 추천됩니다.

1단계: 부착 속성 방식

IconButtonHelper라는 클래스에 Button 타입에 대한 부착 속성을 정의합니다:

public static class IconButtonHelper
{
    // 데이터 바인딩 및 스타일링 지원을 위한 의존성 속성
    public static readonly DependencyProperty IconSizeProperty =
        DependencyProperty.RegisterAttached("IconSize", typeof(int), typeof(IconButtonHelper), new PropertyMetadata(0));

    public static int GetIconSize(DependencyObject obj)
    {
        return (int)obj.GetValue(IconSizeProperty);
    }

    public static void SetIconSize(DependencyObject obj, int value)
    {
        obj.SetValue(IconSizeProperty, value);
    }

    public static readonly DependencyProperty IconGeometryProperty =
        DependencyProperty.RegisterAttached("IconGeometry", typeof(Geometry), typeof(IconButtonHelper), new PropertyMetadata(null));

    public static Geometry GetIconGeometry(DependencyObject obj)
    {
        return (Geometry)obj.GetValue(IconGeometryProperty);
    }

    public static void SetIconGeometry(DependencyObject obj, Geometry value)
    {
        obj.SetValue(IconGeometryProperty, value);
    }
}

스타일 정의:

<ResourceDictionary
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:MyApp.AttachedProperties">
    <Style x:Key="IconButtonBase" TargetType="{x:Type Button}">
        <Setter Property="BorderThickness" Value="0" />
        <Setter Property="HorizontalContentAlignment" Value="Center" />
        <Setter Property="VerticalContentAlignment" Value="Center" />
        <Setter Property="Padding" Value="10,5" />
        <Setter Property="FrameworkElement.Cursor" Value="Hand" />
        <Setter Property="UIElement.SnapsToDevicePixels" Value="True" />
        <Setter Property="local:IconButtonHelper.IconSize" Value="24" />
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type ButtonBase}">
                    <Border
                        Name="border"
                        Background="{TemplateBinding Background}"
                        BorderBrush="{TemplateBinding BorderBrush}"
                        BorderThickness="{TemplateBinding BorderThickness}"
                        SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}">
                        <Grid>
                            <StackPanel
                                Margin="{TemplateBinding Padding}"
                                HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
                                VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
                                Orientation="Vertical">
                                <Path
                                    Name="iconPath"
                                    Width="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}, Path=(local:IconButtonHelper.IconSize)}"
                                    Height="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}, Path=(local:IconButtonHelper.IconSize)}"
                                    Margin="0,0,0,5"
                                    Data="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}, Path=(local:IconButtonHelper.IconGeometry)}"
                                    Fill="{TemplateBinding Foreground}"
                                    Stretch="Uniform" />
                                <ContentPresenter
                                    Name="contentPresenter"
                                    HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
                                    VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
                                    Focusable="False"
                                    RecognizesAccessKey="True"
                                    SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" />
                            </StackPanel>
                        </Grid>
                    </Border>
                    <ControlTemplate.Triggers>
                        <Trigger Property="local:IconButtonHelper.IconGeometry" Value="{x:Null}">
                            <Setter TargetName="iconPath" Property="Visibility" Value="Collapsed" />
                        </Trigger>
                        <Trigger Property="Content" Value="{x:Null}">
                            <Setter TargetName="iconPath" Property="Margin" Value="0" />
                        </Trigger>
                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</ResourceDictionary>

사용 예시:

<Button
    Width="80"
    Height="80"
    local:IconButtonHelper.IconGeometry="{StaticResource RunningGeometry}"
    local:IconButtonHelper.IconSize="40"
    Background="#1e90ff"
    Content="실행"
    Foreground="White"
    Style="{StaticResource IconButtonBase}" />

RunningGeometry 정의:

<PathGeometry x:Key="RunningGeometry">M41.355947 0h572.962133a41.355947 41.355947 0 0 1 41.355947 41.355947v100.037973H0V41.355947A41.355947 41.355947 0 0 1 41.355947 0zM0 210.356907v772.287146A41.355947 41.355947 0 0 0 41.355947 1024h941.288106A41.355947 41.355947 0 0 0 1024 982.644053V210.356907z m851.88608 295.867733L581.973333 776.137387a47.786667 47.786667 0 0 1-66.710186 0.832853 47.786667 47.786667 0 0 1-7.796054-6.294187l-115.083946-115.0976-120.54528 120.558934a47.786667 47.786667 0 0 1-67.611307 0 47.786667 47.786667 0 0 1 0-67.611307l147.12832-147.12832a48.237227 48.237227 0 0 1 13.653333-9.557333 47.786667 47.786667 0 0 1 62.887254 4.096l119.6032 119.507626 236.776106-236.817066a47.786667 47.786667 0 0 1 67.611307 0 47.786667 47.786667 0 0 1 0 67.597653z</PathGeometry>

2단계: 커스텀 컨트롤 방식

추가 내용 예정

태그: WPF AttachedProperties CustomControls DataBinding

6월 7일 20:06에 게시됨