• WP7自定义控件 TabSwitch控件


    我在写一个类似tabcontrol的选项控件,在很多网站的Menu的效果,不知道怎么取名字,暂时就叫TabSwitch吧。

    效果如下:

    TabSwitch控件要点

    1. 类似横排的单选框组,可以多个选项卡,只能选择一个。
    2. 可以点击其中一个选择一个选项,选中后背景图块移动到选中项,背景图块的刷子可以自定义
    3. 支持选中事件和选择项的绑定。
    4. 为了提高视觉效果,有一个动画过度。

    实现控件要点的方法描述

    1 继承ItemsControl控件可以容纳多个Item,并添加TabSwitchItem类

    2 图块用一个Rectangle搞定,为了实现移动位置将RenderTransform设置为CompositeTransform。

    3 添加一个Selected的event实现选中数据,添加多个SelectItem依赖属性实现绑定。

    4 通过选中位置获取相对的X坐标,通过Item长度计算x所在的index,然后应用动画修改CompositeTransform的TranslateX,TranslateX位置设置为index*Item宽度。

    5 DoubleAnimation+EasingFunction 的弹簧效果ElasticEase可以实现动画过度。

    完整代码

    View Code
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Net;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Controls.Primitives;
    using System.Windows.Documents;
    using System.Windows.Input;
    using System.Windows.Media;
    using System.Windows.Media.Animation;
    using System.Windows.Shapes;

    namespace KiminozoStudio
    {
    public class TabSwitchItem : ContentControl
    {
    public TabSwitchItem()
    {
    this.DefaultStyleKey = typeof(TabSwitchItem);
    }
    }

    public class TabSwitch : ItemsControl
    {
    #region Propertys

    public static readonly DependencyProperty SelectedWidthProperty =
    DependencyProperty.Register("SelectedWidth", typeof(double), typeof(TabSwitch),
    new PropertyMetadata(65.0, SelectedWidthPropertyChanged));
    /// <summary>
    /// 选中项的宽度
    /// </summary>
    public double SelectedWidth
    {
    get { return (double)GetValue(SelectedWidthProperty); }
    set { SetValue(SelectedWidthProperty, value); }
    }

    public static readonly DependencyProperty SelectedItemProperty =
    DependencyProperty.Register("SelectedItem", typeof(TabSwitchItem), typeof(TabSwitch),
    new PropertyMetadata(null));

    /// <summary>
    /// 选中的TabSwitchItem控件
    /// </summary>
    public TabSwitchItem SelectedItem
    {
    get { return (TabSwitchItem)GetValue(SelectedItemProperty); }
    private set { SetValue(SelectedItemProperty, value); }
    }

    public static readonly DependencyProperty SelectedValueProperty =
    DependencyProperty.Register("SelectedValue", typeof(object), typeof(TabSwitch),
    new PropertyMetadata(null));

    /// <summary>
    /// 选中项的内容值
    /// </summary>
    public object SelectedValue
    {
    get { return GetValue(SelectedValueProperty); }
    private set { SetValue(SelectedValueProperty, value); }
    }

    public static readonly DependencyProperty SelectedIndexProperty =
    DependencyProperty.Register("SelectedIndex", typeof(int), typeof(TabSwitch),
    new PropertyMetadata(0, SelectedIndexPropertyChanged));
    /// <summary>
    /// 选中项的序号
    /// </summary>
    public int SelectedIndex
    {
    get { return (int)GetValue(SelectedIndexProperty); }
    set { SetValue(SelectedIndexProperty, value); }
    }

    public static readonly DependencyProperty SelectedBackgroundProperty =
    DependencyProperty.Register("SelectedBackground", typeof(Brush), typeof(TabSwitch),
    new PropertyMetadata(new SolidColorBrush(Colors.Red),
    SelectedBackgroundPropertyChanged));
    /// <summary>
    /// 选择项的背景刷子
    /// </summary>
    public Brush SelectedBackground
    {
    get { return (Brush)GetValue(SelectedBackgroundProperty); }
    set { SetValue(SelectedBackgroundProperty, value); }
    }

    private static void SelectedBackgroundPropertyChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
    {
    var sender = o as TabSwitch;
    if (sender == null || e.NewValue == e.OldValue) return;
    sender.SetSelectedBackground(e.NewValue as Brush);
    }

    private static void SelectedIndexPropertyChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
    {
    var sender = o as TabSwitch;
    if (sender == null || e.NewValue == e.OldValue) return;

    sender.SetSelectedIndex((int)e.NewValue);
    }

    private static void SelectedWidthPropertyChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
    {
    var sender = o as TabSwitch;
    if (sender == null || e.NewValue == e.OldValue) return;

    sender.SetSelectedWidth((double)e.NewValue);
    }
    #endregion

    /// <summary>
    /// 背景的方形
    /// </summary>
    private Rectangle rectangle;
    /// <summary>
    /// 选中事件
    /// </summary>
    public event RoutedEventHandler Selected;

    /// <summary>
    /// 触发选中事件
    /// </summary>
    /// <param name="e"></param>
    private void OnSelected(RoutedEventArgs e)
    {
    RoutedEventHandler handler = Selected;
    if (handler != null) handler(this, e);
    }

    /// <summary>
    /// 改变选中项
    /// </summary>
    /// <param name="index">索引序号</param>
    private void ChangeSelected(int index)
    {
    if (index < 0 || index > Items.Count) return;
    ;
    SelectedIndex = index;
    var switchItem = Items[index] as TabSwitchItem;
    if (switchItem != null)
    {
    SelectedItem = switchItem;
    SelectedValue = switchItem.Content;
    }
    else
    {
    SelectedValue = Items[index];
    }
    OnSelected(new RoutedEventArgs());
    }

    public TabSwitch()
    {
    DefaultStyleKey = typeof(TabSwitch);
    }

    public override void OnApplyTemplate()
    {
    base.OnApplyTemplate();
    rectangle = (Rectangle)GetTemplateChild("rectangle");
    SetSelectedBackground(SelectedBackground);
    SetSelectedWidth(SelectedWidth);
    SetSelectedIndex(SelectedIndex);
    }

    /// <summary>
    /// 设置选中背景的宽度
    /// </summary>
    /// <param name="itemWidth"></param>
    private void SetSelectedWidth(double itemWidth)
    {
    if (rectangle == null) return;
    rectangle.Width = itemWidth;
    }

    /// <summary>
    /// 设置选中背景的刷子
    /// </summary>
    /// <param name="brush"></param>
    private void SetSelectedBackground(Brush brush)
    {
    if (rectangle == null) return;
    rectangle.Fill = brush;
    }

    /// <summary>
    /// 设置选中的项目
    /// </summary>
    /// <param name="index">索引序号</param>
    private void SetSelectedIndex(int index)
    {
    if (rectangle == null) return;
    if (index < 0 || index > Items.Count) return;

    double x = SelectedWidth * index;
    var compositeTransform = (CompositeTransform)rectangle.RenderTransform;
    if (Math.Abs(compositeTransform.TranslateX - x) > 1)
    compositeTransform.TranslateX = SelectedWidth * index;
    }


    #if SILVERLIGHT
    /// <summary>
    /// 重写鼠标左键放开事件
    /// </summary>
    /// <param name="e"></param>
    protected override void OnMouseLeftButtonUp(MouseButtonEventArgs e)
    {
    Point point = e.GetPosition(this);
    var x = (int)(point.X / SelectedWidth) * SelectedWidth;
    int index = (int)(x / SelectedWidth);
    ShowAnimation(x);
    ChangeSelected(index);

    base.OnMouseLeftButtonUp(e);
    }

    #else

    /// <summary>
    /// 重写触摸点中事件
    /// </summary>
    /// <param name="e"></param>
    protected override void OnTap(GestureEventArgs e)
    {
    Point point = e.GetPosition(this);

    var x = (int) (point.X/SelectedWidth)*SelectedWidth;
    int index = (int) (x/SelectedWidth);
    ShowAnimation(x);
    ChangeSelected(index);
    base.OnTap(e);
    }
    #endif

    /// <summary>
    /// 选中后的动画
    /// </summary>
    /// <param name="x"></param>
    private void ShowAnimation(double x)
    {
    //移动X坐标,并使用EasingFunction的弹簧特效
    var animation = new DoubleAnimation
    {
    Duration = new Duration(new TimeSpan(0, 0, 0, 0, 500)),
    To = x,
    EasingFunction =
    new ElasticEase { EasingMode = EasingMode.EaseOut, Oscillations = 1, Springiness = 5 }
    };
    Storyboard.SetTarget(animation, rectangle);
    Storyboard.SetTargetProperty(animation, new PropertyPath("(UIElement.RenderTransform).(CompositeTransform.TranslateX)"));
    var storyboard = new Storyboard();
    storyboard.Children.Add(animation);
    storyboard.Begin();
    }
    }
    }

    对应的Generic.xaml

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


    <Style TargetType="local:TabSwitch">
    <Setter Property="Width" Value="400"/>
    <Setter Property="Height" Value="50"/>
    <Setter Property="ItemsPanel">
    <Setter.Value>
    <ItemsPanelTemplate>
    <StackPanel Orientation="Horizontal" />
    </ItemsPanelTemplate>
    </Setter.Value>
    </Setter>
    <Setter Property="Template">
    <Setter.Value>
    <ControlTemplate TargetType="local:TabSwitch">
    <Grid>
    <Rectangle x:Name="rectangle" Width="65" Height="40" RadiusY="5" RadiusX="5"
    Fill
    ="Red" HorizontalAlignment="Left">
    <Rectangle.RenderTransform>
    <CompositeTransform/>
    </Rectangle.RenderTransform>
    </Rectangle>
    <ItemsPresenter/>
    </Grid>
    </ControlTemplate>
    </Setter.Value>
    </Setter>
    </Style>

    <Style TargetType="local:TabSwitchItem">
    <Setter Property="Width" Value="65"/>
    <Setter Property="Height" Value="40"/>
    <Setter Property="Margin" Value="0,10,0,3"/>
    <Setter Property="Template">
    <Setter.Value>
    <ControlTemplate TargetType="local:TabSwitchItem">
    <TextBlock Text="{TemplateBinding Content}" TextAlignment="Center" />
    </ControlTemplate>
    </Setter.Value>
    </Setter>
    </Style>

    </ResourceDictionary>

    测试代码:

    <my:TabSwitch Height="56" HorizontalAlignment="Left" Margin="52,21,0,0" x:Name="tabSwitch1" VerticalAlignment="Top" Width="349" d:IsHidden="True" FontSize="16">
    <my:TabSwitchItem Content="选项1"/>
    <my:TabSwitchItem Content="选项2"/>
    <my:TabSwitchItem Content="选项3"/>
    <my:TabSwitchItem Content="选项4"/>
    <my:TabSwitchItem Content="选项5"/>
    </my:TabSwitch>


     

  • 相关阅读:
    2020-2021-1 20209326 《Linux内核原理与分析》第八周作业
    2020-2021-1 20209326 《Linux内核原理与分析》第七周作业
    2020-2021-1 20209326 《Linux内核原理与分析》第六周作业
    2020-2021-1 20209326 《Linux内核原理与分析》第五周作业
    2020-2021-1 20209326 《Linux内核原理与分析》第四周作业
    2020-2021-1 20209326 《Linux内核原理与分析》第三周作业
    2020-2021-1 20209326 《Linux内核原理与分析》第二周作业 MyOD(选作)
    2020-2021-1 202029325 《Linux内核原理与分析》第十二周作业
    2020-2021-1 202029325 《Linux内核原理与分析》第十一周作业
    2020-2021-1 202029325 《Linux内核原理与分析》第九周作业
  • 原文地址:https://www.cnblogs.com/kiminozo/p/2406432.html
Copyright © 2020-2023  润新知