天天看點

WPF自定義控件05:ToggleButton

        在不少的手機端應用程式中,經常可以看到一些開關按鈕,它可以非常友善的進行狀态的切換操作。本文将介紹一下自定義的開關按鈕ToggleButton,它與FlatCheckBox實作過程非常類似,它也是繼承自CheckBox。下面将詳細介紹具體的實作細節。

1 WPF項目結構

     基于之前建立的WPF示例項目,在其中建立一些新的關于ToggleButton的項目檔案。本質上,ToggleButton是繼承CheckBox控件,利用CheckBox控件的自身的屬性和方法,可以減少自己實作的難度和複雜度。另外,通過自定義Style實作的Flat UI效果的ToggleButton。具體的項目結構如下圖所示:

WPF自定義控件05:ToggleButton

其中的Fonts目錄下存放各種圖示字型檔案,Style目錄下存放各種控件的UI 樣式定義檔案,ToggleButton.xaml就是ToggleButton控件的樣式定義檔案。另外,需要将其注冊到Generic.xaml檔案中。

2 WPF ToggleButton實作

     首先在Yd.WpfControls項目中添加一個類ToggleButton.cs,它繼承自CheckBox控件,示例代碼如下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;

namespace Yd.WpfControls
{
    public class ToggleButton : CheckBox
    {
        static ToggleButton()
        {
            DefaultStyleKeyProperty.OverrideMetadata(typeof(ToggleButton), 
                new FrameworkPropertyMetadata(typeof(ToggleButton)));
        }
        public static readonly DependencyProperty OffTextProperty = 
            DependencyProperty.Register("OffText", typeof(string), typeof(ToggleButton),
                new PropertyMetadata("Off"));
        public string OffText
        {
            get { return (string)GetValue(OffTextProperty); }
            set { SetValue(OffTextProperty, value); }
        }

        public static readonly DependencyProperty OnTextProperty = 
            DependencyProperty.Register("OnText", typeof(string), typeof(ToggleButton), 
                new PropertyMetadata("On"));
        public string OnText
        {
            get { return (string)GetValue(OnTextProperty); }
            set { SetValue(OnTextProperty, value); }
        }

        public static readonly DependencyProperty OnForegroundProperty =
            DependencyProperty.Register("OnForeground", typeof(Brush), typeof(ToggleButton),
                new PropertyMetadata(Brushes.Silver));
        public Brush OnForeground
        {
            get { return (Brush)GetValue(OnForegroundProperty); }
            set { SetValue(OnForegroundProperty, value); }
        }

        public static readonly DependencyProperty OnBackgroundProperty =
            DependencyProperty.Register("OnBackground", typeof(Brush), typeof(ToggleButton),
                new PropertyMetadata(Brushes.Green));
        public Brush OnBackground
        {
            get { return (Brush)GetValue(OnBackgroundProperty); }
            set { SetValue(OnBackgroundProperty, value); }
        }
    }
}      

 其中定義了幾個屬性,特别是OnText和OffText,它們分别代表開關控件在打開狀态的文本和關閉狀态的文本。預設情況下,OnText文本是On,而OffText文本是Off。另外OnBackground是打開狀态下的背景色,OnForeground是打開狀态下的字型顔色。ToggleButton控件的UI樣式主要就是依靠ToggleButton.xaml檔案進行定義的,示例代碼如下:

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                    xmlns:local="clr-namespace:Yd.WpfControls"
                    >

    <ResourceDictionary.MergedDictionaries>
        <ResourceDictionary  Source="/Yd.WpfControls;component/Style/IconFont.xaml"/>
    </ResourceDictionary.MergedDictionaries>

    <Style TargetType="{x:Type local:ToggleButton}">
        <Setter Property="Background" Value="{x:Static local:FlatColors.PETER_RIVER}"></Setter>
        <Setter Property="Foreground" Value="#DDE8E1"></Setter>
        <Setter Property="OnForeground" Value="{x:Static local:FlatColors.EMERALD}"></Setter>
        <Setter Property="OnBackground" Value="{x:Static local:FlatColors.BELIZE_HOLE}"></Setter>
        <Setter Property="FontSize" Value="{x:Static local:FlatFonts.contentFontSize}"/>
        <Setter Property="FontFamily" Value="{x:Static local:FlatFonts.contentFontFamily}"/>
        <Setter Property="Cursor" Value="Hand"></Setter>
        <Setter Property="Width" Value="75"></Setter>
        <Setter Property="Height" Value="28"></Setter>
        <Setter Property="VerticalAlignment" Value="Center"></Setter>
        <Setter Property="HorizontalAlignment" Value="Center"></Setter>
        <Setter Property="Template">
            <Setter.Value>
                <!--控件模闆-->
                <ControlTemplate TargetType="{x:Type local:ToggleButton}">
                    <Grid x:Name="grid"  VerticalAlignment="Center" >
                        <Border x:Name="border" Width="75" Height="28" 
                                Background="{TemplateBinding Background}" SnapsToDevicePixels="True"
                                Margin="0,0,0,0" CornerRadius="14" Cursor="Hand">
                            <StackPanel Orientation="Horizontal">
                                <!--圓形-->
                                <Border x:Name="state" Width="24" Height="24" Margin="3,2,1,2" CornerRadius="12" SnapsToDevicePixels="True"
                                    Background="{TemplateBinding Foreground}">
                                    <Border.RenderTransform>
                                        <TranslateTransform x:Name="transState" X="0"></TranslateTransform>
                                    </Border.RenderTransform>
                                </Border>
                                <!--文本-->
                                <TextBlock Width="40" Foreground="{TemplateBinding Foreground}" x:Name="text" 
                                           FontFamily="{TemplateBinding FontFamily}"
                                           FontSize="{TemplateBinding FontSize}"
                                           Text="{TemplateBinding OffText}" VerticalAlignment="Center" 
                                           TextAlignment="Center" Cursor="Hand">
                                    <TextBlock.RenderTransform>
                                        <TranslateTransform x:Name="transText" X="0"></TranslateTransform>
                                    </TextBlock.RenderTransform>
                                </TextBlock>
                            </StackPanel>
                        </Border>
                    </Grid>

                    <!--觸發器,切換有動畫-->
                    <ControlTemplate.Triggers>
                        <Trigger Property="IsChecked" Value="True">
                            <Setter Property="Text" Value="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}, Path=OnText}" TargetName="text"/>
                            <Setter Property="Background" Value="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}, Path=OnForeground}" TargetName="state"/>
                            <Setter Property="Foreground" Value="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}, Path=OnForeground}" TargetName="text"/>
                            <Setter Property="Background" Value="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}, Path=OnBackground}" TargetName="border"/>
                            <Trigger.EnterActions>
                                <BeginStoryboard>
                                    <Storyboard>
                                        <DoubleAnimation Storyboard.TargetName="transState" Storyboard.TargetProperty="X" To="46" Duration="0:0:0.3" />
                                        <DoubleAnimation Storyboard.TargetName="transText" Storyboard.TargetProperty="X" To="-25" Duration="0:0:0.3" />
                                    </Storyboard>
                                </BeginStoryboard>
                            </Trigger.EnterActions>
                            <Trigger.ExitActions>
                                <BeginStoryboard>
                                    <Storyboard>
                                        <DoubleAnimation Storyboard.TargetName="transState" Storyboard.TargetProperty="X" To="0" Duration="0:0:0.3" />
                                        <DoubleAnimation Storyboard.TargetName="transText" Storyboard.TargetProperty="X" To="0" Duration="0:0:0.3" />
                                    </Storyboard>
                                </BeginStoryboard>
                            </Trigger.ExitActions>
                        </Trigger>

                        <Trigger Property="IsEnabled" Value="false">
                            <Setter Property="Opacity" Value="0.5" TargetName="border"/>
                        </Trigger>
                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

</ResourceDictionary>      

通過觸發器Trigger來确定ToggleButton屬性IsChecked變化時,如何在UI上進行顯示,比如是打開狀态,屬性IsChecked的值為true時,則會觸發如下觸發器定義的規則,其中關于transState控件元素沿着X軸往右邊移動46,而文本transText則沿着X軸往左邊移動25。

3 WPF ToggleButton測試

     首先,需要重新生成一下項目檔案,然後在WpfControls項目中添加自定義控件ToggleButton,MainWindow.xaml部分示例代碼如下:

<WpfControls:ToggleButton Content="SwitchCheckBox" HorizontalAlignment="Left" 
                                  Margin="120,167,0,0" VerticalAlignment="Top"/>
<WpfControls:ToggleButton Content="SwitchCheckBox"  OnText="開" OffText="關"
                                    HorizontalAlignment="Left" Margin="286,167,0,0" 
                                  VerticalAlignment="Top"/>      

運作界面如下:

WPF自定義控件05:ToggleButton