Ostatnio męczyłem się z jednym tematem z WPF’a a mianowicie stworzyć przycisk, który jako swoje stany (Normal, Pressed, Disabled, Hover) będzie miał obrazki. Chciałem to zrobić ze zwykłego przycisku odpowiednio tworząc jego Template i wykorzystując potęgę Triggerów. Wszystkie moje próby spaliły na panewce bo i problem już się pojawiał w momencie gdzie przechowywać ścieżki do 4 obrazków.
W końcu przyszedł pomysł (wsparty przejrzeniem SO – dzięki @Gutek) i zabrałem się za robienie CustomControl. Ten post jest na przyszłość no i dla innych jakby ktoś potrzebował – taki Step By Step.
Zaczynamy zatem od stworzenia nowej klasy – nazwijmy ją MenuButton. Możemy też dodać od razu CustomControl (WPF). Wtedy od razu dostaniemy odpowiedni wpis w konstruktorze:

            DefaultStyleKeyProperty.OverrideMetadata(typeof(MenuButton),
                        new FrameworkPropertyMetadata(typeof(MenuButton)));

Oraz odpowiedni wpis w Generic.xaml w folderze Themes.

    <Style TargetType=”{x:Type local:MenuButton}”>
        <Setter Property=”Template”>
            <Setter.Value>
                <ControlTemplate TargetType=”{x:Type local:MenuButton}”>
                    <Border Background=”{TemplateBinding Background}”
                           BorderBrush=”{TemplateBinding BorderBrush}”
                           BorderThickness=”{TemplateBinding BorderThickness}”>
                    Border>
                ControlTemplate>
            Setter.Value>
        Setter>
    Style>

Dzięki temu nasza kontrolka straciła właśnie swój wygląd. I dobrze. Zabierzmy się zatem z powrotem do pracy. Tak więc podsumowując nasza klasa na chwilę obecną wygląda tak:

    public class MenuButton : Control
    {
        static MenuButton()
        {
            DefaultStyleKeyProperty.OverrideMetadata(typeof(MenuButton),
                        new FrameworkPropertyMetadata(typeof(MenuButton)));
        }
    }

Teraz potrzebujemy mieć miejsce gdzie będziemy przechowywać ścieżki do naszych obrazków. Aby móc je ładnie bindować i wykorzystywać całą potęgę WPFa zrobimy je jako DependencyProprty.

        public static DependencyProperty NormalStateImageProperty;
        public string NormalStateImage
        {
            get { return (string)GetValue(NormalStateImageProperty); }
            set { SetValue(NormalStateImageProperty, value); }
        }

Oraz zarejestrujmy ją w konstruktorze.

NormalStateImageProperty = DependencyProperty.Register(“NormalStateImage”,
    typeof(string), typeof(MenuButton));

I powtarzamy operację z pozostałymi 3-ma stanami: Hover, Pressed oraz Disabled. Zanim jednak przejdziemy do definiowania wyglądu zobaczmy jak będziemy mogli dodawać nasz przycisk:

<controls:MenuButton Height=”24″ Width=”24″ 
                    NormalStateImage=”/Images/zoom_in_normal.png”
                    HoverStateImage=”/Images/zoom_in_hover.png”
                    PressedStateImage=”/Images/zoom_in_clicked.png”
                    DisabledStateImage=”/Images/zoom_in_disabled.png” x:Name=”btn” />

Czyli dość intuicyjnie. Ok. Przejdźmy do wyglądu. Z tego kodu nie jestem 100% zadowolony więc jakby ktoś chciał pomóc w poprawieniu to zapraszam do napisania komentarza lub maila, na adres który jest w profilu. Zacznijmy od samego wyglądu. Robimy to w pliku Generic.xaml

<Setter Property=”Template”>
    <Setter.Value>
        <ControlTemplate TargetType=”{x:Type local:MenuButton}”>
            <Grid x:Name=”grid”>
                <Image Name=”DefaultImage” Source=”{TemplateBinding NormalStateImage,
                                           Converter={StaticResource StringToImage}}” />
                <Image Name=”HoverImage”   Source=”{TemplateBinding HoverStateImage,
                                           Converter={StaticResource StringToImage}}”
                                           Visibility=”Hidden” />                       
                <Image Name=”ClickImage”   Source=”{TemplateBinding PressedStateImage,
                                           Converter={StaticResource StringToImage}}”
                                           Visibility=”Hidden” />
                <Image Name=”DisabledImage” Source=”{TemplateBinding DisabledStateImage,
                                           Converter={StaticResource StringToImage}}”
                                           Visibility=”Hidden” />
            Grid>                   
        ControlTemplate>
    Setter.Value>
Setter>

4 obrazki. Tu jest minus tego rozwiązania. Odnośnie konwertera to jest to prosty – weź-łańcuch-znaków-i-zamień-na-bitmapę konwerter. Pominę go. Jeśli ktoś będzie go potrzebował niech napisze maila – podeślę.
Na koniec zostały nam same smaczki czyli zmiana stanów. W moim przypadku wyglądają one następująco:

<ControlTemplate.Triggers>
    <Trigger Property=”IsMouseOver” Value=”True”>
        <Setter TargetName=”ClickImage” Property=”Visibility” Value=”Hidden” />
        <Setter TargetName=”DefaultImage” Property=”Visibility” Value=”Hidden” />
        <Setter TargetName=”DisabledImage” Property=”Visibility” Value=”Hidden” />
        <Setter TargetName=”HoverImage” Property=”Visibility” Value=”Visible” />
    Trigger>
    <Trigger Property=”IsPressed” Value=”True”>
        <Setter TargetName=”grid” Property=”RenderTransformOrigin” Value=”.5,.5″ />
        <Setter TargetName=”grid” Property=”RenderTransform”>
            <Setter.Value>
                <ScaleTransform ScaleX=”.9″ ScaleY=”.9″ />
            Setter.Value>
        Setter>
    Trigger>
    <Trigger Property=”IsEnabled” Value=”False”>
        <Setter TargetName=”ClickImage” Property=”Visibility” Value=”Hidden” />
        <Setter TargetName=”DefaultImage” Property=”Visibility” Value=”Hidden” />
        <Setter TargetName=”HoverImage” Property=”Visibility” Value=”Hidden” />
        <Setter TargetName=”DisabledImage” Property=”Visibility” Value=”Visible” />
    Trigger>
ControlTemplate.Triggers>

Jak widać cały trik polega na odpowiednim ukrywaniu i pokazywaniu obrazków. Nie jest to może bardzo wyszukane rozwiązanie ale działa. Dodatkowo w trybie Pressed zrezygnowałem z obrazka na rzecz skalowania grida. Ot i cała filozofia. Może komuś się przyda! Całość do ściągnięcia z http://plukasik.eu/files/MenuButton.zip
Uwaga: Dla czytających tylko RSS’a tego bloga. Zacząłem ćwierkać 🙂 – @pawel_lukasik. Można śledzić – zapraszam.