2013년 2월 28일 목요일

WPF Custom Toggle Image Button

http://www.syntaxstudio.co.uk/2012/12/creating-a-toggle-image-button-control-in-wpf/

사이트의 소스를 수정하여 ImageButton 을 구현하였다. 기존 소스를 확장하여
Check/Uncheck 토글뿐 아니라 일반 버튼으로도 사용할 수 있다.

< ImageButton.xaml >
 <ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"  
           xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"  
           xmlns:local="clr-namespace:SyntaxStudio.ToggleImageButton">  
   
   <Style x:Key="ImageButton" TargetType="{x:Type local:ImageButton}">  
     <Setter Property="Foreground" Value="#FFB8B8B8" />  
     <Setter Property="FontWeight" Value="Bold" />  
     <Setter Property="FontSize" Value="14" />  
     <Setter Property="Template">  
       <Setter.Value>  
         <ControlTemplate TargetType="{x:Type local:ImageButton}">  
           <Border x:Name="PART_Border"  
               Background="{TemplateBinding Background}"  
               BorderBrush="{TemplateBinding BorderBrush}"  
               BorderThickness="{TemplateBinding BorderThickness}">  
   
             <StackPanel>  
               <Grid>  
                 <Image x:Name="PART_Icon"  
                     Source="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=CurrentImage}"   
                     Width="{TemplateBinding Width}"   
                     Height="{TemplateBinding Height}" />  
                 <ContentPresenter Content="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Content}"   
                     Margin="0,0,0,12"  
                     HorizontalAlignment="Center"  
                     VerticalAlignment="Bottom">  
                 </ContentPresenter>  
               </Grid>  
             </StackPanel>  
           </Border>  
           <!--  
           <ControlTemplate.Triggers>  
             <Trigger Property="IsChecked" Value="True">  
               <Setter TargetName="PART_Icon" Property="Source" Value="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=ActiveIcon}" />  
             </Trigger>  
             <Trigger Property="IsChecked" Value="False">  
               <Setter TargetName="PART_Icon" Property="Source" Value="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=InActiveIcon}" />  
             </Trigger>  
           </ControlTemplate.Triggers>  
           -->  
         </ControlTemplate>  
       </Setter.Value>  
     </Setter>  
   </Style>  
   
   <Style x:Key="ImageButton1" TargetType="{x:Type local:ImageButton}">  
     <Setter Property="Foreground" Value="#FFB8B8B8" />  
     <Setter Property="Template">  
       <Setter.Value>  
         <ControlTemplate TargetType="{x:Type local:ImageButton}">  
           <Border x:Name="PART_Border"  
               Background="{TemplateBinding Background}"  
               BorderBrush="{TemplateBinding BorderBrush}"  
               BorderThickness="{TemplateBinding BorderThickness}">  
   
             <StackPanel>  
               <Grid>  
                 <Image x:Name="PART_Icon"  
                     Source="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=CurrentImage}"   
                     Width="{TemplateBinding Width}"   
                     Height="{TemplateBinding Height}" />  
                 <ContentPresenter Content="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Content}"   
                     HorizontalAlignment="Center"  
                     VerticalAlignment="Center">  
                 </ContentPresenter>  
               </Grid>  
             </StackPanel>  
           </Border>  
           <!--  
           <ControlTemplate.Triggers>  
             <Trigger Property="IsChecked" Value="True">  
               <Setter TargetName="PART_Icon" Property="Source" Value="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=ActiveIcon}" />  
             </Trigger>  
             <Trigger Property="IsChecked" Value="False">  
               <Setter TargetName="PART_Icon" Property="Source" Value="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=InActiveIcon}" />  
             </Trigger>  
           </ControlTemplate.Triggers>  
           -->  
         </ControlTemplate>  
       </Setter.Value>  
     </Setter>  
   </Style>  
 </ResourceDictionary>  

< ImageButton.cs >
 // --------------------------------  
 // <copyright file="ToggleImageButton.cs" company="SyntaxStudio">  
 //   Copyright 2012, www.syntaxstudio.co.uk  
 // </copyright>  
 // <author>Ryan Haworth</author>  
 // ---------------------------------  
   
 using System;  
 using System.Collections.Generic;  
 using System.Linq;  
 using System.Text;  
 using System.Windows;  
 using System.Windows.Controls;  
 using System.Windows.Data;  
 using System.Windows.Documents;  
 using System.Windows.Input;  
 using System.Windows.Media;  
 using System.Windows.Media.Imaging;  
 using System.Windows.Navigation;  
 using System.Windows.Shapes;  
 using System.Windows.Controls.Primitives;  
   
 namespace SyntaxStudio.ToggleImageButton  
 {  
   /// <summary>  
   /// The is is a custom control class for the toggle button. This toggle button will  
   /// accept two images. As the button is toggled between an inactive state to an  
   /// active state the button will change its image.  
   /// </summary>  
   public class ImageButton : ButtonBase  
   {  
     private static readonly DependencyProperty CurrentImageProperty = DependencyProperty.Register("CurrentImage", typeof(ImageSource), typeof(ImageButton));  
     private static readonly DependencyProperty UnCheckedImageProperty = DependencyProperty.Register("UnCheckedImage", typeof(ImageSource), typeof(ImageButton));  
     private static readonly DependencyProperty UnCheckedHoverImageProperty = DependencyProperty.Register("UnCheckedHoverImage", typeof(ImageSource), typeof(ImageButton));  
     private static readonly DependencyProperty CheckedImageProperty = DependencyProperty.Register("CheckedImage", typeof(ImageSource), typeof(ImageButton));  
     private static readonly DependencyProperty CheckedHoverImageProperty = DependencyProperty.Register("CheckedHoverImage", typeof(ImageSource), typeof(ImageButton));  
     private static readonly DependencyProperty PressedImageProperty = DependencyProperty.Register("PressedImage", typeof(ImageSource), typeof(ImageButton));  
     private static readonly DependencyProperty DisabledImageProperty = DependencyProperty.Register("DisabledImage", typeof(ImageSource), typeof(ImageButton));  
   
     private bool isHovering = false;  
   
     private bool isChecked = false;  
     public bool IsChecked  
     {  
       get { return isChecked; }  
       set  
       {  
         if (value == false)  
         {  
           if (isHovering == false)  
             CurrentImage = UnCheckedImage;  
           else  
             CurrentImage = UnCheckedHoverImage;  
         }  
         else  
         {  
           if (isHovering == false)  
             CurrentImage = CheckedImage;  
           else  
             CurrentImage = CheckedHoverImage;  
         }  
         isChecked = value;  
       }  
     }  
   
     private bool isDisabled = false;  
     public bool IsDisabled  
     {  
       get { return isDisabled; }  
       set  
       {  
         if (value == false)  
         {  
           CurrentImage = UnCheckedImage;  
         }  
         else  
         {  
           CurrentImage = DisabledImage;  
         }  
       }  
     }  
   
     public ImageSource CurrentImage  
     {  
       get { return (ImageSource)GetValue(CurrentImageProperty); }  
       set { SetValue(CurrentImageProperty, value); }  
     }  
   
     public ImageSource UnCheckedImage  
     {  
       get { return (ImageSource)GetValue(UnCheckedImageProperty); }  
       set { SetValue(UnCheckedImageProperty, value); }  
     }  
   
     public ImageSource UnCheckedHoverImage  
     {  
       get { return (ImageSource)GetValue(UnCheckedHoverImageProperty); }  
       set { SetValue(UnCheckedHoverImageProperty, value); }  
     }  
   
     public ImageSource CheckedImage  
     {  
       get { return (ImageSource)GetValue(CheckedImageProperty); }  
       set { SetValue(CheckedImageProperty, value); }  
     }  
   
     public ImageSource CheckedHoverImage  
     {  
       get { return (ImageSource)GetValue(CheckedHoverImageProperty); }  
       set { SetValue(CheckedHoverImageProperty, value); }  
     }  
   
     public ImageSource PressedImage  
     {  
       get { return (ImageSource)GetValue(PressedImageProperty); }  
       set { SetValue(PressedImageProperty, value); }  
     }  
   
     public ImageSource DisabledImage  
     {  
       get { return (ImageSource)GetValue(DisabledImageProperty); }  
       set { SetValue(DisabledImageProperty, value); }  
     }  
   
     public ImageButton()  
     {  
       CurrentImage = UnCheckedImage;  
     }  
   
     static ImageButton()  
     {  
       DefaultStyleKeyProperty.OverrideMetadata(typeof(ImageButton), new FrameworkPropertyMetadata(typeof(ImageButton)));  
     }  
   
     protected override void OnMouseEnter(MouseEventArgs e)  
     {  
       base.OnMouseEnter(e);  
   
       isHovering = true;  
   
       if (IsChecked == false)  
       {  
         if (UnCheckedHoverImage != null)  
           CurrentImage = UnCheckedHoverImage;  
       }  
       else  
       {  
         if (CheckedHoverImage != null)  
           CurrentImage = CheckedHoverImage;  
       }  
     }  
   
     protected override void OnMouseLeave(MouseEventArgs e)  
     {  
       base.OnMouseLeave(e);  
   
       isHovering = false;  
   
       if (IsChecked == false)  
       {  
         if (UnCheckedImage != null)  
           CurrentImage = UnCheckedImage;  
       }  
       else  
       {  
         if (CheckedImage != null)  
           CurrentImage = CheckedImage;  
       }  
     }  
   
     protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e)  
     {  
       base.OnMouseLeftButtonDown(e);  
       if (PressedImage != null)  
         CurrentImage = PressedImage;  
     }  
   
     protected override void OnMouseLeftButtonUp(MouseButtonEventArgs e)  
     {  
       base.OnMouseLeftButtonUp(e);  
       if (PressedImage != null)  
       {  
         if (IsChecked == true)  
           CurrentImage = CheckedImage;  
         else  
           CurrentImage = UnCheckedImage;  
       }  
     }  
   }  
 }  
   


< 사용예 >
...
xmlns:ToggleImageButton="clr-namespace:SyntaxStudio.ToggleImageButton" 
...
                <ToggleImageButton:ImageButton x:Name="btnToggleBorder" Width="65" Height="35" 
                                               CurrentImage="/images/uncheck.png"
                                               UnCheckedImage="/images/uncheck.png" UnCheckedHoverImage="/images/uncheck_hover.png" 
                                               CheckedImage="/images/check.png" CheckedHoverImage="check_hover.png" 
                                               Click="btnToggleBorder_Click"/>
...
btnToggleBorder.IsChecked = true/false 를 줘서 토글상태 변경




WPF Grid Sliding Animation

http://www.codeproject.com/Articles/18379/WPF-Tutorial-Part-2-Writing-a-custom-animation-cla
에 있는 클래스를 조금 수정하여 Grid 패널을 숨기고 보여주는 기능을 애니메이션으로 구현.
기존 클래스를 사용하면 Grid Column/Row 의 GridLength 속성이 Pixel 인 경우 제대로 동작하지 않는 문제와 애니메이션후 동작하지않는 문제가 있었고 위 주소를 참조하여 이를 해결.

< GridLengthAnimation.cs >

namespace GridAnimationDemo
{
    internal class GridLengthAnimation : AnimationTimeline
    {
        static GridLengthAnimation()
        {
            FromProperty = DependencyProperty.Register("From", typeof(GridLength),
                typeof(GridLengthAnimation));

            ToProperty = DependencyProperty.Register("To", typeof(GridLength),
                typeof(GridLengthAnimation));
        }

        public override Type TargetPropertyType
        {
            get
            {
                return typeof(GridLength);
            }
        }

        protected override System.Windows.Freezable CreateInstanceCore()
        {
            return new GridLengthAnimation();
        }

        public static readonly DependencyProperty FromProperty;
        public GridLength From
        {
            get
            {
                return (GridLength)GetValue(GridLengthAnimation.FromProperty);
            }
            set
            {
                SetValue(GridLengthAnimation.FromProperty, value);
            }
        }

        public static readonly DependencyProperty ToProperty;
        public GridLength To
        {
            get
            {
                return (GridLength)GetValue(GridLengthAnimation.ToProperty);
            }
            set
            {
                SetValue(GridLengthAnimation.ToProperty, value);
            }
        }

        public override object GetCurrentValue(object defaultOriginValue,
            object defaultDestinationValue, AnimationClock animationClock)
        {
            double fromVal = ((GridLength)GetValue(GridLengthAnimation.FromProperty)).Value;
            double toVal = ((GridLength)GetValue(GridLengthAnimation.ToProperty)).Value;

            if (fromVal > toVal)
            {
                return new GridLength((1 - animationClock.CurrentProgress.Value) * (fromVal - toVal) + toVal,
                ((GridLength)GetValue(GridLengthAnimation.FromProperty)).GridUnitType);
            }

            return new GridLength(animationClock.CurrentProgress.Value * (toVal - fromVal) + fromVal,
            ((GridLength)GetValue(GridLengthAnimation.ToProperty)).GridUnitType);
        }
    }
}

< Show/Hide 애니메이션 >

GridLength orgGridWidth = gridMain.ColumnDefinitions[0].Width;
int minPanelWidth = 30;
        private void btnSlidePanel_Click(object sender, RoutedEventArgs e)
        {
            if (gridMain.ColumnDefinitions[0].Width.Value > minPanelWidth)  // hide grid
            {
                orgGridWidth = gridMain.ColumnDefinitions[0].Width;                
                GridLengthAnimation gla = new GridLengthAnimation();
                gla.From = gridMain.ColumnDefinitions[0].Width;
                gla.To = new GridLength(minPanelWidth, GridUnitType.Pixel);
                gla.Duration = new TimeSpan(0, 0, 0, 0, 300);
                gla.FillBehavior = FillBehavior.HoldEnd;
                gla.Completed += delegate(object o, EventArgs evt)
                {
                    gridMain.ColumnDefinitions[0].Width = new GridLength(minPanelWidth, GridUnitType.Pixel);
                    gridMain.ColumnDefinitions[0].BeginAnimation(ColumnDefinition.WidthProperty, null);
                    gla.FillBehavior = FillBehavior.Stop;
                };
                gridMain.ColumnDefinitions[0].BeginAnimation(ColumnDefinition.WidthProperty, gla);
            }
            else // show grid
            {
                GridLengthAnimation gla = new GridLengthAnimation();
                gla.From = gridMain.ColumnDefinitions[0].Width;
                gla.To = orgGridWidth;
                gla.Duration = new TimeSpan(0, 0, 0, 0, 300);
                gla.FillBehavior = FillBehavior.HoldEnd;
                gla.Completed += delegate(object o, EventArgs evt)
                {
                    gridMain.ColumnDefinitions[0].Width = orgGridWidth;
                    gridMain.ColumnDefinitions[0].BeginAnimation(ColumnDefinition.WidthProperty, null);
                    gla.FillBehavior = FillBehavior.Stop;
                };
                gridMain.ColumnDefinitions[0].BeginAnimation(ColumnDefinition.WidthProperty, gla);
            }
        }