using System; using System.Collections.Generic; using System.Windows; using System.Windows.Controls; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Media.Media3D; using HandyControl.Data; namespace HandyControl.Controls { /// /// 封面流 /// [TemplatePart(Name = ElementViewport3D, Type = typeof(Viewport3D))] [TemplatePart(Name = ElementCamera, Type = typeof(ProjectionCamera))] [TemplatePart(Name = ElementVisualParent, Type = typeof(ModelVisual3D))] public class CoverFlow : Control { private const string ElementViewport3D = "PART_Viewport3D"; private const string ElementCamera = "PART_Camera"; private const string ElementVisualParent = "PART_VisualParent"; /// /// 最大显示数量的一半 /// private const int MaxShowCountHalf = 7; /// /// 页码 /// public static readonly DependencyProperty PageIndexProperty = DependencyProperty.Register( "PageIndex", typeof(int), typeof(CoverFlow), new PropertyMetadata(ValueBoxes.Int0Box, OnPageIndexChanged, CoercePageIndex)); private static object CoercePageIndex(DependencyObject d, object baseValue) { var ctl = (CoverFlow) d; var v = (int) baseValue; if (v < 0) { return 0; } if (v >= ctl._uriList.Count) { return ctl._uriList.Count - 1; } return v; } private static void OnPageIndexChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { var ctl = (CoverFlow) d; ctl.UpdateIndex((int)e.NewValue, (int)e.OldValue); } /// /// 是否循环 /// public static readonly DependencyProperty LoopProperty = DependencyProperty.Register( "Loop", typeof(bool), typeof(CoverFlow), new PropertyMetadata(ValueBoxes.FalseBox)); /// /// 存储所有的资源标识符 /// private readonly Dictionary _uriList = new Dictionary(); /// /// 当前在显示范围内的项 /// private readonly Dictionary _itemShowList = new Dictionary(); /// /// 相机 /// private ProjectionCamera _camera; /// /// 3d画布 /// private Viewport3D _viewport3D; /// /// 项容器 /// private ModelVisual3D _visualParent; /// /// 显示范围内第一个项的编号 /// private int _firstShowIndex; /// /// 显示范围内最后一个项的编号 /// private int _lastShowIndex; /// /// 在获取新的模板后是否需要更新项 /// private bool _updateItems; private bool _isLoaded; /// /// 跳转编号 /// private int _jumpToIndex = -1; public CoverFlow() { Loaded += (s, e) => { if (_isLoaded) return; _isLoaded = true; if (_updateItems) { UpdateShowRange(); _updateItems = false; } if (_jumpToIndex > 0) { PageIndex = _jumpToIndex; } }; } /// /// 页码 /// public int PageIndex { get => (int) GetValue(PageIndexProperty); internal set => SetValue(PageIndexProperty, value); } /// /// 是否循环 /// public bool Loop { get => (bool)GetValue(LoopProperty); set => SetValue(LoopProperty, value); } public override void OnApplyTemplate() { if (_viewport3D != null) { _viewport3D.Children.Clear(); _itemShowList.Clear(); _viewport3D.MouseLeftButtonDown -= Viewport3D_MouseLeftButtonDown; } base.OnApplyTemplate(); _viewport3D = GetTemplateChild(ElementViewport3D) as Viewport3D; if (_viewport3D != null) { _viewport3D.MouseLeftButtonDown += Viewport3D_MouseLeftButtonDown; } _camera = GetTemplateChild(ElementCamera) as ProjectionCamera; _visualParent = GetTemplateChild(ElementVisualParent) as ModelVisual3D; if (_isLoaded) { UpdateShowRange(); _camera.Position = new Point3D(CoverFlowItem.Interval * PageIndex, _camera.Position.Y, _camera.Position.Z); } } /// /// 批量添加资源 /// /// public void AddRange(List uriList) { foreach (var uri in uriList) { _uriList.Add(_uriList.Count, uri); } _updateItems = true; } /// /// 添加一项资源 /// /// public void Add(string uriString) { _uriList.Add(_uriList.Count, new Uri(uriString)); _updateItems = true; } /// /// 添加一项资源 /// /// public void Add(Uri uri) { _uriList.Add(_uriList.Count, uri); _updateItems = true; } /// /// 跳转 /// public void JumpTo(int index) => _jumpToIndex = index; protected override void OnMouseWheel(MouseWheelEventArgs e) { base.OnMouseWheel(e); if (e.Delta < 0) { var index = PageIndex + 1; PageIndex = index >= _uriList.Count ? (Loop ? 0 : _uriList.Count - 1) : index; } else { var index = PageIndex - 1; PageIndex = index < 0 ? (Loop ? _uriList.Count - 1 : 0) : index; } } /// /// 删除指定位置的项 /// /// private void Remove(int pos) { var item = _itemShowList[pos]; _visualParent.Children.Remove(item); _itemShowList.Remove(pos); } /// /// 移动项到指定的位置 /// /// /// private void Move(int index, bool animated) => _itemShowList[index].Move(PageIndex, animated); private void Viewport3D_MouseLeftButtonDown(object sender, MouseButtonEventArgs e) { var result = (RayMeshGeometry3DHitTestResult) VisualTreeHelper.HitTest(_viewport3D, e.GetPosition(_viewport3D)); if (result != null) { foreach (var item in _itemShowList.Values) { if (item.HitTest(result.MeshHit)) { PageIndex = item.Index; break; } } } } /// /// 更新项的位置 /// /// /// private void UpdateIndex(int newIndex, int oldIndex) { var animate = Math.Abs(newIndex - oldIndex) < MaxShowCountHalf; UpdateShowRange(); if (newIndex > oldIndex) { if (oldIndex < _firstShowIndex) { oldIndex = _firstShowIndex; } for (var i = oldIndex; i <= newIndex; i++) { Move(i, animate); } } else { if (oldIndex > _lastShowIndex) { oldIndex = _lastShowIndex; } for (var i = oldIndex; i >= newIndex; i--) { Move(i, animate); } } _camera.Position = new Point3D(CoverFlowItem.Interval * newIndex, _camera.Position.Y, _camera.Position.Z); } /// /// 更新显示范围 /// private void UpdateShowRange() { var newFirstShowIndex = Math.Max(PageIndex - MaxShowCountHalf, 0); var newLastShowIndex = Math.Min(PageIndex + MaxShowCountHalf, _uriList.Count - 1); if (_firstShowIndex < newFirstShowIndex) { for (var i = _firstShowIndex; i < newFirstShowIndex; i++) { Remove(i); } } else if (newLastShowIndex < _lastShowIndex) { for (var i = newLastShowIndex; i < _lastShowIndex; i++) { Remove(i); } } for (var i = newFirstShowIndex; i <= newLastShowIndex; i++) { if (!_itemShowList.ContainsKey(i)) { var cover = new CoverFlowItem(i, PageIndex, BitmapFrame.Create(_uriList[i])); _itemShowList[i] = cover; _visualParent.Children.Add(cover); } } _firstShowIndex = newFirstShowIndex; _lastShowIndex = newLastShowIndex; } } }