Synchronize the Net40
This commit is contained in:
1 parent
b3c3173c46
commit
ab6e2efe21
87 files changed
+2743
-349
No files matched your search
@@ -0,0 +1,305 @@
|
||||
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
|
||||
{
|
||||
/// <summary>
|
||||
/// 封面流
|
||||
/// </summary>
|
||||
[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";
|
||||
|
||||
/// <summary>
|
||||
/// 最大显示数量的一半
|
||||
/// </summary>
|
||||
private const int MaxShowCountHalf = 7;
|
||||
|
||||
/// <summary>
|
||||
/// 页码
|
||||
/// </summary>
|
||||
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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 是否循环
|
||||
/// </summary>
|
||||
public static readonly DependencyProperty LoopProperty = DependencyProperty.Register(
|
||||
"Loop", typeof(bool), typeof(CoverFlow), new PropertyMetadata(ValueBoxes.FalseBox));
|
||||
|
||||
/// <summary>
|
||||
/// 存储所有的资源标识符
|
||||
/// </summary>
|
||||
private readonly Dictionary<int, Uri> _uriList = new Dictionary<int, Uri>();
|
||||
|
||||
/// <summary>
|
||||
/// 当前在显示范围内的项
|
||||
/// </summary>
|
||||
private readonly Dictionary<int, CoverFlowItem> _itemShowList = new Dictionary<int, CoverFlowItem>();
|
||||
|
||||
/// <summary>
|
||||
/// 相机
|
||||
/// </summary>
|
||||
private ProjectionCamera _camera;
|
||||
|
||||
/// <summary>
|
||||
/// 3d画布
|
||||
/// </summary>
|
||||
private Viewport3D _viewport3D;
|
||||
|
||||
/// <summary>
|
||||
/// 项容器
|
||||
/// </summary>
|
||||
private ModelVisual3D _visualParent;
|
||||
|
||||
/// <summary>
|
||||
/// 显示范围内第一个项的编号
|
||||
/// </summary>
|
||||
private int _firstShowIndex;
|
||||
|
||||
/// <summary>
|
||||
/// 显示范围内最后一个项的编号
|
||||
/// </summary>
|
||||
private int _lastShowIndex;
|
||||
|
||||
/// <summary>
|
||||
/// 跳转编号
|
||||
/// </summary>
|
||||
private int _jumpToIndex = -1;
|
||||
|
||||
/// <summary>
|
||||
/// 页码
|
||||
/// </summary>
|
||||
public int PageIndex
|
||||
{
|
||||
get => (int)GetValue(PageIndexProperty);
|
||||
internal set => SetValue(PageIndexProperty, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 是否循环
|
||||
/// </summary>
|
||||
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;
|
||||
|
||||
UpdateShowRange();
|
||||
if (_jumpToIndex > 0)
|
||||
{
|
||||
PageIndex = _jumpToIndex;
|
||||
_jumpToIndex = -1;
|
||||
}
|
||||
_camera.Position = new Point3D(CoverFlowItem.Interval * PageIndex, _camera.Position.Y, _camera.Position.Z);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 批量添加资源
|
||||
/// </summary>
|
||||
/// <param name="uriList"></param>
|
||||
public void AddRange(List<Uri> uriList)
|
||||
{
|
||||
foreach (var uri in uriList)
|
||||
{
|
||||
_uriList.Add(_uriList.Count, uri);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 添加一项资源
|
||||
/// </summary>
|
||||
/// <param name="uriString"></param>
|
||||
public void Add(string uriString) => _uriList.Add(_uriList.Count, new Uri(uriString));
|
||||
|
||||
/// <summary>
|
||||
/// 添加一项资源
|
||||
/// </summary>
|
||||
/// <param name="uri"></param>
|
||||
public void Add(Uri uri) => _uriList.Add(_uriList.Count, uri);
|
||||
|
||||
/// <summary>
|
||||
/// 跳转
|
||||
/// </summary>
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 删除指定位置的项
|
||||
/// </summary>
|
||||
/// <param name="pos"></param>
|
||||
private void Remove(int pos)
|
||||
{
|
||||
var item = _itemShowList[pos];
|
||||
_visualParent.Children.Remove(item);
|
||||
_itemShowList.Remove(pos);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 移动项到指定的位置
|
||||
/// </summary>
|
||||
/// <param name="index"></param>
|
||||
/// <param name="animated"></param>
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 更新项的位置
|
||||
/// </summary>
|
||||
/// <param name="newIndex"></param>
|
||||
/// <param name="oldIndex"></param>
|
||||
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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 更新显示范围
|
||||
/// </summary>
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,165 @@
|
||||
using System;
|
||||
using System.Windows;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Media3D;
|
||||
using HandyControl.Tools;
|
||||
|
||||
namespace HandyControl.Controls
|
||||
{
|
||||
public class CoverFlowItem : ModelVisual3D
|
||||
{
|
||||
internal static readonly double Interval = .2;
|
||||
|
||||
internal static readonly double AnimationSpeed = 400;
|
||||
|
||||
private readonly AxisAngleRotation3D _rotation3D;
|
||||
|
||||
private readonly TranslateTransform3D _transform3D;
|
||||
|
||||
private readonly Model3DGroup _model3DGroup;
|
||||
|
||||
private readonly ImageSource _source;
|
||||
|
||||
internal int Index { get; set; }
|
||||
|
||||
public CoverFlowItem(int itemIndex, int currentIndex, ImageSource source)
|
||||
{
|
||||
Index = itemIndex;
|
||||
_source = source;
|
||||
|
||||
_rotation3D = new AxisAngleRotation3D(new Vector3D(0, 1, 0), GetAngleByPos(currentIndex));
|
||||
_transform3D = new TranslateTransform3D(GetXByPos(currentIndex), 0, GetZByPos(currentIndex));
|
||||
|
||||
var transformGroup = new Transform3DGroup();
|
||||
transformGroup.Children.Add(new RotateTransform3D(_rotation3D));
|
||||
transformGroup.Children.Add(_transform3D);
|
||||
|
||||
_model3DGroup = new Model3DGroup();
|
||||
_model3DGroup.Children.Add(new GeometryModel3D(CreateItemGeometry(), new DiffuseMaterial(new ImageBrush(_source))));
|
||||
_model3DGroup.Transform = transformGroup;
|
||||
|
||||
Content = _model3DGroup;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建网格形状
|
||||
/// </summary>
|
||||
/// <param name="p0"></param>
|
||||
/// <param name="p1"></param>
|
||||
/// <param name="p2"></param>
|
||||
/// <param name="p3"></param>
|
||||
/// <returns></returns>
|
||||
private static MeshGeometry3D CreateMeshGeometry(Point3D p0, Point3D p1, Point3D p2, Point3D p3)
|
||||
{
|
||||
var positions = new Point3DCollection
|
||||
{
|
||||
p0,
|
||||
p1,
|
||||
p2,
|
||||
p3
|
||||
};
|
||||
|
||||
var triangleIndices = new Int32Collection
|
||||
{
|
||||
0,
|
||||
1,
|
||||
2,
|
||||
2,
|
||||
3,
|
||||
0
|
||||
};
|
||||
|
||||
var textureCoordinates = new PointCollection
|
||||
{
|
||||
new Point(0, 1),
|
||||
new Point(1, 1),
|
||||
new Point(1, 0),
|
||||
new Point(0, 0),
|
||||
new Point(1, 0),
|
||||
new Point(1, 1)
|
||||
};
|
||||
|
||||
var normals = new Vector3DCollection
|
||||
{
|
||||
ArithmeticHelper.CalNormal(p0, p1, p2),
|
||||
ArithmeticHelper.CalNormal(p2, p3, p0)
|
||||
};
|
||||
|
||||
var geometry3D = new MeshGeometry3D
|
||||
{
|
||||
Positions = positions,
|
||||
TriangleIndices = triangleIndices,
|
||||
TextureCoordinates = textureCoordinates,
|
||||
Normals = normals
|
||||
};
|
||||
|
||||
geometry3D.Freeze();
|
||||
return geometry3D;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建内容形状
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
private Geometry3D CreateItemGeometry()
|
||||
{
|
||||
var sx = _source.Width > _source.Height ? 0 : 1 - _source.Width / _source.Height;
|
||||
var sy = _source.Width < _source.Height ? 0 : 1 - _source.Height / _source.Width;
|
||||
|
||||
var p0 = new Point3D(-1 + sx, -1 + sy, 0);
|
||||
var p1 = new Point3D(1 - sx, -1 + sy, 0);
|
||||
var p2 = new Point3D(1 - sx, 1 - sy, 0);
|
||||
var p3 = new Point3D(-1 + sx, 1 - sy, 0);
|
||||
|
||||
return CreateMeshGeometry(p0, p1, p2, p3);
|
||||
}
|
||||
|
||||
private double GetAngleByPos(int index) => Math.Sign(Index - index) * -90;
|
||||
|
||||
private double GetXByPos(int index) => Index * Interval + Math.Sign(Index - index) * 1.5;
|
||||
|
||||
private double GetZByPos(int index) => Index == index ? 1 : 0;
|
||||
|
||||
/// <summary>
|
||||
/// 移动
|
||||
/// </summary>
|
||||
internal void Move(int currentIndex, bool animated)
|
||||
{
|
||||
if (animated || _rotation3D.HasAnimatedProperties)
|
||||
{
|
||||
_rotation3D.BeginAnimation(AxisAngleRotation3D.AngleProperty,
|
||||
AnimationHelper.CreateAnimation(GetAngleByPos(currentIndex), AnimationSpeed));
|
||||
_transform3D.BeginAnimation(TranslateTransform3D.OffsetXProperty,
|
||||
AnimationHelper.CreateAnimation(GetXByPos(currentIndex), AnimationSpeed));
|
||||
_transform3D.BeginAnimation(TranslateTransform3D.OffsetZProperty,
|
||||
AnimationHelper.CreateAnimation(GetZByPos(currentIndex), AnimationSpeed));
|
||||
}
|
||||
else
|
||||
{
|
||||
_rotation3D.Angle = GetAngleByPos(currentIndex);
|
||||
_transform3D.OffsetX = GetXByPos(currentIndex);
|
||||
_transform3D.OffsetZ = GetZByPos(currentIndex);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 命中测试
|
||||
/// </summary>
|
||||
/// <param name="mesh"></param>
|
||||
/// <returns></returns>
|
||||
internal bool HitTest(MeshGeometry3D mesh)
|
||||
{
|
||||
foreach (var item in _model3DGroup.Children)
|
||||
{
|
||||
if (item is GeometryModel3D geometryModel3D)
|
||||
{
|
||||
if (Equals(geometryModel3D.Geometry, mesh))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,216 @@
|
||||
using System.Windows;
|
||||
using System.Windows.Data;
|
||||
using HandyControl.Data;
|
||||
using HandyControl.Tools;
|
||||
|
||||
namespace HandyControl.Controls
|
||||
{
|
||||
public class CoverView : RegularItemsControl
|
||||
{
|
||||
private readonly CoverViewContent _viewContent;
|
||||
|
||||
private CoverViewItem _selectedItem;
|
||||
|
||||
public CoverView()
|
||||
{
|
||||
_viewContent = new CoverViewContent();
|
||||
|
||||
AddHandler(SelectableItem.SelectedEvent, new RoutedEventHandler(CoverViewItem_OnSelected));
|
||||
_viewContent.SetBinding(CoverViewContent.ContentHeightProperty, new Binding(ItemContentHeightProperty.Name) { Source = this });
|
||||
_viewContent.SetBinding(WidthProperty, new Binding(ActualWidthProperty.Name) { Source = this });
|
||||
}
|
||||
|
||||
private void CoverViewItem_OnSelected(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (e.OriginalSource is CoverViewItem item)
|
||||
{
|
||||
if (_selectedItem == null)
|
||||
{
|
||||
item.IsSelected = true;
|
||||
_selectedItem = item;
|
||||
if (_viewContent != null)
|
||||
{
|
||||
_viewContent.Content = item.Content;
|
||||
UpdateCoverViewContent(true);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Equals(_selectedItem, item))
|
||||
{
|
||||
_selectedItem.IsSelected = false;
|
||||
item.IsSelected = true;
|
||||
_selectedItem = item;
|
||||
if (_viewContent != null)
|
||||
{
|
||||
_viewContent.Content = item.Content;
|
||||
UpdateCoverViewContent(true);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (_viewContent != null)
|
||||
{
|
||||
_viewContent.Content = null;
|
||||
UpdateCoverViewContent(false);
|
||||
}
|
||||
_selectedItem.IsSelected = false;
|
||||
_selectedItem = null;
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnApplyTemplate()
|
||||
{
|
||||
base.OnApplyTemplate();
|
||||
|
||||
if (_viewContent.Style == null)
|
||||
{
|
||||
_viewContent.Style = CoverViewContentStyle;
|
||||
}
|
||||
|
||||
if (_selectedItem != null)
|
||||
{
|
||||
UpdateCoverViewContent(_selectedItem != null);
|
||||
}
|
||||
}
|
||||
|
||||
protected override DependencyObject GetContainerForItemOverride() => new CoverViewItem();
|
||||
|
||||
protected override bool IsItemItsOwnContainerOverride(object item) => item is CoverViewItem;
|
||||
|
||||
public static readonly DependencyProperty CoverViewContentStyleProperty = DependencyProperty.Register(
|
||||
"CoverViewContentStyle", typeof(Style), typeof(CoverView), new PropertyMetadata(default(Style)));
|
||||
|
||||
public Style CoverViewContentStyle
|
||||
{
|
||||
get => (Style)GetValue(CoverViewContentStyleProperty);
|
||||
set => SetValue(CoverViewContentStyleProperty, value);
|
||||
}
|
||||
|
||||
internal static readonly DependencyProperty GroupsProperty = DependencyProperty.Register(
|
||||
"Groups", typeof(int), typeof(CoverView),
|
||||
new FrameworkPropertyMetadata(ValueBoxes.Int5Box, FrameworkPropertyMetadataOptions.AffectsMeasure,
|
||||
OnGroupsChanged), IsGroupsValid);
|
||||
|
||||
internal static void OnGroupsChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
if (d is CoverView ctl)
|
||||
{
|
||||
ctl.UpdateCoverViewContent(ctl._viewContent.IsOpen);
|
||||
}
|
||||
}
|
||||
|
||||
private static bool IsGroupsValid(object value)
|
||||
{
|
||||
var v = (int)value;
|
||||
return v >= 1;
|
||||
}
|
||||
|
||||
public int Groups
|
||||
{
|
||||
get => (int)GetValue(GroupsProperty);
|
||||
set => SetValue(GroupsProperty, value);
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty ItemContentHeightProperty = DependencyProperty.Register(
|
||||
"ItemContentHeight", typeof(double), typeof(CoverView), new PropertyMetadata(ValueBoxes.Double300Box),
|
||||
value => ValidateHelper.IsInRangeOfPosDouble(value, true));
|
||||
|
||||
public double ItemContentHeight
|
||||
{
|
||||
get => (double)GetValue(ItemContentHeightProperty);
|
||||
set => SetValue(ItemContentHeightProperty, value);
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty ItemContentHeightFixedProperty = DependencyProperty.Register(
|
||||
"ItemContentHeightFixed", typeof(bool), typeof(CoverView), new PropertyMetadata(ValueBoxes.TrueBox));
|
||||
|
||||
public bool ItemContentHeightFixed
|
||||
{
|
||||
get => (bool)GetValue(ItemContentHeightFixedProperty);
|
||||
set => SetValue(ItemContentHeightFixedProperty, value);
|
||||
}
|
||||
|
||||
protected override void OnRenderSizeChanged(SizeChangedInfo sizeInfo)
|
||||
{
|
||||
base.OnRenderSizeChanged(sizeInfo);
|
||||
|
||||
Groups = (int)(sizeInfo.NewSize.Width / (ItemWidth + ItemMargin.Left + ItemMargin.Right));
|
||||
}
|
||||
|
||||
protected override void Refresh()
|
||||
{
|
||||
if (ItemsHost == null) return;
|
||||
|
||||
ItemsHost.Children.Clear();
|
||||
var index = 0;
|
||||
foreach (var item in Items)
|
||||
{
|
||||
DependencyObject container;
|
||||
if (IsItemItsOwnContainerOverride(item))
|
||||
{
|
||||
container = item as DependencyObject;
|
||||
}
|
||||
else
|
||||
{
|
||||
container = GetContainerForItemOverride();
|
||||
PrepareContainerForItemOverride(container, item);
|
||||
}
|
||||
|
||||
if (container is CoverViewItem element)
|
||||
{
|
||||
element.Index = index++;
|
||||
element.Style = ItemContainerStyle;
|
||||
ItemsHost.Children.Add(element);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 更新内容视图
|
||||
/// </summary>
|
||||
private void UpdateCoverViewContent(bool isOpen)
|
||||
{
|
||||
if (_selectedItem == null) return;
|
||||
|
||||
ItemsHost.Children.Remove(_viewContent);
|
||||
|
||||
if (!ItemContentHeightFixed)
|
||||
{
|
||||
_viewContent.ManualHeight = 0;
|
||||
if (_viewContent.Content is FrameworkElement element)
|
||||
{
|
||||
element.Arrange(new Rect(new Size(double.MaxValue, double.MaxValue)));
|
||||
_viewContent.ManualHeight = element.ActualHeight;
|
||||
}
|
||||
}
|
||||
|
||||
var total = Items.Count + 1;
|
||||
var totalRow = total / Groups + (total % Groups > 0 ? 1 : 0);
|
||||
if (total <= Groups)
|
||||
{
|
||||
ItemsHost.Children.Add(_viewContent);
|
||||
}
|
||||
else
|
||||
{
|
||||
var row = _selectedItem.Index / Groups + 1;
|
||||
if (row == totalRow)
|
||||
{
|
||||
ItemsHost.Children.Add(_viewContent);
|
||||
}
|
||||
else
|
||||
{
|
||||
ItemsHost.Children.Insert(row * Groups, _viewContent);
|
||||
}
|
||||
}
|
||||
|
||||
_viewContent.UpdatePosition(_selectedItem.Index, Groups, _selectedItem.DesiredSize.Width);
|
||||
if (_viewContent.CanSwitch)
|
||||
{
|
||||
_viewContent.IsOpen = isOpen;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using HandyControl.Data;
|
||||
using HandyControl.Expression.Drawing;
|
||||
using HandyControl.Tools;
|
||||
using HandyControl.Tools.Extension;
|
||||
|
||||
namespace HandyControl.Controls
|
||||
{
|
||||
[TemplatePart(Name = ElementTriangle, Type = typeof(FrameworkElement))]
|
||||
[TemplatePart(Name = ElementContent, Type = typeof(FrameworkElement))]
|
||||
internal class CoverViewContent : ContentControl
|
||||
{
|
||||
private const string ElementTriangle = "PART_Triangle";
|
||||
|
||||
private const string ElementContent = "PART_Content";
|
||||
|
||||
private FrameworkElement _triangle;
|
||||
|
||||
private FrameworkElement _content;
|
||||
|
||||
internal bool WaitForUpdate { get; set; }
|
||||
|
||||
private int _index;
|
||||
|
||||
private int _groups;
|
||||
|
||||
private double _itemWidth;
|
||||
|
||||
internal bool CanSwitch { get; set; } = true;
|
||||
|
||||
private bool _isOpen;
|
||||
|
||||
internal bool IsOpen
|
||||
{
|
||||
get => _isOpen;
|
||||
set
|
||||
{
|
||||
if (_isOpen == value) return;
|
||||
_isOpen = value;
|
||||
OpenSwitch(value);
|
||||
}
|
||||
}
|
||||
|
||||
internal static readonly DependencyProperty ManualHeightProperty = DependencyProperty.Register(
|
||||
"ManualHeight", typeof(double), typeof(CoverViewContent), new PropertyMetadata(default(double)), value => ValidateHelper.IsInRangeOfPosDouble(value, true));
|
||||
|
||||
internal double ManualHeight
|
||||
{
|
||||
get => (double)GetValue(ManualHeightProperty);
|
||||
set => SetValue(ManualHeightProperty, value);
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty ContentHeightProperty = DependencyProperty.Register(
|
||||
"ContentHeight", typeof(double), typeof(CoverViewContent), new PropertyMetadata(ValueBoxes.Double300Box),
|
||||
value => ValidateHelper.IsInRangeOfPosDouble(value, true));
|
||||
|
||||
public double ContentHeight
|
||||
{
|
||||
get => (double)GetValue(ContentHeightProperty);
|
||||
set => SetValue(ContentHeightProperty, value);
|
||||
}
|
||||
|
||||
public override void OnApplyTemplate()
|
||||
{
|
||||
base.OnApplyTemplate();
|
||||
|
||||
_triangle = GetTemplateChild(ElementTriangle) as FrameworkElement;
|
||||
_content = GetTemplateChild(ElementContent) as FrameworkElement;
|
||||
|
||||
if (WaitForUpdate)
|
||||
{
|
||||
_triangle.BeginAnimation(MarginProperty, AnimationHelper.CreateAnimation(new Thickness((_index % _groups + .5) * _itemWidth - _triangle.Width / 2, 0, 0, 0)));
|
||||
OpenSwitch(_isOpen);
|
||||
WaitForUpdate = false;
|
||||
}
|
||||
}
|
||||
|
||||
internal void UpdatePosition(int index, int groups, double itemWidth)
|
||||
{
|
||||
if (_triangle == null)
|
||||
{
|
||||
_index = index;
|
||||
_groups = groups;
|
||||
_itemWidth = itemWidth;
|
||||
WaitForUpdate = true;
|
||||
return;
|
||||
}
|
||||
_triangle.BeginAnimation(MarginProperty, AnimationHelper.CreateAnimation(new Thickness((index % groups + .5) * itemWidth - _triangle.Width / 2, 0, 0, 0)));
|
||||
if (IsOpen)
|
||||
{
|
||||
if (ManualHeight > 0 && !MathHelper.AreClose(ManualHeight, ContentHeight))
|
||||
{
|
||||
_content.BeginAnimation(HeightProperty, AnimationHelper.CreateAnimation(ManualHeight));
|
||||
}
|
||||
else
|
||||
{
|
||||
_content.BeginAnimation(HeightProperty, AnimationHelper.CreateAnimation(ContentHeight));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void OpenSwitch(bool isOpen)
|
||||
{
|
||||
if (_content == null) return;
|
||||
var animation = AnimationHelper.CreateAnimation(isOpen ? ManualHeight > 0 ? ManualHeight : ContentHeight : 0);
|
||||
_triangle.Show(false);
|
||||
this.Show(true);
|
||||
animation.Completed += (s, e) =>
|
||||
{
|
||||
CanSwitch = true;
|
||||
this.Show(IsOpen);
|
||||
if (IsOpen)
|
||||
{
|
||||
_triangle.Show(true);
|
||||
}
|
||||
};
|
||||
CanSwitch = false;
|
||||
_content.BeginAnimation(HeightProperty, animation);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
namespace HandyControl.Controls
|
||||
{
|
||||
public class CoverViewItem : HeaderedSelectableItem
|
||||
{
|
||||
internal int Index { get; set; }
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user