zoukankan      html  css  js  c++  java
  • 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>


     

  • 相关阅读:
    Time Zone 【模拟时区转换】(HDU暑假2018多校第一场)
    HDU 1281 棋盘游戏 【二分图最大匹配】
    Codeforces Round #527 (Div. 3) F. Tree with Maximum Cost 【DFS换根 || 树形dp】
    Codeforces Round #527 (Div. 3) D2. Great Vova Wall (Version 2) 【思维】
    Codeforces Round #527 (Div. 3) D1. Great Vova Wall (Version 1) 【思维】
    Codeforces Round #528 (Div. 2, based on Technocup 2019 Elimination Round 4) C. Connect Three 【模拟】
    Avito Cool Challenge 2018 E. Missing Numbers 【枚举】
    Avito Cool Challenge 2018 C. Colorful Bricks 【排列组合】
    005 如何分析问题框架
    004 如何定义和澄清问题
  • 原文地址:https://www.cnblogs.com/kiminozo/p/2406432.html
Copyright © 2011-2022 走看看