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>


 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM