using NPOI.SS.Formula.Functions;
|
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.Controls.Primitives;
|
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;
|
|
namespace XHandler.Controls.FlipView
|
{
|
/// <summary>
|
/// 按照步骤 1a 或 1b 操作,然后执行步骤 2 以在 XAML 文件中使用此自定义控件。
|
///
|
/// 步骤 1a) 在当前项目中存在的 XAML 文件中使用该自定义控件。
|
/// 将此 XmlNamespace 特性添加到要使用该特性的标记文件的根
|
/// 元素中:
|
///
|
/// xmlns:MyNamespace="clr-namespace:XHandler.Controls.FlipView"
|
///
|
///
|
/// 步骤 1b) 在其他项目中存在的 XAML 文件中使用该自定义控件。
|
/// 将此 XmlNamespace 特性添加到要使用该特性的标记文件的根
|
/// 元素中:
|
///
|
/// xmlns:MyNamespace="clr-namespace:XHandler.Controls.FlipView;assembly=XHandler.Controls.FlipView"
|
///
|
/// 您还需要添加一个从 XAML 文件所在的项目到此项目的项目引用,
|
/// 并重新生成以避免编译错误:
|
///
|
/// 在解决方案资源管理器中右击目标项目,然后依次单击
|
/// “添加引用”->“项目”->[浏览查找并选择此项目]
|
///
|
///
|
/// 步骤 2)
|
/// 继续操作并在 XAML 文件中使用控件。
|
///
|
/// <MyNamespace:FlipView/>
|
///
|
/// </summary>
|
public class FlipView : Selector
|
{
|
#region Private Fields
|
private ContentControl PART_CurrentItem;
|
private ContentControl PART_PreviousItem;
|
private ContentControl PART_NextItem;
|
private ListBox PART_ListBox;
|
private FrameworkElement PART_Root;
|
private FrameworkElement PART_Container;
|
private double fromValue = 0.0;
|
private double elasticFactor = 1.0;
|
#endregion
|
|
#region Constructor
|
static FlipView()
|
{
|
DefaultStyleKeyProperty.OverrideMetadata(typeof(FlipView), new FrameworkPropertyMetadata(typeof(FlipView)));
|
SelectedIndexProperty.OverrideMetadata(typeof(FlipView), new FrameworkPropertyMetadata(-1, OnSelectedIndexChanged));
|
}
|
|
public FlipView()
|
{
|
this.CommandBindings.Add(new CommandBinding(NextCommand, this.OnNextExecuted, this.OnNextCanExecute));
|
this.CommandBindings.Add(new CommandBinding(PreviousCommand, this.OnPreviousExecuted, this.OnPreviousCanExecute));
|
}
|
#endregion
|
|
#region Private methods
|
private void OnRootManipulationCompleted(object sender, ManipulationCompletedEventArgs e)
|
{
|
this.fromValue = e.TotalManipulation.Translation.X;
|
if (this.fromValue > 0)
|
{
|
if (this.SelectedIndex > 0)
|
{
|
this.SelectedIndex -= 1;
|
}
|
}
|
else
|
{
|
if (this.SelectedIndex < this.Items.Count - 1)
|
{
|
this.SelectedIndex += 1;
|
}
|
}
|
|
if (this.elasticFactor < 1)
|
{
|
this.RunSlideAnimation(0, ((MatrixTransform)this.PART_Root.RenderTransform).Matrix.OffsetX);
|
}
|
this.elasticFactor = 1.0;
|
}
|
|
private void OnRootManipulationDelta(object sender, ManipulationDeltaEventArgs e)
|
{
|
if (!(this.PART_Root.RenderTransform is MatrixTransform))
|
{
|
this.PART_Root.RenderTransform = new MatrixTransform();
|
}
|
|
Matrix matrix = ((MatrixTransform)this.PART_Root.RenderTransform).Matrix;
|
var delta = e.DeltaManipulation;
|
|
if ((this.SelectedIndex == 0 && delta.Translation.X > 0 && this.elasticFactor > 0)
|
|| (this.SelectedIndex == this.Items.Count - 1 && delta.Translation.X < 0 && this.elasticFactor > 0))
|
{
|
this.elasticFactor -= 0.05;
|
}
|
|
matrix.Translate(delta.Translation.X * elasticFactor, 0);
|
this.PART_Root.RenderTransform = new MatrixTransform(matrix);
|
|
e.Handled = true;
|
}
|
|
private void OnRootManipulationStarting(object sender, ManipulationStartingEventArgs e)
|
{
|
e.ManipulationContainer = this.PART_Container;
|
e.Handled = true;
|
}
|
|
private void OnSizeChanged(object sender, SizeChangedEventArgs e)
|
{
|
this.RefreshViewPort(this.SelectedIndex);
|
}
|
|
private void OnLoaded(object sender, RoutedEventArgs e)
|
{
|
if (this.SelectedIndex > -1)
|
{
|
PART_ListBox.ItemsSource = ItemsSource;
|
PART_ListBox.Width = 26 * itemsCount;
|
this.RefreshViewPort(this.SelectedIndex);
|
}
|
}
|
private static void OnSelectedIndexChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
{
|
var control = d as FlipView;
|
|
control.OnSelectedIndexChanged(e);
|
}
|
|
private void OnSelectedIndexChanged(DependencyPropertyChangedEventArgs e)
|
{
|
if (!this.EnsureTemplateParts())
|
{
|
return;
|
}
|
|
if ((int)e.NewValue >= 0 && (int)e.NewValue < this.Items.Count)
|
{
|
double toValue = (int)e.OldValue < (int)e.NewValue ? -this.ActualWidth : this.ActualWidth;
|
this.RunSlideAnimation(toValue, fromValue);
|
}
|
}
|
|
private void RefreshViewPort(int selectedIndex)
|
{
|
if (!this.EnsureTemplateParts())
|
{
|
return;
|
}
|
|
Canvas.SetLeft(this.PART_PreviousItem, -this.ActualWidth);
|
Canvas.SetLeft(this.PART_NextItem, this.ActualWidth);
|
this.PART_Root.RenderTransform = new TranslateTransform();
|
|
var currentItem = this.GetItemAt(selectedIndex);
|
var nextItem = this.GetItemAt(selectedIndex + 1);
|
var previousItem = this.GetItemAt(selectedIndex - 1);
|
|
this.PART_CurrentItem.Content = currentItem;
|
this.PART_NextItem.Content = nextItem;
|
this.PART_PreviousItem.Content = previousItem;
|
this.PART_ListBox.SelectedIndex = selectedIndex;
|
}
|
|
public void RunSlideAnimation(double toValue, double fromValue = 0)
|
{
|
if (!(this.PART_Root.RenderTransform is TranslateTransform))
|
{
|
this.PART_Root.RenderTransform = new TranslateTransform();
|
}
|
|
var story = AnimationFactory.Instance.GetAnimation(this.PART_Root, toValue, fromValue);
|
story.Completed += (s, e) =>
|
{
|
this.RefreshViewPort(this.SelectedIndex);
|
};
|
story.Begin();
|
}
|
|
private object GetItemAt(int index)
|
{
|
if (index < 0 || index >= this.Items.Count)
|
{
|
return null;
|
}
|
|
return this.Items[index];
|
}
|
|
private bool EnsureTemplateParts()
|
{
|
return this.PART_CurrentItem != null &&
|
this.PART_NextItem != null &&
|
this.PART_PreviousItem != null &&
|
this.PART_Root != null;
|
}
|
|
private void OnPreviousCanExecute(object sender, CanExecuteRoutedEventArgs e)
|
{
|
e.CanExecute = this.SelectedIndex > 0;
|
}
|
|
private void OnPreviousExecuted(object sender, ExecutedRoutedEventArgs e)
|
{
|
this.SelectedIndex -= 1;
|
}
|
|
private void OnNextCanExecute(object sender, CanExecuteRoutedEventArgs e)
|
{
|
e.CanExecute = this.SelectedIndex < (this.Items.Count - 1);
|
}
|
|
private void OnNextExecuted(object sender, ExecutedRoutedEventArgs e)
|
{
|
this.SelectedIndex += 1;
|
}
|
#endregion
|
|
#region Commands
|
|
public static RoutedUICommand NextCommand = new RoutedUICommand("Next", "Next", typeof(FlipView));
|
public static RoutedUICommand PreviousCommand = new RoutedUICommand("Previous", "Previous", typeof(FlipView));
|
|
#endregion
|
|
#region Override methods
|
public override void OnApplyTemplate()
|
{
|
base.OnApplyTemplate();
|
|
this.PART_PreviousItem = this.GetTemplateChild("PART_PreviousItem") as ContentControl;
|
this.PART_NextItem = this.GetTemplateChild("PART_NextItem") as ContentControl;
|
this.PART_CurrentItem = this.GetTemplateChild("PART_CurrentItem") as ContentControl;
|
this.PART_ListBox = this.GetTemplateChild("PART_ListBox") as ListBox;
|
this.PART_Root = this.GetTemplateChild("PART_Root") as FrameworkElement;
|
this.PART_Container = this.GetTemplateChild("PART_Container") as FrameworkElement;
|
|
this.Loaded += this.OnLoaded;
|
this.SizeChanged += this.OnSizeChanged;
|
this.PART_Root.ManipulationStarting += this.OnRootManipulationStarting;
|
this.PART_Root.ManipulationDelta += this.OnRootManipulationDelta;
|
this.PART_Root.ManipulationCompleted += this.OnRootManipulationCompleted;
|
|
|
}
|
#endregion
|
|
int itemsCount = 0;
|
public void SetCount(int count)
|
{
|
itemsCount = count;
|
}
|
}
|
}
|