support for .NET framework 3.5

Still have bugs
This commit is contained in:
NaBian committed 2019-01-21 00:43:10 +08:00
1 parent 5878039e21
commit 7d6b491f25
704 files changed
+48273 -33

No files matched your search

@@ -0,0 +1,22 @@
using System.Windows;
using System.Windows.Media;
namespace HandyControl.Controls
{
public class BackgroundSwitchElement
{
public static readonly DependencyProperty MouseHoverBackgroundProperty = DependencyProperty.RegisterAttached(
"MouseHoverBackground", typeof(Brush), typeof(BackgroundSwitchElement), new FrameworkPropertyMetadata(Brushes.Transparent, FrameworkPropertyMetadataOptions.Inherits));
public static void SetMouseHoverBackground(DependencyObject element, Brush value) => element.SetValue(MouseHoverBackgroundProperty, value);
public static Brush GetMouseHoverBackground(DependencyObject element) => (Brush)element.GetValue(MouseHoverBackgroundProperty);
public static readonly DependencyProperty MouseDownBackgroundProperty = DependencyProperty.RegisterAttached(
"MouseDownBackground", typeof(Brush), typeof(BackgroundSwitchElement), new FrameworkPropertyMetadata(Brushes.Transparent, FrameworkPropertyMetadataOptions.Inherits));
public static void SetMouseDownBackground(DependencyObject element, Brush value) => element.SetValue(MouseDownBackgroundProperty, value);
public static Brush GetMouseDownBackground(DependencyObject element) => (Brush)element.GetValue(MouseDownBackgroundProperty);
}
}
@@ -0,0 +1,14 @@
using System.Windows;
namespace HandyControl.Controls
{
public class BorderElement
{
public static readonly DependencyProperty CornerRadiusProperty = DependencyProperty.RegisterAttached(
"CornerRadius", typeof(CornerRadius), typeof(BorderElement), new FrameworkPropertyMetadata(default(CornerRadius), FrameworkPropertyMetadataOptions.Inherits));
public static void SetCornerRadius(DependencyObject element, CornerRadius value) => element.SetValue(CornerRadiusProperty, value);
public static CornerRadius GetCornerRadius(DependencyObject element) => (CornerRadius)element.GetValue(CornerRadiusProperty);
}
}
@@ -0,0 +1,47 @@
using System.Windows;
using HandyControl.Data;
namespace HandyControl.Controls
{
public class EdgeElement
{
public static readonly DependencyProperty LeftContentProperty = DependencyProperty.RegisterAttached(
"LeftContent", typeof(object), typeof(EdgeElement), new PropertyMetadata(default(object), OnEdgeContentChanged));
public static void SetLeftContent(DependencyObject element, object value) => element.SetValue(LeftContentProperty, value);
public static object GetLeftContent(DependencyObject element) => element.GetValue(LeftContentProperty);
public static readonly DependencyProperty TopContentProperty = DependencyProperty.RegisterAttached(
"TopContent", typeof(object), typeof(EdgeElement), new PropertyMetadata(default(object), OnEdgeContentChanged));
public static void SetTopContent(DependencyObject element, object value) => element.SetValue(TopContentProperty, value);
public static object GetTopContent(DependencyObject element) => element.GetValue(TopContentProperty);
public static readonly DependencyProperty RightContentProperty = DependencyProperty.RegisterAttached(
"RightContent", typeof(object), typeof(EdgeElement), new PropertyMetadata(default(object), OnEdgeContentChanged));
public static void SetRightContent(DependencyObject element, object value) => element.SetValue(RightContentProperty, value);
public static object GetRightContent(DependencyObject element) => element.GetValue(RightContentProperty);
public static readonly DependencyProperty BottomContentProperty = DependencyProperty.RegisterAttached(
"BottomContent", typeof(object), typeof(EdgeElement), new PropertyMetadata(default(object), OnEdgeContentChanged));
public static void SetBottomContent(DependencyObject element, object value) => element.SetValue(BottomContentProperty, value);
public static object GetBottomContent(DependencyObject element) => element.GetValue(BottomContentProperty);
public static readonly DependencyProperty ShowEdgeContentProperty = DependencyProperty.RegisterAttached(
"ShowEdgeContent", typeof(bool), typeof(EdgeElement), new PropertyMetadata(ValueBoxes.FalseBox));
public static void SetShowEdgeContent(DependencyObject element, bool value) => element.SetValue(ShowEdgeContentProperty, value);
public static bool GetShowEdgeContent(DependencyObject element) => (bool) element.GetValue(ShowEdgeContentProperty);
private static void OnEdgeContentChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) =>
SetShowEdgeContent(d, !(GetLeftContent(d) == null && GetTopContent(d) == null &&
GetRightContent(d) == null && GetBottomContent(d) == null));
}
}
@@ -0,0 +1,35 @@
using System.Windows;
using System.Windows.Media;
namespace HandyControl.Controls
{
public class IconElement
{
public static readonly DependencyProperty GeometryProperty = DependencyProperty.RegisterAttached(
"Geometry", typeof(Geometry), typeof(IconElement), new PropertyMetadata(default(Geometry)));
public static void SetGeometry(DependencyObject element, Geometry value)
=> element.SetValue(GeometryProperty, value);
public static Geometry GetGeometry(DependencyObject element)
=> (Geometry) element.GetValue(GeometryProperty);
public static readonly DependencyProperty WidthProperty = DependencyProperty.RegisterAttached(
"Width", typeof(double), typeof(IconElement), new PropertyMetadata(double.NaN));
public static void SetWidth(DependencyObject element, double value)
=> element.SetValue(WidthProperty, value);
public static double GetWidth(DependencyObject element)
=> (double) element.GetValue(WidthProperty);
public static readonly DependencyProperty HeightProperty = DependencyProperty.RegisterAttached(
"Height", typeof(double), typeof(IconElement), new PropertyMetadata(double.NaN));
public static void SetHeight(DependencyObject element, double value)
=> element.SetValue(HeightProperty, value);
public static double GetHeight(DependencyObject element)
=> (double) element.GetValue(HeightProperty);
}
}
@@ -0,0 +1,21 @@
using System.Windows;
using System.Windows.Media;
namespace HandyControl.Controls
{
public class IconSwitchElement : IconElement
{
public static readonly DependencyProperty GeometrySelectedProperty = DependencyProperty.RegisterAttached(
"GeometrySelected", typeof(Geometry), typeof(IconSwitchElement), new PropertyMetadata(default(Geometry)));
public static void SetGeometrySelected(DependencyObject element, Geometry value)
{
element.SetValue(GeometrySelectedProperty, value);
}
public static Geometry GetGeometrySelected(DependencyObject element)
{
return (Geometry)element.GetValue(GeometrySelectedProperty);
}
}
}
@@ -0,0 +1,48 @@
using System.Windows;
using HandyControl.Data;
namespace HandyControl.Controls
{
public class InfoElement : TitleElement
{
/// <summary>
/// 占位符
/// </summary>
public static readonly DependencyProperty PlaceholderProperty = DependencyProperty.RegisterAttached(
"Placeholder", typeof(string), typeof(InfoElement), new FrameworkPropertyMetadata(default(string), FrameworkPropertyMetadataOptions.Inherits));
public static void SetPlaceholder(DependencyObject element, string value) => element.SetValue(PlaceholderProperty, value);
public static string GetPlaceholder(DependencyObject element) => (string)element.GetValue(PlaceholderProperty);
/// <summary>
/// 是否必填
/// </summary>
public static readonly DependencyProperty NecessaryProperty = DependencyProperty.RegisterAttached(
"Necessary", typeof(bool), typeof(InfoElement), new FrameworkPropertyMetadata(ValueBoxes.FalseBox, FrameworkPropertyMetadataOptions.Inherits));
public static void SetNecessary(DependencyObject element, bool value) => element.SetValue(NecessaryProperty, value);
public static bool GetNecessary(DependencyObject element) => (bool)element.GetValue(NecessaryProperty);
/// <summary>
/// 标记
/// </summary>
public static readonly DependencyProperty SymbolProperty = DependencyProperty.RegisterAttached(
"Symbol", typeof(string), typeof(InfoElement), new FrameworkPropertyMetadata(default(string), FrameworkPropertyMetadataOptions.Inherits));
public static void SetSymbol(DependencyObject element, string value) => element.SetValue(SymbolProperty, value);
public static string GetSymbol(DependencyObject element) => (string)element.GetValue(SymbolProperty);
/// <summary>
/// 内容高度
/// </summary>
public static readonly DependencyProperty ContentHeightProperty = DependencyProperty.RegisterAttached(
"ContentHeight", typeof(double), typeof(InfoElement), new FrameworkPropertyMetadata(30.0, FrameworkPropertyMetadataOptions.Inherits));
public static void SetContentHeight(DependencyObject element, double value) => element.SetValue(ContentHeightProperty, value);
public static double GetContentHeight(DependencyObject element) => (double)element.GetValue(ContentHeightProperty);
}
}
@@ -0,0 +1,47 @@
using System.Diagnostics;
using System.Windows;
using System.Windows.Input;
using HandyControl.Interactivity;
namespace HandyControl.Controls
{
public class LinkElement
{
public static readonly DependencyProperty LinkProperty = DependencyProperty.RegisterAttached(
"Link", typeof(string), typeof(LinkElement), new PropertyMetadata(default(string), OnLinkChanged));
private static void OnLinkChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is UIElement element)
{
CreateTrriger(element, (string) e.NewValue);
}
}
private static readonly DependencyProperty CommandBindingProperty = DependencyProperty.RegisterAttached(
"CommandBinding", typeof(CommandBinding), typeof(LinkElement), new PropertyMetadata(default(CommandBinding)));
private static void SetCommandBinding(DependencyObject element, CommandBinding value)
=> element.SetValue(CommandBindingProperty, value);
private static CommandBinding GetCommandBinding(DependencyObject element)
=> (CommandBinding) element.GetValue(CommandBindingProperty);
private static void CreateTrriger(UIElement element, string link)
{
element.CommandBindings.Remove(GetCommandBinding(element));
SetCommandBinding(element, null);
if (string.IsNullOrEmpty(link)) return;
var commandBinding = new CommandBinding(ControlCommands.OpenLink, (sender, args) => Process.Start(link));
SetCommandBinding(element, commandBinding);
element.CommandBindings.Add(commandBinding);
}
public static void SetLink(DependencyObject element, string value)
=> element.SetValue(LinkProperty, value);
public static string GetLink(DependencyObject element)
=> (string) element.GetValue(LinkProperty);
}
}
@@ -0,0 +1,43 @@
using System.Windows;
using System.Windows.Controls;
using HandyControl.Interactivity;
namespace HandyControl.Controls
{
public class PanelElement
{
public static readonly DependencyProperty FluidMoveBehaviorProperty = DependencyProperty.RegisterAttached(
"FluidMoveBehavior", typeof(FluidMoveBehavior), typeof(PanelElement), new PropertyMetadata(default(FluidMoveBehavior), OnFluidMoveBehaviorChanged));
private static void OnFluidMoveBehaviorChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is Panel panel)
{
var collection = Interaction.GetBehaviors(panel);
var oldBehavior = GetTempFluidMoveBehavior(panel);
collection.Remove(oldBehavior);
if (e.NewValue is FluidMoveBehavior behavior)
{
collection.Add(behavior);
SetTempFluidMoveBehavior(panel, behavior);
}
}
}
public static void SetFluidMoveBehavior(DependencyObject element, FluidMoveBehavior value)
=> element.SetValue(FluidMoveBehaviorProperty, value);
public static FluidMoveBehavior GetFluidMoveBehavior(DependencyObject element)
=> (FluidMoveBehavior) element.GetValue(FluidMoveBehaviorProperty);
private static readonly DependencyProperty TempFluidMoveBehaviorProperty = DependencyProperty.RegisterAttached(
"TempFluidMoveBehavior", typeof(FluidMoveBehavior), typeof(PanelElement), new PropertyMetadata(default(FluidMoveBehavior)));
private static void SetTempFluidMoveBehavior(DependencyObject element, FluidMoveBehavior value)
=> element.SetValue(TempFluidMoveBehaviorProperty, value);
private static FluidMoveBehavior GetTempFluidMoveBehavior(DependencyObject element)
=> (FluidMoveBehavior) element.GetValue(TempFluidMoveBehaviorProperty);
}
}
@@ -0,0 +1,54 @@
using System.Windows;
using HandyControl.Data;
namespace HandyControl.Controls
{
public class PasswordBoxMonitor
{
/// <summary>
/// 密码长度
/// </summary>
public static readonly DependencyProperty PasswordLengthProperty = DependencyProperty.RegisterAttached(
"PasswordLength", typeof(int), typeof(PasswordBoxMonitor), new PropertyMetadata(default(int)));
public static void SetPasswordLength(DependencyObject element, int value) => element.SetValue(PasswordLengthProperty, value);
public static int GetPasswordLength(DependencyObject element) => (int)element.GetValue(PasswordLengthProperty);
/// <summary>
/// 是否监测
/// </summary>
public static readonly DependencyProperty IsMonitoringProperty = DependencyProperty.RegisterAttached(
"IsMonitoring", typeof(bool), typeof(PasswordBoxMonitor), new FrameworkPropertyMetadata(ValueBoxes.FalseBox, FrameworkPropertyMetadataOptions.Inherits, OnIsMonitoringChanged));
private static void OnIsMonitoringChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is System.Windows.Controls.PasswordBox passwordBox)
{
if (e.NewValue is bool boolValue)
{
if (boolValue)
{
passwordBox.PasswordChanged += PasswordChanged;
}
else
{
passwordBox.PasswordChanged -= PasswordChanged;
}
}
}
}
public static void SetIsMonitoring(DependencyObject element, bool value) => element.SetValue(IsMonitoringProperty, value);
public static bool GetIsMonitoring(DependencyObject element) => (bool)element.GetValue(IsMonitoringProperty);
private static void PasswordChanged(object sender, RoutedEventArgs e)
{
if (sender is System.Windows.Controls.PasswordBox passwordBox)
{
SetPasswordLength(passwordBox, passwordBox.Password.Length);
}
}
}
}
@@ -0,0 +1,28 @@
using System.Windows;
using HandyControl.Data;
namespace HandyControl.Controls
{
public class StatusSwitchElement
{
/// <summary>
/// 选中时展示的元素
/// </summary>
public static readonly DependencyProperty CheckedElementProperty = DependencyProperty.RegisterAttached(
"CheckedElement", typeof(object), typeof(StatusSwitchElement), new PropertyMetadata(default(object)));
public static void SetCheckedElement(DependencyObject element, object value) => element.SetValue(CheckedElementProperty, value);
public static object GetCheckedElement(DependencyObject element) => element.GetValue(CheckedElementProperty);
/// <summary>
/// 是否隐藏元素
/// </summary>
public static readonly DependencyProperty HiddenElementProperty = DependencyProperty.RegisterAttached(
"HiddenElement", typeof(bool), typeof(StatusSwitchElement), new PropertyMetadata(ValueBoxes.FalseBox));
public static void SetHiddenElement(DependencyObject element, bool value) => element.SetValue(HiddenElementProperty, value);
public static bool GetHiddenElement(DependencyObject element) => (bool) element.GetValue(HiddenElementProperty);
}
}
@@ -0,0 +1,22 @@
using System.Windows;
using HandyControl.Data;
namespace HandyControl.Controls
{
public class TipElement
{
public static readonly DependencyProperty VisibilityProperty = DependencyProperty.RegisterAttached(
"Visibility", typeof(Visibility), typeof(TipElement), new PropertyMetadata(Visibility.Collapsed));
public static void SetVisibility(DependencyObject element, Visibility value) => element.SetValue(VisibilityProperty, value);
public static Visibility GetVisibility(DependencyObject element) => (Visibility) element.GetValue(VisibilityProperty);
public static readonly DependencyProperty PlacementProperty = DependencyProperty.RegisterAttached(
"Placement", typeof(TipPlacement), typeof(TipElement), new PropertyMetadata(default(TipPlacement)));
public static void SetPlacement(DependencyObject element, TipPlacement value) => element.SetValue(PlacementProperty, value);
public static TipPlacement GetPlacement(DependencyObject element) => (TipPlacement) element.GetValue(PlacementProperty);
}
}
@@ -0,0 +1,67 @@
using System.Windows;
using System.Windows.Media;
using HandyControl.Data;
namespace HandyControl.Controls
{
public class TitleElement
{
public static readonly DependencyProperty TitleProperty = DependencyProperty.RegisterAttached(
"Title", typeof(string), typeof(TitleElement), new PropertyMetadata(default(string)));
public static void SetTitle(DependencyObject element, string value)
=> element.SetValue(TitleProperty, value);
public static string GetTitle(DependencyObject element)
=> (string) element.GetValue(TitleProperty);
public static readonly DependencyProperty BackgroundProperty = DependencyProperty.RegisterAttached(
"Background", typeof(Brush), typeof(TitleElement), new FrameworkPropertyMetadata(default(Brush), FrameworkPropertyMetadataOptions.Inherits));
public static void SetBackground(DependencyObject element, Brush value)
=> element.SetValue(BackgroundProperty, value);
public static Brush GetBackground(DependencyObject element)
=> (Brush) element.GetValue(BackgroundProperty);
public static readonly DependencyProperty ForegroundProperty = DependencyProperty.RegisterAttached(
"Foreground", typeof(Brush), typeof(TitleElement), new FrameworkPropertyMetadata(default(Brush), FrameworkPropertyMetadataOptions.Inherits));
public static void SetForeground(DependencyObject element, Brush value)
=> element.SetValue(ForegroundProperty, value);
public static Brush GetForeground(DependencyObject element)
=> (Brush) element.GetValue(ForegroundProperty);
public static readonly DependencyProperty BorderBrushProperty = DependencyProperty.RegisterAttached(
"BorderBrush", typeof(Brush), typeof(TitleElement), new FrameworkPropertyMetadata(default(Brush), FrameworkPropertyMetadataOptions.Inherits));
public static void SetBorderBrush(DependencyObject element, Brush value)
=> element.SetValue(BorderBrushProperty, value);
public static Brush GetBorderBrush(DependencyObject element)
=> (Brush) element.GetValue(BorderBrushProperty);
/// <summary>
/// 标题对齐方式
/// </summary>
public static readonly DependencyProperty TitleAlignmentProperty = DependencyProperty.RegisterAttached(
"TitleAlignment", typeof(TitleAlignment), typeof(TitleElement), new FrameworkPropertyMetadata(TitleAlignment.Top, FrameworkPropertyMetadataOptions.Inherits));
public static void SetTitleAlignment(DependencyObject element, TitleAlignment value)
=> element.SetValue(TitleAlignmentProperty, value);
public static TitleAlignment GetTitleAlignment(DependencyObject element)
=> (TitleAlignment) element.GetValue(TitleAlignmentProperty);
/// <summary>
/// 标题宽度
/// </summary>
public static readonly DependencyProperty TitleWidthProperty = DependencyProperty.RegisterAttached(
"TitleWidth", typeof(GridLength), typeof(TitleElement), new FrameworkPropertyMetadata(new GridLength(120.0), FrameworkPropertyMetadataOptions.Inherits));
public static void SetTitleWidth(DependencyObject element, GridLength value) => element.SetValue(TitleWidthProperty, value);
public static GridLength GetTitleWidth(DependencyObject element) => (GridLength)element.GetValue(TitleWidthProperty);
}
}
@@ -0,0 +1,17 @@
using System.Windows;
using System.Windows.Media;
namespace HandyControl.Controls
{
public class VisualElement
{
public static readonly DependencyProperty HighlightBrushProperty = DependencyProperty.RegisterAttached(
"HighlightBrush", typeof(Brush), typeof(VisualElement), new PropertyMetadata(default(Brush)));
public static void SetHighlightBrush(DependencyObject element, Brush value)
=> element.SetValue(HighlightBrushProperty, value);
public static Brush GetHighlightBrush(DependencyObject element)
=> (Brush) element.GetValue(HighlightBrushProperty);
}
}
@@ -0,0 +1,41 @@
using System.Windows;
using System.Windows.Input;
using HandyControl.Data;
namespace HandyControl.Controls
{
public static class WindowAttach
{
public static readonly DependencyProperty IsDragElementProperty = DependencyProperty.RegisterAttached(
"IsDragElement", typeof(bool), typeof(WindowAttach), new PropertyMetadata(ValueBoxes.FalseBox, OnIsDragElementChanged));
public static void SetIsDragElement(DependencyObject element, bool value)
=> element.SetValue(IsDragElementProperty, value);
public static bool GetIsDragElement(DependencyObject element)
=> (bool) element.GetValue(IsDragElementProperty);
private static void OnIsDragElementChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is UIElement ctl)
{
if ((bool)e.NewValue)
{
ctl.MouseLeftButtonDown += DragElement_MouseLeftButtonDown;
}
else
{
ctl.MouseLeftButtonDown -= DragElement_MouseLeftButtonDown;
}
}
}
private static void DragElement_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
if (sender is DependencyObject obj && e.ButtonState == MouseButtonState.Pressed)
{
System.Windows.Window.GetWindow(obj)?.DragMove();
}
}
}
}
@@ -0,0 +1,16 @@
using System.Windows;
namespace HandyControl.Controls
{
public class HeaderedSelectableItem : SelectableItem
{
public static readonly DependencyProperty HeaderProperty = DependencyProperty.Register(
"Header", typeof(object), typeof(HeaderedSelectableItem), new PropertyMetadata(default(object)));
public object Header
{
get => GetValue(HeaderProperty);
set => SetValue(HeaderProperty, value);
}
}
}
@@ -0,0 +1,11 @@
using System.Windows;
namespace HandyControl.Controls
{
public interface ISelectable
{
event RoutedEventHandler Selected;
bool IsSelected { get; set; }
}
}
@@ -0,0 +1,9 @@
using System;
namespace HandyControl.Controls
{
public interface ISingleOpen : IDisposable
{
bool CanDispose { get; }
}
}
@@ -0,0 +1,41 @@
using System.Windows;
using HandyControl.Data;
namespace HandyControl.Controls
{
/// <summary>
/// 规则ItemsControl
/// </summary>
/// <remarks>
/// 该类的每一项都具有相同的大小和外边距
/// </remarks>
public class RegularItemsControl : SimpleItemsControl
{
public static readonly DependencyProperty ItemWidthProperty = DependencyProperty.Register(
"ItemWidth", typeof(double), typeof(RegularItemsControl), new PropertyMetadata(ValueBoxes.Double200Box));
public double ItemWidth
{
get => (double) GetValue(ItemWidthProperty);
set => SetValue(ItemWidthProperty, value);
}
public static readonly DependencyProperty ItemHeightProperty = DependencyProperty.Register(
"ItemHeight", typeof(double), typeof(RegularItemsControl), new PropertyMetadata(ValueBoxes.Double200Box));
public double ItemHeight
{
get => (double) GetValue(ItemHeightProperty);
set => SetValue(ItemHeightProperty, value);
}
public static readonly DependencyProperty ItemMarginProperty = DependencyProperty.Register(
"ItemMargin", typeof(Thickness), typeof(RegularItemsControl), new PropertyMetadata(default(Thickness)));
public Thickness ItemMargin
{
get => (Thickness) GetValue(ItemMarginProperty);
set => SetValue(ItemMarginProperty, value);
}
}
}
@@ -0,0 +1,67 @@
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Markup;
using HandyControl.Data;
namespace HandyControl.Controls
{
[ContentProperty("Content")]
public class SelectableItem : Control, ISelectable
{
private bool _isMouseLeftButtonDown;
protected override void OnMouseLeave(MouseEventArgs e)
{
base.OnMouseLeave(e);
_isMouseLeftButtonDown = false;
}
protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e)
{
base.OnMouseLeftButtonDown(e);
_isMouseLeftButtonDown = true;
}
protected override void OnMouseLeftButtonUp(MouseButtonEventArgs e)
{
base.OnMouseLeftButtonUp(e);
if (_isMouseLeftButtonDown)
{
RaiseEvent(new RoutedEventArgs(SelectedEvent, this));
_isMouseLeftButtonDown = false;
}
}
public static readonly DependencyProperty IsSelectedProperty = DependencyProperty.Register(
"IsSelected", typeof(bool), typeof(SelectableItem), new PropertyMetadata(ValueBoxes.FalseBox));
public bool IsSelected
{
get => (bool)GetValue(IsSelectedProperty);
set => SetValue(IsSelectedProperty, value);
}
public static readonly RoutedEvent SelectedEvent =
EventManager.RegisterRoutedEvent("Selected", RoutingStrategy.Bubble,
typeof(RoutedEventHandler), typeof(SelectableItem));
public event RoutedEventHandler Selected
{
add => AddHandler(SelectedEvent, value);
remove => RemoveHandler(SelectedEvent, value);
}
public static readonly DependencyProperty ContentProperty = DependencyProperty.Register(
"Content", typeof(object), typeof(SelectableItem), new PropertyMetadata(default(object)));
public object Content
{
get => GetValue(ContentProperty);
set => SetValue(ContentProperty, value);
}
}
}
@@ -0,0 +1,134 @@
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Markup;
namespace HandyControl.Controls
{
/// <summary>
/// ItemsControl的轻量级版本
/// </summary>
[DefaultProperty("Items")]
[ContentProperty("Items")]
[TemplatePart(Name = ElementPanel, Type = typeof(Panel))]
public class SimpleItemsControl : Control
{
private const string ElementPanel = "PART_Panel";
public static readonly DependencyProperty ItemTemplateProperty = DependencyProperty.Register(
"ItemTemplate", typeof(DataTemplate), typeof(SimpleItemsControl),
new FrameworkPropertyMetadata(default(DataTemplate), OnItemTemplateChanged));
public static readonly DependencyProperty ItemContainerStyleProperty = DependencyProperty.Register(
"ItemContainerStyle", typeof(Style), typeof(SimpleItemsControl),
new PropertyMetadata(default(Style), OnItemContainerStyleChanged));
public SimpleItemsControl()
{
var items = new ObservableCollection<object>();
items.CollectionChanged += OnItemsCollectionChanged;
Items = items;
}
[Bindable(true)]
[Category("Content")]
public Style ItemContainerStyle
{
get => (Style) GetValue(ItemContainerStyleProperty);
set => SetValue(ItemContainerStyleProperty, value);
}
[Bindable(true)]
public DataTemplate ItemTemplate
{
get => (DataTemplate) GetValue(ItemTemplateProperty);
set => SetValue(ItemTemplateProperty, value);
}
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
[Bindable(true)]
public Collection<object> Items { get; }
internal Panel ItemsHost { get; set; }
public override void OnApplyTemplate()
{
ItemsHost?.Children.Clear();
base.OnApplyTemplate();
ItemsHost = GetTemplateChild(ElementPanel) as Panel;
Refresh();
}
private void OnItemsCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
Refresh();
UpdateItems();
}
protected virtual DependencyObject GetContainerForItemOverride() => new ContentPresenter();
protected virtual bool IsItemItsOwnContainerOverride(object item) => item is ContentPresenter;
protected virtual void PrepareContainerForItemOverride(DependencyObject element, object item)
{
switch (element)
{
case ContentControl contentControl:
contentControl.Content = item;
contentControl.ContentTemplate = ItemTemplate;
break;
case ContentPresenter contentPresenter:
contentPresenter.Content = item;
contentPresenter.ContentTemplate = ItemTemplate;
break;
}
}
private static void OnItemTemplateChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var target = d as SimpleItemsControl;
target?.Refresh();
}
private static void OnItemContainerStyleChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var target = d as SimpleItemsControl;
target?.Refresh();
}
protected virtual void Refresh()
{
if (ItemsHost == null) return;
ItemsHost.Children.Clear();
foreach (var item in Items)
{
DependencyObject container;
if (IsItemItsOwnContainerOverride(item))
{
container = item as DependencyObject;
}
else
{
container = GetContainerForItemOverride();
PrepareContainerForItemOverride(container, item);
}
if (container is FrameworkElement element)
{
element.Style = ItemContainerStyle;
ItemsHost.Children.Add(element);
}
}
}
protected virtual void UpdateItems()
{
}
}
}
@@ -0,0 +1,23 @@
using System.Windows.Controls;
namespace HandyControl.Controls
{
/// <summary>
/// 带上下文菜单的按钮
/// </summary>
public class ContextMenuButton : Button
{
public ContextMenu Menu { get; set; }
protected override void OnClick()
{
base.OnClick();
if (Menu != null)
{
Menu.PlacementTarget = this;
Menu.IsOpen = true;
}
}
}
}
@@ -0,0 +1,31 @@
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
namespace HandyControl.Controls
{
/// <summary>
/// 带上下文菜单的切换按钮
/// </summary>
public class ContextMenuToggleButton : ToggleButton
{
public ContextMenu Menu { get; set; }
protected override void OnClick()
{
base.OnClick();
if (Menu != null)
{
if (IsChecked == true)
{
Menu.PlacementTarget = this;
Menu.IsOpen = true;
}
else
{
Menu.IsOpen = false;
}
}
}
}
}
@@ -0,0 +1,338 @@
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 bool _updateItems;
private bool _isLoaded;
/// <summary>
/// 跳转编号
/// </summary>
private int _jumpToIndex = -1;
public CoverFlow()
{
Loaded += (s, e) =>
{
if (_isLoaded) return;
_isLoaded = true;
if (_updateItems)
{
UpdateShowRange();
_updateItems = false;
}
if (_jumpToIndex > 0)
{
PageIndex = _jumpToIndex;
}
};
}
/// <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;
if (_isLoaded)
{
UpdateShowRange();
_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);
}
_updateItems = true;
}
/// <summary>
/// 添加一项资源
/// </summary>
/// <param name="uriString"></param>
public void Add(string uriString)
{
_uriList.Add(_uriList.Count, new Uri(uriString));
_updateItems = true;
}
/// <summary>
/// 添加一项资源
/// </summary>
/// <param name="uri"></param>
public void Add(Uri uri)
{
_uriList.Add(_uriList.Count, uri);
_updateItems = true;
}
/// <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,192 @@
using System.Windows;
using System.Windows.Data;
using HandyControl.Data;
namespace HandyControl.Controls
{
public class CoverView : RegularItemsControl
{
private readonly CoverViewContent _viewContent;
private CoverViewItem _selectedItem;
public CoverView()
{
_viewContent = new CoverViewContent {ContentHeight = ItemContentHeight};
AddHandler(SelectableItem.SelectedEvent, new RoutedEventHandler(CoverViewItem_OnSelected));
_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));
public double ItemContentHeight
{
get => (double) GetValue(ItemContentHeightProperty);
set => SetValue(ItemContentHeightProperty, 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);
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,100 @@
using System.Windows;
using System.Windows.Controls;
using HandyControl.Data;
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;
private bool _isOpen;
internal bool CanSwitch { get; set; } = true;
internal bool IsOpen
{
get => _isOpen;
set
{
if (_isOpen == value) return;
_isOpen = value;
OpenSwitch(value);
}
}
public static readonly DependencyProperty ContentHeightProperty = DependencyProperty.Register(
"ContentHeight", typeof(double), typeof(CoverViewContent), new PropertyMetadata(ValueBoxes.Double300Box));
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)));
}
private void OpenSwitch(bool isOpen)
{
if (_content == null) return;
var animation = AnimationHelper.CreateAnimation(isOpen ? 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; }
}
}
@@ -0,0 +1,461 @@
using System;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Security.Permissions;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Interop;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Resources;
using HandyControl.Data;
using HandyControl.Tools;
namespace HandyControl.Controls
{
public class GifImage : Image, IDisposable
{
private static readonly Guid GifGuid = new Guid("{b96b3caa-0728-11d3-9d7b-0000f81ef32e}");
internal IntPtr NativeImage;
private byte[] _rawData;
private bool _isStart;
private bool _isLoaded;
static GifImage()
{
VisibilityProperty.OverrideMetadata(typeof(GifImage), new PropertyMetadata(default(Visibility), OnVisibilityChanged));
}
private static void OnVisibilityChanged(DependencyObject s, DependencyPropertyChangedEventArgs e)
{
var ctl = (GifImage)s;
var v = (Visibility)e.NewValue;
if (v != Visibility.Visible)
{
ctl.StopAnimate();
}
else if (!ctl._isStart)
{
ctl.StartAnimate();
}
}
~GifImage() => Dispose(false);
public GifImage()
{
Loaded += (s, e) =>
{
if (DesignerProperties.GetIsInDesignMode(this)) return;
if (_isLoaded) return;
_isLoaded = true;
if (Source is BitmapImage image)
{
var uri = image.UriSource;
GetGifStreamFromPack(uri);
StartAnimate();
}
};
}
public GifImage(string filename)
{
Source = null;
CreateSourceFromFile(filename);
StartAnimate();
}
public GifImage(Stream stream)
{
Source = null;
CreateSourceFromStream(stream);
StartAnimate();
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (NativeImage == IntPtr.Zero) return;
try
{
StopAnimate();
ExternDllHelper.Gdip.GdipDisposeImage(new HandleRef(this, NativeImage));
}
catch
{
// ignored
}
finally
{
NativeImage = IntPtr.Zero;
}
}
private void CreateSourceFromFile(string filename)
{
filename = Path.GetFullPath(filename);
var status = ExternDllHelper.Gdip.GdipCreateBitmapFromFile(filename, out var bitmap);
if (status != ExternDllHelper.Gdip.Ok)
throw ExternDllHelper.Gdip.StatusException(status);
status = ExternDllHelper.Gdip.GdipImageForceValidation(new HandleRef(null, bitmap));
if (status != ExternDllHelper.Gdip.Ok)
{
ExternDllHelper.Gdip.GdipDisposeImage(new HandleRef(null, bitmap));
throw ExternDllHelper.Gdip.StatusException(status);
}
SetNativeImage(bitmap);
EnsureSave(this, filename, null);
}
private void CreateSourceFromStream(Stream stream)
{
if (stream == null)
throw new ArgumentException("stream null");
var status = ExternDllHelper.Gdip.GdipCreateBitmapFromStream(new GPStream(stream), out var bitmap);
if (status != ExternDllHelper.Gdip.Ok)
throw ExternDllHelper.Gdip.StatusException(status);
status = ExternDllHelper.Gdip.GdipImageForceValidation(new HandleRef(null, bitmap));
if (status != ExternDllHelper.Gdip.Ok)
{
ExternDllHelper.Gdip.GdipDisposeImage(new HandleRef(null, bitmap));
throw ExternDllHelper.Gdip.StatusException(status);
}
SetNativeImage(bitmap);
EnsureSave(this, null, stream);
}
private void GetGifStreamFromPack(Uri uri)
{
try
{
StreamResourceInfo streamInfo;
if (!uri.IsAbsoluteUri)
{
streamInfo = Application.GetContentStream(uri) ?? Application.GetResourceStream(uri);
}
else
{
if (uri.GetLeftPart(UriPartial.Authority).Contains("siteoforigin"))
{
streamInfo = Application.GetRemoteStream(uri);
}
else
{
streamInfo = Application.GetContentStream(uri) ?? Application.GetResourceStream(uri);
}
}
if (streamInfo == null)
throw new FileNotFoundException("Resource not found.", uri.ToString());
CreateSourceFromStream(streamInfo.Stream);
}
catch
{
// ignored
}
}
[ResourceExposure(ResourceScope.None)]
[ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
internal static void EnsureSave(GifImage image, string filename, Stream dataStream)
{
if (!image.RawGuid.Equals(GifGuid)) return;
var animatedGif = false;
var dimensions = image.FrameDimensionsList;
if (dimensions.Select(guid => new GifFrameDimension(guid)).Contains(GifFrameDimension.Time))
{
animatedGif = image.GetFrameCount(GifFrameDimension.Time) > 1;
}
if (!animatedGif) return;
try
{
Stream created = null;
long lastPos = 0;
if (dataStream != null)
{
lastPos = dataStream.Position;
dataStream.Position = 0;
}
try
{
if (dataStream == null)
{
created = dataStream = File.OpenRead(filename);
}
image._rawData = new byte[(int)dataStream.Length];
dataStream.Read(image._rawData, 0, (int)dataStream.Length);
}
finally
{
if (created != null)
{
created.Close();
}
else
{
// ReSharper disable once PossibleNullReferenceException
dataStream.Position = lastPos;
}
}
}
// possible exceptions for reading the filename
catch (UnauthorizedAccessException)
{
}
catch (DirectoryNotFoundException)
{
}
catch (IOException)
{
}
// possible exceptions for setting/getting the position inside dataStream
catch (NotSupportedException)
{
}
catch (ObjectDisposedException)
{
}
// possible exception when reading stuff into dataStream
catch (ArgumentException)
{
}
}
internal void SetNativeImage(IntPtr handle)
{
if (handle == IntPtr.Zero)
throw new ArgumentException("NativeHandle0");
NativeImage = handle;
}
internal Guid RawGuid
{
get
{
var guid = new Guid();
var status = ExternDllHelper.Gdip.GdipGetImageRawFormat(new HandleRef(this, NativeImage), ref guid);
if (status != ExternDllHelper.Gdip.Ok)
throw ExternDllHelper.Gdip.StatusException(status);
return guid;
}
}
internal void StartAnimate()
{
ImageAnimator.Animate(this, OnFrameChanged);
_isStart = true;
}
internal void StopAnimate()
{
ImageAnimator.StopAnimate(this, OnFrameChanged);
_isStart = false;
}
internal Guid[] FrameDimensionsList
{
get
{
var status = ExternDllHelper.Gdip.GdipImageGetFrameDimensionsCount(new HandleRef(this, NativeImage), out var count);
if (status != ExternDllHelper.Gdip.Ok)
{
throw ExternDllHelper.Gdip.StatusException(status);
}
if (count <= 0)
{
return new Guid[0];
}
var size = Marshal.SizeOf(typeof(Guid));
var buffer = Marshal.AllocHGlobal(checked(size * count));
if (buffer == IntPtr.Zero)
{
throw ExternDllHelper.Gdip.StatusException(ExternDllHelper.Gdip.OutOfMemory);
}
status = ExternDllHelper.Gdip.GdipImageGetFrameDimensionsList(new HandleRef(this, NativeImage), buffer, count);
if (status != ExternDllHelper.Gdip.Ok)
{
Marshal.FreeHGlobal(buffer);
throw ExternDllHelper.Gdip.StatusException(status);
}
var guids = new Guid[count];
try
{
for (int i = 0; i < count; i++)
{
guids[i] = (Guid)ExternDllHelper.PtrToStructure((IntPtr)((long)buffer + size * i), typeof(Guid));
}
}
finally
{
Marshal.FreeHGlobal(buffer);
}
return guids;
}
}
internal int GetFrameCount(GifFrameDimension dimension)
{
var count = new[] { 0 };
var dimensionId = dimension.Guid;
var status = ExternDllHelper.Gdip.GdipImageGetFrameCount(new HandleRef(this, NativeImage), ref dimensionId, count);
if (status != ExternDllHelper.Gdip.Ok)
throw ExternDllHelper.Gdip.StatusException(status);
return count[0];
}
internal GifPropertyItem GetPropertyItem(int propid)
{
GifPropertyItem propitem;
var status = ExternDllHelper.Gdip.GdipGetPropertyItemSize(new HandleRef(this, NativeImage), propid, out var size);
if (status != ExternDllHelper.Gdip.Ok)
throw ExternDllHelper.Gdip.StatusException(status);
if (size == 0)
return null;
var propdata = Marshal.AllocHGlobal(size);
if (propdata == IntPtr.Zero)
throw ExternDllHelper.Gdip.StatusException(ExternDllHelper.Gdip.OutOfMemory);
status = ExternDllHelper.Gdip.GdipGetPropertyItem(new HandleRef(this, NativeImage), propid, size, propdata);
try
{
if (status != ExternDllHelper.Gdip.Ok)
{
throw ExternDllHelper.Gdip.StatusException(status);
}
propitem = GifPropertyItemInternal.ConvertFromMemory(propdata, 1)[0];
}
finally
{
Marshal.FreeHGlobal(propdata);
}
return propitem;
}
internal int SelectActiveFrame(GifFrameDimension dimension, int frameIndex)
{
var count = new[] { 0 };
var dimensionId = dimension.Guid;
var status = ExternDllHelper.Gdip.GdipImageSelectActiveFrame(new HandleRef(this, NativeImage), ref dimensionId, frameIndex);
if (status != ExternDllHelper.Gdip.Ok)
throw ExternDllHelper.Gdip.StatusException(status);
return count[0];
}
[SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.UnmanagedCode)]
[EditorBrowsable(EditorBrowsableState.Advanced)]
[ResourceExposure(ResourceScope.Machine)]
[ResourceConsumption(ResourceScope.Machine)]
internal IntPtr GetHbitmap() => GetHbitmap(Colors.LightGray);
[SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.UnmanagedCode)]
[EditorBrowsable(EditorBrowsableState.Advanced)]
[ResourceExposure(ResourceScope.Machine)]
[ResourceConsumption(ResourceScope.Machine)]
internal IntPtr GetHbitmap(Color background)
{
var status = ExternDllHelper.Gdip.GdipCreateHBITMAPFromBitmap(new HandleRef(this, NativeImage), out var hBitmap,
ColorHelper.ToWin32(background));
if (status == 2 && (Width >= short.MaxValue || Height >= short.MaxValue))
{
throw new ArgumentException("GdiplusInvalidSize");
}
if (status != ExternDllHelper.Gdip.Ok && NativeImage != IntPtr.Zero)
{
throw ExternDllHelper.Gdip.StatusException(status);
}
return hBitmap;
}
private void GetBitmapSource()
{
var handle = IntPtr.Zero;
try
{
handle = GetHbitmap();
Source = Imaging.CreateBitmapSourceFromHBitmap(handle, IntPtr.Zero, Int32Rect.Empty,
BitmapSizeOptions.FromEmptyOptions());
}
catch
{
// ignored
}
finally
{
if (handle != IntPtr.Zero)
{
ExternDllHelper.DeleteObject(handle);
}
}
}
private void OnFrameChanged(object sender, EventArgs e)
{
Dispatcher.BeginInvoke(new Action(() =>
{
ImageAnimator.UpdateFrames();
Source?.Freeze();
GetBitmapSource();
InvalidateVisual();
}));
}
}
}
@@ -0,0 +1,883 @@
using System;
using System.Diagnostics;
using System.IO;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Media.Imaging;
using HandyControl.Data;
using HandyControl.Interactivity;
using HandyControl.Properties.Langs;
using HandyControl.Tools;
using Microsoft.Win32;
namespace HandyControl.Controls
{
[TemplatePart(Name = ElementGridMain, Type = typeof(Grid))]
[TemplatePart(Name = ElementCanvasSmallImg, Type = typeof(Canvas))]
[TemplatePart(Name = ElementBorderMove, Type = typeof(Border))]
[TemplatePart(Name = ElementBorderBottom, Type = typeof(Border))]
[TemplatePart(Name = ElementImageMain, Type = typeof(Image))]
public class ImageViewer : Control
{
#region Constants
private const string ElementGridMain = "PART_GridMain";
private const string ElementCanvasSmallImg = "PART_CanvasSmallImg";
private const string ElementBorderMove = "PART_BorderMove";
private const string ElementBorderBottom = "PART_BorderBottom";
private const string ElementImageMain = "PART_ImageMain";
/// <summary>
/// 缩放比间隔
/// </summary>
private const double ScaleInternal = 0.2;
#endregion Constants
#region Data
/// <summary>
/// 图片保存对话框
/// </summary>
private static readonly SaveFileDialog SaveFileDialog = new SaveFileDialog
{
Filter = $"{Lang.PngImg}|*.png"
};
private Grid _gridMain;
private Canvas _canvasSmallImg;
private Border _borderMove;
private Border _borderBottom;
private Image _imageMain;
/// <summary>
/// 右下角小图片是否加载过
/// </summary>
private bool _borderSmallIsLoaded;
/// <summary>
/// 图片是否可以在x轴方向移动
/// </summary>
private bool _canMoveX;
/// <summary>
/// 图片是否可以在y轴方向移动
/// </summary>
private bool _canMoveY;
/// <summary>
/// 图片实际位置
/// </summary>
private Thickness _imgActualMargin;
/// <summary>
/// 图片实际旋转角度
/// </summary>
private double _imgActualRotate;
/// <summary>
/// 图片实际旋缩放比
/// </summary>
private double _imgActualScale = 1;
/// <summary>
/// 在图片上鼠标移动时的即时位置
/// </summary>
private Point _imgCurrentPoint;
/// <summary>
/// 鼠标是否在图片上按下左键
/// </summary>
private bool _imgIsMouseLeftButtonDown;
/// <summary>
/// 在图片上按下时图片的位置
/// </summary>
private Thickness _imgMouseDownMargin;
/// <summary>
/// 在图片上按下时鼠标的位置
/// </summary>
private Point _imgMouseDownPoint;
/// <summary>
/// 在小图片上鼠标移动时的即时位置
/// </summary>
private Point _imgSmallCurrentPoint;
/// <summary>
/// 鼠标是否在小图片上按下左键
/// </summary>
private bool _imgSmallIsMouseLeftButtonDown;
/// <summary>
/// 在小图片上按下时图片的位置
/// </summary>
private Thickness _imgSmallMouseDownMargin;
/// <summary>
/// 在小图片上按下时鼠标的位置
/// </summary>
private Point _imgSmallMouseDownPoint;
/// <summary>
/// 图片长宽比
/// </summary>
private double _imgWidHeiScale;
/// <summary>
/// 是否已经初始化
/// </summary>
private bool _isLoaded;
/// <summary>
/// 图片是否倾斜
/// </summary>
private bool _isOblique;
/// <summary>
/// 缩放高度间隔
/// </summary>
private double _scaleInternalHeight;
/// <summary>
/// 缩放宽度间隔
/// </summary>
private double _scaleInternalWidth;
/// <summary>
/// 底部BorderBottom(包含一些图片操作)是否显示中
/// </summary>
private bool _showBorderBottom;
#endregion Data
#region ctor
public ImageViewer()
{
Loaded += ImageViewer_OnLoaded;
CommandBindings.Add(new CommandBinding(ControlCommands.Save, ButtonSave_OnClick));
CommandBindings.Add(new CommandBinding(ControlCommands.Open, ButtonWindowsOpen_OnClick));
CommandBindings.Add(new CommandBinding(ControlCommands.Restore, ButtonActual_OnClick));
CommandBindings.Add(new CommandBinding(ControlCommands.Reduce, ButtonReduce_OnClick));
CommandBindings.Add(new CommandBinding(ControlCommands.Enlarge, ButtonEnlarge_OnClick));
CommandBindings.Add(new CommandBinding(ControlCommands.RotateLeft, ButtonRotateLeft_OnClick));
CommandBindings.Add(new CommandBinding(ControlCommands.RotateRight, ButtonRotateRight_OnClick));
}
/// <summary>
/// 带一个图片Uri的构造函数
/// </summary>
/// <param name="uri"></param>
public ImageViewer(Uri uri) : this()
{
try
{
ImageSource = BitmapFrame.Create(uri);
ImgPath = uri.AbsolutePath;
if (File.Exists(ImgPath))
{
var info = new FileInfo(ImgPath);
ImgSize = info.Length;
}
}
catch
{
MessageBox.Show(Lang.ErrorImgPath);
}
}
/// <summary>
/// 带一个图片路径的构造函数
/// </summary>
/// <param name="path"></param>
public ImageViewer(string path) : this(new Uri(path))
{
}
#endregion
#region Properties
public static readonly DependencyProperty ShowImgMapProperty = DependencyProperty.Register(
"ShowImgMap", typeof(bool), typeof(ImageViewer), new PropertyMetadata(ValueBoxes.FalseBox));
public static readonly DependencyProperty ImageSourceProperty = DependencyProperty.Register(
"ImageSource", typeof(BitmapFrame), typeof(ImageViewer), new PropertyMetadata(default(BitmapFrame)));
public static readonly DependencyProperty IsFullScreenProperty = DependencyProperty.Register(
"IsFullScreen", typeof(bool), typeof(ImageViewer), new PropertyMetadata(ValueBoxes.FalseBox));
internal static readonly DependencyProperty ImgPathProperty = DependencyProperty.Register(
"ImgPath", typeof(string), typeof(ImageViewer), new PropertyMetadata(default(string)));
internal static readonly DependencyProperty ImgSizeProperty = DependencyProperty.Register(
"ImgSize", typeof(long), typeof(ImageViewer), new PropertyMetadata(-1L));
/// <summary>
/// 是否显示全屏按钮
/// </summary>
internal static readonly DependencyProperty ShowFullScreenButtonProperty = DependencyProperty.Register(
"ShowFullScreenButton", typeof(bool), typeof(ImageViewer), new PropertyMetadata(ValueBoxes.FalseBox));
/// <summary>
/// 关闭按钮是否显示中
/// </summary>
internal static readonly DependencyProperty ShowCloseButtonProperty = DependencyProperty.Register(
"ShowCloseButton", typeof(bool), typeof(ImageViewer), new PropertyMetadata(ValueBoxes.FalseBox));
internal static readonly DependencyProperty ImageContentProperty = DependencyProperty.Register(
"ImageContent", typeof(object), typeof(ImageViewer), new PropertyMetadata(default(object)));
internal static readonly DependencyProperty ImageMarginProperty = DependencyProperty.Register(
"ImageMargin", typeof(Thickness), typeof(ImageViewer), new PropertyMetadata(default(Thickness)));
internal static readonly DependencyProperty ImageWidthProperty = DependencyProperty.Register(
"ImageWidth", typeof(double), typeof(ImageViewer), new PropertyMetadata(ValueBoxes.Double0Box));
internal static readonly DependencyProperty ImageHeightProperty = DependencyProperty.Register(
"ImageHeight", typeof(double), typeof(ImageViewer), new PropertyMetadata(ValueBoxes.Double0Box));
internal static readonly DependencyProperty ImageScaleProperty = DependencyProperty.Register(
"ImageScale", typeof(double), typeof(ImageViewer), new PropertyMetadata(ValueBoxes.Double1Box, OnImageScaleChanged));
internal static readonly DependencyProperty ScaleStrProperty = DependencyProperty.Register(
"ScaleStr", typeof(string), typeof(ImageViewer), new PropertyMetadata("100%"));
internal static readonly DependencyProperty ImageRotateProperty = DependencyProperty.Register(
"ImageRotate", typeof(double), typeof(ImageViewer), new PropertyMetadata(ValueBoxes.Double0Box));
internal static readonly DependencyProperty ShowSmallImgInternalProperty = DependencyProperty.Register(
"ShowSmallImgInternal", typeof(bool), typeof(ImageViewer), new PropertyMetadata(ValueBoxes.FalseBox));
public bool IsFullScreen
{
get => (bool)GetValue(IsFullScreenProperty);
set => SetValue(IsFullScreenProperty, value);
}
public bool ShowImgMap
{
get => (bool)GetValue(ShowImgMapProperty);
set => SetValue(ShowImgMapProperty, value);
}
public BitmapFrame ImageSource
{
get => (BitmapFrame)GetValue(ImageSourceProperty);
set => SetValue(ImageSourceProperty, value);
}
internal object ImageContent
{
get => GetValue(ImageContentProperty);
set => SetValue(ImageContentProperty, value);
}
internal string ImgPath
{
get => (string)GetValue(ImgPathProperty);
set => SetValue(ImgPathProperty, value);
}
internal long ImgSize
{
get => (long)GetValue(ImgSizeProperty);
set => SetValue(ImgSizeProperty, value);
}
/// <summary>
/// 是否显示全屏按钮
/// </summary>
internal bool ShowFullScreenButton
{
get => (bool)GetValue(ShowFullScreenButtonProperty);
set => SetValue(ShowFullScreenButtonProperty, value);
}
internal Thickness ImageMargin
{
get => (Thickness)GetValue(ImageMarginProperty);
set => SetValue(ImageMarginProperty, value);
}
internal double ImageWidth
{
get => (double)GetValue(ImageWidthProperty);
set => SetValue(ImageWidthProperty, value);
}
internal double ImageHeight
{
get => (double)GetValue(ImageHeightProperty);
set => SetValue(ImageHeightProperty, value);
}
internal double ImageScale
{
get => (double)GetValue(ImageScaleProperty);
set => SetValue(ImageScaleProperty, value);
}
internal string ScaleStr
{
get => (string)GetValue(ScaleStrProperty);
set => SetValue(ScaleStrProperty, value);
}
internal double ImageRotate
{
get => (double)GetValue(ImageRotateProperty);
set => SetValue(ImageRotateProperty, value);
}
internal bool ShowSmallImgInternal
{
get => (bool)GetValue(ShowSmallImgInternalProperty);
set => SetValue(ShowSmallImgInternalProperty, value);
}
/// <summary>
/// 图片原始宽度
/// </summary>
private double ImageOriWidth { get; set; }
/// <summary>
/// 图片原始高度
/// </summary>
private double ImageOriHeight { get; set; }
/// <summary>
/// 关闭按钮是否显示中
/// </summary>
internal bool ShowCloseButton
{
get => (bool)GetValue(ShowCloseButtonProperty);
set => SetValue(ShowCloseButtonProperty, value);
}
/// <summary>
/// 底部BorderBottom(包含一些图片操作)是否显示中
/// </summary>
internal bool ShowBorderBottom
{
get => _showBorderBottom;
set
{
if (_showBorderBottom == value) return;
_borderBottom?.BeginAnimation(OpacityProperty,
value ? AnimationHelper.CreateAnimation(1, 100) : AnimationHelper.CreateAnimation(0, 400));
_showBorderBottom = value;
}
}
#endregion
public override void OnApplyTemplate()
{
if (_imageMain != null) _imageMain.MouseLeftButtonDown -= ImageMain_OnMouseLeftButtonDown;
if (_canvasSmallImg != null)
{
_canvasSmallImg.MouseLeftButtonDown -= CanvasSmallImg_OnMouseLeftButtonDown;
_canvasSmallImg.MouseLeftButtonUp -= CanvasSmallImg_OnMouseLeftButtonUp;
_canvasSmallImg.MouseMove -= CanvasSmallImg_OnMouseMove;
_canvasSmallImg.MouseLeave -= CanvasSmallImg_OnMouseLeave;
}
base.OnApplyTemplate();
_gridMain = GetTemplateChild(ElementGridMain) as Grid;
_canvasSmallImg = GetTemplateChild(ElementCanvasSmallImg) as Canvas;
_borderMove = GetTemplateChild(ElementBorderMove) as Border;
_imageMain = GetTemplateChild(ElementImageMain) as Image;
_borderBottom = GetTemplateChild(ElementBorderBottom) as Border;
if (_imageMain != null)
{
var t = new RotateTransform();
BindingOperations.SetBinding(t, RotateTransform.AngleProperty, new Binding(ImageRotateProperty.Name) {Source = this});
_imageMain.LayoutTransform = t;
_imageMain.MouseLeftButtonDown += ImageMain_OnMouseLeftButtonDown;
}
if (_canvasSmallImg != null)
{
_canvasSmallImg.MouseLeftButtonDown += CanvasSmallImg_OnMouseLeftButtonDown;
_canvasSmallImg.MouseLeftButtonUp += CanvasSmallImg_OnMouseLeftButtonUp;
_canvasSmallImg.MouseMove += CanvasSmallImg_OnMouseMove;
_canvasSmallImg.MouseLeave += CanvasSmallImg_OnMouseLeave;
}
}
private static void OnImageScaleChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is ImageViewer imageViewer && e.NewValue is double newValue)
{
imageViewer.ImageWidth = imageViewer.ImageOriWidth * newValue;
imageViewer.ImageHeight = imageViewer.ImageOriHeight * newValue;
imageViewer.ScaleStr = $"{newValue * 100:#0}%";
}
}
/// <summary>
/// 初始化
/// </summary>
private void Init()
{
if (ImageSource == null) return;
double width;
double height;
if (!_isOblique)
{
width = ImageSource.Width;
height = ImageSource.Height;
}
else
{
width = ImageSource.Height;
height = ImageSource.Width;
}
ImageWidth = width;
ImageHeight = height;
ImageOriWidth = width;
ImageOriHeight = height;
_scaleInternalWidth = ImageOriWidth * ScaleInternal;
_scaleInternalHeight = ImageOriHeight * ScaleInternal;
if (Math.Abs(height - 0) < 0.001 || Math.Abs(width - 0) < 0.001)
{
MessageBox.Show(Lang.ErrorImgSize);
return;
}
_imgWidHeiScale = width / height;
var scaleWindow = ActualWidth / ActualHeight;
if (_imgWidHeiScale > scaleWindow)
{
if (width > ActualWidth)
ImageScale = ActualWidth / width;
}
else if (height > ActualHeight)
{
ImageScale = ActualHeight / height;
}
else
{
ImageScale = 1;
}
ImageMargin = new Thickness((ActualWidth - ImageWidth) / 2, (ActualHeight - ImageHeight) / 2, 0, 0);
_imgActualScale = 1;
_imgActualMargin = ImageMargin;
}
private void ButtonActual_OnClick(object sender, RoutedEventArgs e)
{
var scaleAnimation = AnimationHelper.CreateAnimation(1);
scaleAnimation.FillBehavior = FillBehavior.Stop;
_imgActualScale = 1;
scaleAnimation.Completed += (s, e1) =>
{
ImageScale = 1;
_canMoveX = ImageWidth > ActualWidth;
_canMoveY = ImageHeight > ActualHeight;
BorderSmallShowSwitch();
};
var thickness = new Thickness((ActualWidth - ImageOriWidth) / 2, (ActualHeight - ImageOriHeight) / 2, 0, 0);
var marginAnimation = AnimationHelper.CreateAnimation(thickness);
marginAnimation.FillBehavior = FillBehavior.Stop;
_imgActualMargin = thickness;
marginAnimation.Completed += (s, e1) => { ImageMargin = thickness; };
BeginAnimation(ImageScaleProperty, scaleAnimation);
BeginAnimation(ImageMarginProperty, marginAnimation);
}
private void ButtonReduce_OnClick(object sender, RoutedEventArgs e) => ScaleImg(false);
private void ButtonEnlarge_OnClick(object sender, RoutedEventArgs e) => ScaleImg(true);
private void ButtonRotateLeft_OnClick(object sender, RoutedEventArgs e) => RotateImg(_imgActualRotate - 90);
private void ButtonRotateRight_OnClick(object sender, RoutedEventArgs e) => RotateImg(_imgActualRotate + 90);
private void ButtonSave_OnClick(object sender, RoutedEventArgs e)
{
if (ImageSource == null) return;
SaveFileDialog.FileName = $"{DateTime.Now:yyyy-M-d-h-m-s.fff}";
if (SaveFileDialog.ShowDialog() == true)
{
var path = SaveFileDialog.FileName;
using (var fileStream = new FileStream(path, FileMode.Create, FileAccess.Write))
{
var encoder = new PngBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(ImageSource));
encoder.Save(fileStream);
}
}
}
private void ButtonWindowsOpen_OnClick(object sender, RoutedEventArgs e)
{
try
{
Process.Start(ImgPath);
}
catch (Exception exception)
{
MessageBox.Show(exception.Message);
}
}
protected override void OnMouseMove(MouseEventArgs e)
{
base.OnMouseMove(e);
MoveImg();
}
protected override void OnMouseLeave(MouseEventArgs e)
{
base.OnMouseLeave(e);
_imgIsMouseLeftButtonDown = false;
ShowBorderBottom = false;
}
protected override void OnMouseWheel(MouseWheelEventArgs e)
{
base.OnMouseWheel(e);
ScaleImg(e.Delta > 0);
}
protected override void OnRenderSizeChanged(SizeChangedInfo sizeInfo)
{
base.OnRenderSizeChanged(sizeInfo);
if (!_isLoaded) return;
if (ImageWidth < 0.001 || ImageHeight < 0.001) return;
_canMoveX = true;
_canMoveY = true;
var marginX = ImageMargin.Left;
var marginY = ImageMargin.Top;
if (ImageWidth <= ActualWidth)
{
_canMoveX = false;
marginX = (ActualWidth - ImageWidth) / 2;
}
if (ImageHeight <= ActualHeight)
{
_canMoveY = false;
marginY = (ActualHeight - ImageHeight) / 2;
}
ImageMargin = new Thickness(marginX, marginY, 0, 0);
_imgActualMargin = ImageMargin;
BorderSmallShowSwitch();
_imgSmallMouseDownMargin = _borderMove.Margin;
MoveSmallImg(_imgSmallMouseDownMargin.Left, _imgSmallMouseDownMargin.Top);
}
private void ImageViewer_OnLoaded(object sender, RoutedEventArgs e)
{
if (_isLoaded) return;
Init();
_isLoaded = true;
}
private void ImageMain_OnMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
_imgMouseDownPoint = Mouse.GetPosition(_gridMain);
_imgMouseDownMargin = ImageMargin;
_imgIsMouseLeftButtonDown = true;
}
protected override void OnPreviewMouseLeftButtonUp(MouseButtonEventArgs e)
{
base.OnPreviewMouseLeftButtonUp(e);
_imgIsMouseLeftButtonDown = false;
}
/// <summary>
/// 右下角小图片显示切换
/// </summary>
private void BorderSmallShowSwitch()
{
if (_canMoveX || _canMoveY)
{
if (!_borderSmallIsLoaded)
{
_canvasSmallImg.Background = new VisualBrush(_imageMain);
InitBorderSmall();
_borderSmallIsLoaded = true;
}
ShowSmallImgInternal = true;
UpdateBorderSmall();
}
else
{
ShowSmallImgInternal = false;
}
}
/// <summary>
/// 初始化右下角小图片
/// </summary>
private void InitBorderSmall()
{
var scaleWindow = _canvasSmallImg.MaxWidth / _canvasSmallImg.MaxHeight;
if (_imgWidHeiScale > scaleWindow)
{
_canvasSmallImg.Width = _canvasSmallImg.MaxWidth;
_canvasSmallImg.Height = _canvasSmallImg.Width / _imgWidHeiScale;
}
else
{
_canvasSmallImg.Width = _canvasSmallImg.MaxHeight * _imgWidHeiScale;
_canvasSmallImg.Height = _canvasSmallImg.MaxHeight;
}
}
/// <summary>
/// 更新右下角小图片
/// </summary>
private void UpdateBorderSmall()
{
if (!ShowSmallImgInternal) return;
var widthMin = Math.Min(ImageWidth, ActualWidth);
var heightMin = Math.Min(ImageHeight, ActualHeight);
_borderMove.Width = widthMin / ImageWidth * _canvasSmallImg.Width;
_borderMove.Height = heightMin / ImageHeight * _canvasSmallImg.Height;
var marginX = -ImageMargin.Left / ImageWidth * _canvasSmallImg.Width;
var marginY = -ImageMargin.Top / ImageHeight * _canvasSmallImg.Height;
var marginXMax = _canvasSmallImg.Width - _borderMove.Width;
var marginYMax = _canvasSmallImg.Height - _borderMove.Height;
marginX = Math.Max(0, marginX);
marginX = Math.Min(marginXMax, marginX);
marginY = Math.Max(0, marginY);
marginY = Math.Min(marginYMax, marginY);
_borderMove.Margin = new Thickness(marginX, marginY, 0, 0);
}
/// <summary>
/// 缩放图片
/// </summary>
/// <param name="isEnlarge"></param>
private void ScaleImg(bool isEnlarge)
{
if (Mouse.LeftButton == MouseButtonState.Pressed) return;
var oldImageWidth = ImageWidth;
var olgImageHeight = ImageHeight;
var tempScale = isEnlarge ? _imgActualScale + ScaleInternal : _imgActualScale - ScaleInternal;
if (Math.Abs(tempScale) < ScaleInternal || Math.Abs(tempScale - 50) < 0.001)
return;
ImageScale = tempScale;
var posCanvas = Mouse.GetPosition(_gridMain);
var posImg = new Point(posCanvas.X - _imgActualMargin.Left, posCanvas.Y - _imgActualMargin.Top);
var marginX = .5 * _scaleInternalWidth;
var marginY = .5 * _scaleInternalHeight;
if (ImageWidth > ActualWidth)
{
_canMoveX = true;
if (ImageHeight > ActualHeight)
{
_canMoveY = true;
marginX = posImg.X / oldImageWidth * _scaleInternalWidth;
marginY = posImg.Y / olgImageHeight * _scaleInternalHeight;
}
else
{
_canMoveY = false;
}
}
else
{
_canMoveY = ImageHeight > ActualHeight;
_canMoveX = false;
}
Thickness thickness;
if (isEnlarge)
{
thickness = new Thickness(_imgActualMargin.Left - marginX, _imgActualMargin.Top - marginY, 0, 0);
}
else
{
var marginActualX = _imgActualMargin.Left + marginX;
var marginActualY = _imgActualMargin.Top + marginY;
var subX = ImageWidth - ActualWidth;
var subY = ImageHeight - ActualHeight;
var right = Math.Abs(_borderMove.Width - _canvasSmallImg.ActualWidth + _borderMove.Margin.Left);
var top = Math.Abs(_borderMove.Height - _canvasSmallImg.ActualHeight + _borderMove.Margin.Top);
if (Math.Abs(ImageMargin.Left) < 0.001 || right < 0.001)
marginActualX = _imgActualMargin.Left + _borderMove.Margin.Left /
(_canvasSmallImg.ActualWidth - _borderMove.Width) * _scaleInternalWidth;
if (Math.Abs(ImageMargin.Top) < 0.001 || top < 0.001)
marginActualY = _imgActualMargin.Top + _borderMove.Margin.Top /
(_canvasSmallImg.ActualHeight - _borderMove.Height) * _scaleInternalHeight;
if (subX < 0.001) marginActualX = (ActualWidth - ImageWidth) / 2;
if (subY < 0.001) marginActualY = (ActualHeight - ImageHeight) / 2;
thickness = new Thickness(marginActualX, marginActualY, 0, 0);
}
ImageMargin = thickness;
_imgActualScale = tempScale;
_imgActualMargin = thickness;
BorderSmallShowSwitch();
_imgSmallMouseDownMargin = _borderMove.Margin;
MoveSmallImg(_imgSmallMouseDownMargin.Left, _imgSmallMouseDownMargin.Top);
}
/// <summary>
/// 旋转图片
/// </summary>
/// <param name="rotate"></param>
private void RotateImg(double rotate)
{
_imgActualRotate = rotate;
_isOblique = ((int) _imgActualRotate - 90) % 180 == 0;
ShowSmallImgInternal = false;
Init();
InitBorderSmall();
var animation = AnimationHelper.CreateAnimation(rotate);
animation.Completed += (s, e1) => { ImageRotate = rotate; };
animation.FillBehavior = FillBehavior.Stop;
BeginAnimation(ImageRotateProperty, animation);
}
/// <summary>
/// 移动图片
/// </summary>
private void MoveImg()
{
_imgCurrentPoint = Mouse.GetPosition(_gridMain);
ShowCloseButton = _imgCurrentPoint.Y < 200;
ShowBorderBottom = _imgCurrentPoint.Y > ActualHeight - 200;
if (_imgIsMouseLeftButtonDown)
{
var subX = _imgCurrentPoint.X - _imgMouseDownPoint.X;
var subY = _imgCurrentPoint.Y - _imgMouseDownPoint.Y;
var marginX = _imgMouseDownMargin.Left;
if (ImageWidth > ActualWidth)
{
marginX = _imgMouseDownMargin.Left + subX;
if (marginX >= 0)
marginX = 0;
else if (-marginX + ActualWidth >= ImageWidth) marginX = ActualWidth - ImageWidth;
_canMoveX = true;
}
var marginY = _imgMouseDownMargin.Top;
if (ImageHeight > ActualHeight)
{
marginY = _imgMouseDownMargin.Top + subY;
if (marginY >= 0)
marginY = 0;
else if (-marginY + ActualHeight >= ImageHeight) marginY = ActualHeight - ImageHeight;
_canMoveY = true;
}
ImageMargin = new Thickness(marginX, marginY, 0, 0);
_imgActualMargin = ImageMargin;
UpdateBorderSmall();
}
}
/// <summary>
/// 移动小图片
/// </summary>
private void MoveSmallImg()
{
if (_imgSmallIsMouseLeftButtonDown)
{
_imgSmallCurrentPoint = Mouse.GetPosition(_canvasSmallImg);
var subX = _imgSmallCurrentPoint.X - _imgSmallMouseDownPoint.X;
var subY = _imgSmallCurrentPoint.Y - _imgSmallMouseDownPoint.Y;
var marginX = _imgSmallMouseDownMargin.Left + subX;
var marginY = _imgSmallMouseDownMargin.Top + subY;
MoveSmallImg(marginX, marginY);
}
}
private void MoveSmallImg(double marginX, double marginY)
{
if (marginX < 0)
marginX = 0;
else if (marginX + _borderMove.Width >= _canvasSmallImg.Width)
marginX = _canvasSmallImg.Width - _borderMove.Width;
if (marginY < 0)
marginY = 0;
else if (marginY + _borderMove.Height >= _canvasSmallImg.Height)
marginY = _canvasSmallImg.Height - _borderMove.Height;
_borderMove.Margin = new Thickness(marginX, marginY, 0, 0);
var marginActualX = (ActualWidth - ImageWidth) / 2;
var marginActualY = (ActualHeight - ImageHeight) / 2;
if (_canMoveX) marginActualX = -marginX / _canvasSmallImg.Width * ImageWidth;
if (_canMoveY) marginActualY = -marginY / _canvasSmallImg.Height * ImageHeight;
ImageMargin = new Thickness(marginActualX, marginActualY, 0, 0);
_imgActualMargin = ImageMargin;
}
private void CanvasSmallImg_OnMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
_imgSmallMouseDownPoint = Mouse.GetPosition(_canvasSmallImg);
_imgSmallMouseDownMargin = _borderMove.Margin;
_imgSmallIsMouseLeftButtonDown = true;
e.Handled = true;
}
private void CanvasSmallImg_OnMouseLeftButtonUp(object sender, MouseButtonEventArgs e) => _imgSmallIsMouseLeftButtonDown = false;
private void CanvasSmallImg_OnMouseMove(object sender, MouseEventArgs e) => MoveSmallImg();
private void CanvasSmallImg_OnMouseLeave(object sender, MouseEventArgs e) => _imgSmallIsMouseLeftButtonDown = false;
}
}
@@ -0,0 +1,112 @@
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using HandyControl.Data;
using HandyControl.Interactivity;
namespace HandyControl.Controls
{
/// <inheritdoc cref="IDataInput" />
public class ComboBox : System.Windows.Controls.ComboBox, IDataInput
{
public ComboBox()
{
CommandBindings.Add(new CommandBinding(ControlCommands.Clear, (s, e) =>
{
ClearValue(SelectedValueProperty);
ClearValue(SelectedItemProperty);
ClearValue(SelectedIndexProperty);
ClearValue(TextProperty);
}));
}
protected override void OnSelectionChanged(SelectionChangedEventArgs e)
{
base.OnSelectionChanged(e);
VerifyData();
}
public Func<string, OperationResult<bool>> VerifyFunc { get; set; }
/// <summary>
/// 数据是否错误
/// </summary>
public static readonly DependencyProperty IsErrorProperty = DependencyProperty.Register(
"IsError", typeof(bool), typeof(ComboBox), new PropertyMetadata(ValueBoxes.FalseBox));
public bool IsError
{
get => (bool) GetValue(IsErrorProperty);
set => SetValue(IsErrorProperty, value);
}
/// <summary>
/// 错误提示
/// </summary>
public static readonly DependencyProperty ErrorStrProperty = DependencyProperty.Register(
"ErrorStr", typeof(string), typeof(ComboBox), new PropertyMetadata(default(string)));
public string ErrorStr
{
get => (string) GetValue(ErrorStrProperty);
set => SetValue(ErrorStrProperty, value);
}
public static readonly DependencyPropertyKey TextTypePropertyKey =
DependencyProperty.RegisterReadOnly("TextType", typeof(TextType), typeof(ComboBox),
new PropertyMetadata(default(TextType)));
/// <summary>
/// 文本类型
/// </summary>
public static readonly DependencyProperty TextTypeProperty = TextTypePropertyKey.DependencyProperty;
public TextType TextType
{
get => (TextType) GetValue(TextTypeProperty);
set => SetValue(TextTypeProperty, value);
}
/// <summary>
/// 是否显示清除按钮
/// </summary>
public static readonly DependencyProperty ShowClearButtonProperty = DependencyProperty.Register(
"ShowClearButton", typeof(bool), typeof(ComboBox), new PropertyMetadata(ValueBoxes.FalseBox));
public bool ShowClearButton
{
get => (bool) GetValue(ShowClearButtonProperty);
set => SetValue(ShowClearButtonProperty, value);
}
public virtual bool VerifyData()
{
OperationResult<bool> result;
if (VerifyFunc != null)
{
result = VerifyFunc.Invoke(Text);
}
else
{
if (!string.IsNullOrEmpty(Text))
{
result = OperationResult.Success();
}
else if (InfoElement.GetNecessary(this))
{
result = OperationResult.Failed(Properties.Langs.Lang.IsNecessary);
}
else
{
result = OperationResult.Success();
}
}
IsError = !result.Data;
ErrorStr = result.Message;
return result.Data;
}
}
}
@@ -0,0 +1,43 @@
using System;
using HandyControl.Data;
namespace HandyControl.Controls
{
/// <summary>
/// 表示可以成为一个数据输入控件
/// </summary>
public interface IDataInput
{
/// <summary>
/// 验证数据
/// </summary>
/// <returns></returns>
bool VerifyData();
/// <summary>
/// 数据验证委托
/// </summary>
Func<string, OperationResult<bool>> VerifyFunc { get; set; }
/// <summary>
/// 数据是否错误
/// </summary>
bool IsError { get; set; }
/// <summary>
/// 错误提示
/// </summary>
string ErrorStr { get; set; }
/// <summary>
/// 文本类型
/// </summary>
TextType TextType { get; set; }
/// <summary>
/// 是否显示清除按钮
/// </summary>
bool ShowClearButton { get; set; }
}
}
@@ -0,0 +1,400 @@
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using HandyControl.Data;
using HandyControl.Interactivity;
using HandyControl.Tools;
namespace HandyControl.Controls
{
/// <inheritdoc cref="IDataInput" />
/// <summary>
/// 数值选择控件
/// </summary>
[TemplatePart(Name = ElementTextBox, Type = typeof(TextBox))]
public class NumericUpDown : Control, IDataInput
{
#region Constants
private const string ElementTextBox = "PART_TextBox";
#endregion Constants
#region Data
private TextBox _textBox;
#endregion Data
public NumericUpDown()
{
CommandBindings.Add(new CommandBinding(ControlCommands.Prev, (s, e) =>
{
Value += Increment;
_textBox.Text = CurrentText;
_textBox.Select(_textBox.Text.Length, 0);
}));
CommandBindings.Add(new CommandBinding(ControlCommands.Next, (s, e) =>
{
Value -= Increment;
_textBox.Text = CurrentText;
_textBox.Select(_textBox.Text.Length, 0);
}));
CommandBindings.Add(new CommandBinding(ControlCommands.Clear, (s, e) =>
{
ClearValue(ValueProperty);
_textBox.Text = string.Empty;
}));
Loaded += (s, e) => OnApplyTemplate();
}
protected override void OnGotFocus(RoutedEventArgs e)
{
base.OnGotFocus(e);
if (_textBox != null)
{
_textBox?.Focus();
_textBox.Select(_textBox.Text.Length, 0);
}
}
public override void OnApplyTemplate()
{
if (_textBox != null)
{
_textBox.TextChanged -= TextBox_TextChanged;
_textBox.PreviewKeyDown -= TextBox_PreviewKeyDown;
_textBox.LostFocus -= TextBox_LostFocus;
}
base.OnApplyTemplate();
_textBox = GetTemplateChild(ElementTextBox) as TextBox;
if (_textBox != null)
{
_textBox.TextChanged += TextBox_TextChanged;
_textBox.PreviewKeyDown += TextBox_PreviewKeyDown;
_textBox.LostFocus += TextBox_LostFocus;
_textBox.Text = CurrentText;
}
}
private void TextBox_LostFocus(object sender, RoutedEventArgs e)
{
if (IsError) return;
_textBox.Text = CurrentText;
}
private void TextBox_PreviewKeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.Up)
{
Value += Increment;
_textBox.Text = CurrentText;
}
else if (e.Key == Key.Down)
{
Value -= Increment;
_textBox.Text = CurrentText;
}
}
private void TextBox_TextChanged(object sender, TextChangedEventArgs e)
{
if (!VerifyData()) return;
if (double.TryParse(_textBox.Text, out var value))
{
Value = value;
}
}
protected override void OnMouseWheel(MouseWheelEventArgs e)
{
base.OnMouseWheel(e);
if (_textBox.IsFocused)
{
Value += e.Delta > 0 ? Increment : -Increment;
_textBox.Text = CurrentText;
_textBox.Select(_textBox.Text.Length, 0);
e.Handled = true;
}
}
private string CurrentText => DecimalPlaces.HasValue
? Value.ToString($"#0.{new string('0', DecimalPlaces.Value)}")
: Value.ToString("#0");
protected virtual void OnValueChanged(FunctionEventArgs<double> e) => RaiseEvent(e);
/// <summary>
/// 值改变事件
/// </summary>
public static readonly RoutedEvent ValueChangedEvent =
EventManager.RegisterRoutedEvent("ValueChanged", RoutingStrategy.Bubble,
typeof(EventHandler<FunctionEventArgs<double>>), typeof(NumericUpDown));
/// <summary>
/// 值改变事件
/// </summary>
public event EventHandler<FunctionEventArgs<double>> ValueChanged
{
add => AddHandler(ValueChangedEvent, value);
remove => RemoveHandler(ValueChangedEvent, value);
}
/// <summary>
/// 当前值
/// </summary>
public static readonly DependencyProperty ValueProperty = DependencyProperty.Register(
"Value", typeof(double), typeof(NumericUpDown),
new FrameworkPropertyMetadata(default(double), FrameworkPropertyMetadataOptions.BindsTwoWayByDefault,
OnValueChanged, CoerceValue), ArithmeticHelper.IsValidDoubleValue);
private static void OnValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var ctl = (NumericUpDown) d;
var v = (double) e.NewValue;
ctl.OnValueChanged(new FunctionEventArgs<double>(ValueChangedEvent, ctl)
{
Info = v
});
}
private static object CoerceValue(DependencyObject d, object basevalue)
{
var ctl = (NumericUpDown)d;
var minimum = ctl.Minimum;
var num = (double)basevalue;
if (num < minimum)
{
ctl.Value = minimum;
return minimum;
}
var maximum = ctl.Maximum;
if (num > maximum)
{
ctl.Value = maximum;
}
var result = num > maximum ? maximum : num;
if (!ctl.DecimalPlaces.HasValue)
{
result = Math.Floor(result);
}
return result;
}
/// <summary>
/// 当前值
/// </summary>
public double Value
{
get => (double) GetValue(ValueProperty);
set => SetValue(ValueProperty, value);
}
/// <summary>
/// 最大值
/// </summary>
public static readonly DependencyProperty MaximumProperty = DependencyProperty.Register(
"Maximum", typeof(double), typeof(NumericUpDown), new PropertyMetadata(100.0, OnMaximumChanged, CoerceMaximum), ArithmeticHelper.IsValidDoubleValue);
private static void OnMaximumChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var ctl = (NumericUpDown)d;
ctl.CoerceValue(MinimumProperty);
ctl.CoerceValue(ValueProperty);
}
private static object CoerceMaximum(DependencyObject d, object basevalue)
{
var minimum = ((NumericUpDown)d).Minimum;
return (double)basevalue < minimum ? minimum : basevalue;
}
/// <summary>
/// 最大值
/// </summary>
public double Maximum
{
get => (double) GetValue(MaximumProperty);
set => SetValue(MaximumProperty, value);
}
/// <summary>
/// 最小值
/// </summary>
public static readonly DependencyProperty MinimumProperty = DependencyProperty.Register(
"Minimum", typeof(double), typeof(NumericUpDown), new PropertyMetadata(default(double), OnMinimumChanged, CoerceMinimum), ArithmeticHelper.IsValidDoubleValue);
private static void OnMinimumChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var ctl = (NumericUpDown)d;
ctl.CoerceValue(MaximumProperty);
ctl.CoerceValue(ValueProperty);
}
private static object CoerceMinimum(DependencyObject d, object basevalue)
{
var maximum = ((NumericUpDown)d).Maximum;
return (double)basevalue > maximum ? maximum : basevalue;
}
/// <summary>
/// 最小值
/// </summary>
public double Minimum
{
get => (double) GetValue(MinimumProperty);
set => SetValue(MinimumProperty, value);
}
/// <summary>
/// 指示每单击一下按钮时增加或减少的数量
/// </summary>
public static readonly DependencyProperty IncrementProperty = DependencyProperty.Register(
"Increment", typeof(double), typeof(NumericUpDown), new PropertyMetadata(ValueBoxes.Double1Box));
/// <summary>
/// 指示每单击一下按钮时增加或减少的数量
/// </summary>
public double Increment
{
get => (double) GetValue(IncrementProperty);
set => SetValue(IncrementProperty, value);
}
/// <summary>
/// 指示要显示的小数位数
/// </summary>
public static readonly DependencyProperty DecimalPlacesProperty = DependencyProperty.Register(
"DecimalPlaces", typeof(int?), typeof(NumericUpDown), new PropertyMetadata(default(int?)));
/// <summary>
/// 指示要显示的小数位数
/// </summary>
public int? DecimalPlaces
{
get => (int?) GetValue(DecimalPlacesProperty);
set => SetValue(DecimalPlacesProperty, value);
}
/// <summary>
/// 是否显示上下调值按钮
/// </summary>
internal static readonly DependencyProperty ShowUpDownButtonProperty = DependencyProperty.Register(
"ShowUpDownButton", typeof(bool), typeof(NumericUpDown), new PropertyMetadata(ValueBoxes.TrueBox));
/// <summary>
/// 是否显示上下调值按钮
/// </summary>
internal bool ShowUpDownButton
{
get => (bool) GetValue(ShowUpDownButtonProperty);
set => SetValue(ShowUpDownButtonProperty, value);
}
/// <summary>
/// 数据是否错误
/// </summary>
public static readonly DependencyProperty IsErrorProperty = DependencyProperty.Register(
"IsError", typeof(bool), typeof(NumericUpDown), new PropertyMetadata(ValueBoxes.FalseBox));
public bool IsError
{
get => (bool) GetValue(IsErrorProperty);
set => SetValue(IsErrorProperty, value);
}
/// <summary>
/// 错误提示
/// </summary>
public static readonly DependencyProperty ErrorStrProperty = DependencyProperty.Register(
"ErrorStr", typeof(string), typeof(NumericUpDown), new PropertyMetadata(default(string)));
public string ErrorStr
{
get => (string) GetValue(ErrorStrProperty);
set => SetValue(ErrorStrProperty, value);
}
public static readonly DependencyPropertyKey TextTypePropertyKey =
DependencyProperty.RegisterReadOnly("TextType", typeof(TextType), typeof(NumericUpDown),
new PropertyMetadata(default(TextType)));
/// <summary>
/// 文本类型
/// </summary>
public static readonly DependencyProperty TextTypeProperty = TextTypePropertyKey.DependencyProperty;
public TextType TextType
{
get => (TextType) GetValue(TextTypeProperty);
set => SetValue(TextTypeProperty, value);
}
/// <summary>
/// 是否显示清除按钮
/// </summary>
public static readonly DependencyProperty ShowClearButtonProperty = DependencyProperty.Register(
"ShowClearButton", typeof(bool), typeof(NumericUpDown), new PropertyMetadata(ValueBoxes.FalseBox));
public bool ShowClearButton
{
get => (bool) GetValue(ShowClearButtonProperty);
set => SetValue(ShowClearButtonProperty, value);
}
public Func<string, OperationResult<bool>> VerifyFunc { get; set; }
public virtual bool VerifyData()
{
OperationResult<bool> result;
if (VerifyFunc != null)
{
result = VerifyFunc.Invoke(_textBox.Text);
}
else
{
if (!string.IsNullOrEmpty(_textBox.Text))
{
if (double.TryParse(_textBox.Text, out var value))
{
if (value < Minimum || value > Maximum)
{
result = OperationResult.Failed(Properties.Langs.Lang.OutOfRange);
}
else
{
result = OperationResult.Success();
}
}
else
{
result = OperationResult.Failed(Properties.Langs.Lang.FormatError);
}
}
else if (InfoElement.GetNecessary(this))
{
result = OperationResult.Failed(Properties.Langs.Lang.IsNecessary);
}
else
{
result = OperationResult.Success();
}
}
IsError = !result.Data;
ErrorStr = result.Message;
return result.Data;
}
}
}
@@ -0,0 +1,173 @@
using System;
using System.ComponentModel;
using System.Security;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using HandyControl.Data;
using HandyControl.Interactivity;
using HandyControl.Properties.Langs;
namespace HandyControl.Controls
{
/// <inheritdoc cref="IDataInput" />
[TemplatePart(Name = ElementPasswordBox, Type = typeof(System.Windows.Controls.PasswordBox))]
public class PasswordBox : Control, IDataInput
{
private const string ElementPasswordBox = "PART_PasswordBox";
/// <summary>
/// 掩码字符
/// </summary>
public static readonly DependencyProperty PasswordCharProperty =
System.Windows.Controls.PasswordBox.PasswordCharProperty.AddOwner(typeof(PasswordBox),
new FrameworkPropertyMetadata('●'));
/// <summary>
/// 数据是否错误
/// </summary>
public static readonly DependencyProperty IsErrorProperty = DependencyProperty.Register(
"IsError", typeof(bool), typeof(PasswordBox), new PropertyMetadata(ValueBoxes.FalseBox));
/// <summary>
/// 错误提示
/// </summary>
public static readonly DependencyProperty ErrorStrProperty = DependencyProperty.Register(
"ErrorStr", typeof(string), typeof(PasswordBox), new PropertyMetadata(default(string)));
/// <summary>
/// 文本类型
/// </summary>
public static readonly DependencyProperty TextTypeProperty = DependencyProperty.Register(
"TextType", typeof(TextType), typeof(PasswordBox), new PropertyMetadata(default(TextType)));
/// <summary>
/// 是否显示清除按钮
/// </summary>
public static readonly DependencyProperty ShowClearButtonProperty = DependencyProperty.Register(
"ShowClearButton", typeof(bool), typeof(PasswordBox), new PropertyMetadata(ValueBoxes.FalseBox));
private string _password;
private char _passwordChar;
public PasswordBox()
{
CommandBindings.Add(new CommandBinding(ControlCommands.Clear, (s, e) => ActualPasswordBox.Clear()));
}
public System.Windows.Controls.PasswordBox ActualPasswordBox { get; set; }
[DefaultValue("")]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public string Password
{
get => ActualPasswordBox?.Password;
set
{
if (ActualPasswordBox == null)
{
_password = value;
return;
}
ActualPasswordBox.Password = value;
}
}
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public SecureString SecurePassword => ActualPasswordBox?.SecurePassword;
public char PasswordChar
{
get => (char) GetValue(PasswordCharProperty);
set => SetValue(PasswordCharProperty, value);
}
public Func<string, OperationResult<bool>> VerifyFunc { get; set; }
public bool IsError
{
get => (bool) GetValue(IsErrorProperty);
set => SetValue(IsErrorProperty, value);
}
public string ErrorStr
{
get => (string) GetValue(ErrorStrProperty);
set => SetValue(ErrorStrProperty, value);
}
public TextType TextType
{
get => (TextType) GetValue(TextTypeProperty);
set => SetValue(TextTypeProperty, value);
}
public bool ShowClearButton
{
get => (bool) GetValue(ShowClearButtonProperty);
set => SetValue(ShowClearButtonProperty, value);
}
public virtual bool VerifyData()
{
OperationResult<bool> result;
if (VerifyFunc != null)
{
result = VerifyFunc.Invoke(ActualPasswordBox.Password);
}
else
{
if (!string.IsNullOrEmpty(ActualPasswordBox.Password))
result = OperationResult.Success();
else if (InfoElement.GetNecessary(this))
result = OperationResult.Failed(Lang.IsNecessary);
else
result = OperationResult.Success();
}
IsError = !result.Data;
ErrorStr = result.Message;
return result.Data;
}
public override void OnApplyTemplate()
{
if (ActualPasswordBox != null)
ActualPasswordBox.PasswordChanged -= PasswordBox_PasswordChanged;
base.OnApplyTemplate();
ActualPasswordBox = GetTemplateChild(ElementPasswordBox) as System.Windows.Controls.PasswordBox;
if (ActualPasswordBox != null)
{
ActualPasswordBox.PasswordChanged += PasswordBox_PasswordChanged;
if (!string.IsNullOrEmpty(_password))
{
ActualPasswordBox.Password = _password;
_password = null;
}
if (_passwordChar != default(char))
{
ActualPasswordBox.PasswordChar = _passwordChar;
_passwordChar = default(char);
}
}
}
public void Paste() => ActualPasswordBox.Paste();
public void SelectAll() => ActualPasswordBox.SelectAll();
public void Clear() => ActualPasswordBox.SelectAll();
private void PasswordBox_PasswordChanged(object sender, RoutedEventArgs e)
{
VerifyData();
}
}
}
@@ -0,0 +1,193 @@
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Input;
using HandyControl.Data;
using HandyControl.Interactivity;
using HandyControl.Tools;
namespace HandyControl.Controls
{
[TemplatePart(Name = ElementTextBox, Type = typeof(WatermarkTextBox))]
public class SearchBar : Control, IDataInput
{
private const string ElementTextBox = "PART_TextBox";
private WatermarkTextBox _textBox;
public Func<string, OperationResult<bool>> VerifyFunc { get; set; }
public SearchBar()
{
CommandBindings.Add(new CommandBinding(ControlCommands.Clear, (s, e) => _textBox.Text = string.Empty));
CommandBindings.Add(new CommandBinding(ControlCommands.Search, (s, e) => OnSearchStarted()));
}
public static readonly RoutedEvent SearchStartedEvent =
EventManager.RegisterRoutedEvent("SearchStarted", RoutingStrategy.Direct,
typeof(EventHandler<FunctionEventArgs<string>>), typeof(SearchBar));
public event EventHandler<FunctionEventArgs<string>> SearchStarted
{
add => AddHandler(SearchStartedEvent, value);
remove => RemoveHandler(SearchStartedEvent, value);
}
public override void OnApplyTemplate()
{
if (_textBox != null)
{
_textBox.TextChanged -= TextBox_TextChanged;
}
base.OnApplyTemplate();
_textBox = GetTemplateChild(ElementTextBox) as WatermarkTextBox;
if (_textBox != null)
{
SetBinding(TextProperty, new Binding(TextProperty.Name)
{
Source = _textBox,
Mode = BindingMode.TwoWay,
UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged
});
_textBox.TextChanged += TextBox_TextChanged;
_textBox.KeyDown += TextBox_KeyDown;
}
}
private void TextBox_KeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.Enter)
{
OnSearchStarted();
}
}
private void TextBox_TextChanged(object sender, TextChangedEventArgs e)
{
if (IsRealTime)
{
OnSearchStarted();
}
VerifyData();
}
private void OnSearchStarted()
{
RaiseEvent(new FunctionEventArgs<string>(SearchStartedEvent, this)
{
Info = Text
});
}
public static readonly DependencyProperty TextProperty = DependencyProperty.Register(
"Text", typeof(string), typeof(SearchBar), new PropertyMetadata(default(string)));
public string Text
{
get => (string)GetValue(TextProperty);
set => SetValue(TextProperty, value);
}
/// <summary>
/// 数据是否错误
/// </summary>
public static readonly DependencyProperty IsErrorProperty = DependencyProperty.Register(
"IsError", typeof(bool), typeof(SearchBar), new PropertyMetadata(ValueBoxes.FalseBox));
public bool IsError
{
get => (bool)GetValue(IsErrorProperty);
set => SetValue(IsErrorProperty, value);
}
/// <summary>
/// 错误提示
/// </summary>
public static readonly DependencyProperty ErrorStrProperty = DependencyProperty.Register(
"ErrorStr", typeof(string), typeof(SearchBar), new PropertyMetadata(default(string)));
public string ErrorStr
{
get => (string)GetValue(ErrorStrProperty);
set => SetValue(ErrorStrProperty, value);
}
/// <summary>
/// 文本类型
/// </summary>
public static readonly DependencyProperty TextTypeProperty = DependencyProperty.Register(
"TextType", typeof(TextType), typeof(SearchBar), new PropertyMetadata(default(TextType)));
public TextType TextType
{
get => (TextType)GetValue(TextTypeProperty);
set => SetValue(TextTypeProperty, value);
}
/// <summary>
/// 是否显示清除按钮
/// </summary>
public static readonly DependencyProperty ShowClearButtonProperty = DependencyProperty.Register(
"ShowClearButton", typeof(bool), typeof(SearchBar), new PropertyMetadata(ValueBoxes.FalseBox));
public bool ShowClearButton
{
get => (bool)GetValue(ShowClearButtonProperty);
set => SetValue(ShowClearButtonProperty, value);
}
/// <summary>
/// 是否实时搜索
/// </summary>
public static readonly DependencyProperty IsRealTimeProperty = DependencyProperty.Register(
"IsRealTime", typeof(bool), typeof(SearchBar), new PropertyMetadata(ValueBoxes.FalseBox));
/// <summary>
/// 是否实时搜索
/// </summary>
public bool IsRealTime
{
get => (bool)GetValue(IsRealTimeProperty);
set => SetValue(IsRealTimeProperty, value);
}
public virtual bool VerifyData()
{
OperationResult<bool> result;
if (VerifyFunc != null)
{
result = VerifyFunc.Invoke(Text);
}
else
{
if (!string.IsNullOrEmpty(Text))
{
if (TextType != TextType.Common)
{
result = Text.IsKindOf(TextType) ? OperationResult.Success() : OperationResult.Failed(Properties.Langs.Lang.FormatError);
}
else
{
result = OperationResult.Success();
}
}
else if (InfoElement.GetNecessary(this))
{
result = OperationResult.Failed(Properties.Langs.Lang.IsNecessary);
}
else
{
result = OperationResult.Success();
}
}
IsError = !result.Data;
ErrorStr = result.Message;
return result.Data;
}
}
}
@@ -0,0 +1,114 @@
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using HandyControl.Data;
using HandyControl.Interactivity;
using HandyControl.Tools;
namespace HandyControl.Controls
{
/// <inheritdoc cref="IDataInput" />
public class TextBox : System.Windows.Controls.TextBox, IDataInput
{
public TextBox()
{
CommandBindings.Add(new CommandBinding(ControlCommands.Clear, (s, e) =>
{
ClearValue(TextProperty);
}));
}
protected override void OnTextChanged(TextChangedEventArgs e)
{
base.OnTextChanged(e);
VerifyData();
}
public Func<string, OperationResult<bool>> VerifyFunc { get; set; }
/// <summary>
/// 数据是否错误
/// </summary>
public static readonly DependencyProperty IsErrorProperty = DependencyProperty.Register(
"IsError", typeof(bool), typeof(TextBox), new PropertyMetadata(ValueBoxes.FalseBox));
public bool IsError
{
get => (bool) GetValue(IsErrorProperty);
set => SetValue(IsErrorProperty, value);
}
/// <summary>
/// 错误提示
/// </summary>
public static readonly DependencyProperty ErrorStrProperty = DependencyProperty.Register(
"ErrorStr", typeof(string), typeof(TextBox), new PropertyMetadata(default(string)));
public string ErrorStr
{
get => (string) GetValue(ErrorStrProperty);
set => SetValue(ErrorStrProperty, value);
}
/// <summary>
/// 文本类型
/// </summary>
public static readonly DependencyProperty TextTypeProperty = DependencyProperty.Register(
"TextType", typeof(TextType), typeof(TextBox), new PropertyMetadata(default(TextType)));
public TextType TextType
{
get => (TextType) GetValue(TextTypeProperty);
set => SetValue(TextTypeProperty, value);
}
/// <summary>
/// 是否显示清除按钮
/// </summary>
public static readonly DependencyProperty ShowClearButtonProperty = DependencyProperty.Register(
"ShowClearButton", typeof(bool), typeof(TextBox), new PropertyMetadata(ValueBoxes.FalseBox));
public bool ShowClearButton
{
get => (bool) GetValue(ShowClearButtonProperty);
set => SetValue(ShowClearButtonProperty, value);
}
public virtual bool VerifyData()
{
OperationResult<bool> result;
if (VerifyFunc != null)
{
result = VerifyFunc.Invoke(Text);
}
else
{
if (!string.IsNullOrEmpty(Text))
{
if (TextType != TextType.Common)
{
result = Text.IsKindOf(TextType) ? OperationResult.Success() : OperationResult.Failed(Properties.Langs.Lang.FormatError);
}
else
{
result = OperationResult.Success();
}
}
else if (InfoElement.GetNecessary(this))
{
result = OperationResult.Failed(Properties.Langs.Lang.IsNecessary);
}
else
{
result = OperationResult.Success();
}
}
IsError = !result.Data;
ErrorStr = result.Message;
return result.Data;
}
}
}
@@ -0,0 +1,77 @@
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
namespace HandyControl.Controls
{
[TemplatePart(Name = ElementContentName, Type = typeof(ContentControl))]
public class WatermarkTextBox : TextBox
{
#region Constants
private const string ElementContentName = "PART_Watermark";
#endregion Constants
#region Data
private ContentControl _elementContent;
#endregion Data
#region Public Properties
#region Watermark
public static readonly DependencyProperty WatermarkProperty = DependencyProperty.Register(
"Watermark", typeof(object), typeof(WatermarkTextBox), new PropertyMetadata(default(object), OnWatermarkChanged));
private static void OnWatermarkChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var ctl = (WatermarkTextBox) d;
ctl.OnWatermarkChanged();
}
public object Watermark
{
get => GetValue(WatermarkProperty);
set => SetValue(WatermarkProperty, value);
}
#endregion Watermark
#endregion Public Properties
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
_elementContent = GetTemplateChild(ElementContentName) as ContentControl;
_elementContent?.SetBinding(ContentControl.ContentProperty, new Binding(WatermarkProperty.Name) {Source = this});
OnWatermarkChanged();
}
private void OnWatermarkChanged()
{
if (_elementContent == null) return;
if (Watermark is Control watermarkControl)
{
watermarkControl.IsTabStop = false;
watermarkControl.IsHitTestVisible = false;
}
}
protected override void OnGotFocus(RoutedEventArgs e)
{
base.OnGotFocus(e);
if (IsEnabled)
{
if (!string.IsNullOrEmpty(Text))
{
Select(0, Text.Length);
}
}
}
}
}
@@ -0,0 +1,64 @@
using System.Windows;
using System.Windows.Controls;
namespace HandyControl.Controls
{
internal class InternalCanvas : Canvas
{
internal static readonly DependencyProperty OrientationProperty = DependencyProperty.Register(
"Orientation", typeof(Orientation), typeof(InternalCanvas), new PropertyMetadata(default(Orientation)));
internal Orientation Orientation
{
get => (Orientation) GetValue(OrientationProperty);
set => SetValue(OrientationProperty, value);
}
protected override Size ArrangeOverride(Size arrangeSize)
{
foreach (UIElement internalChild in InternalChildren)
{
if (internalChild == null) continue;
var x = 0.0;
var y = 0.0;
if (Orientation == Orientation.Horizontal)
{
x = (arrangeSize.Width - internalChild.DesiredSize.Width) / 2;
var top = GetTop(internalChild);
if (!double.IsNaN(top))
{
y = top;
}
else
{
var bottom = GetBottom(internalChild);
if (!double.IsNaN(bottom))
y = arrangeSize.Height - internalChild.DesiredSize.Height - bottom;
}
}
else
{
y = (arrangeSize.Height - internalChild.DesiredSize.Height) / 2;
var left = GetLeft(internalChild);
if (!double.IsNaN(left))
{
x = left;
}
else
{
var right = GetRight(internalChild);
if (!double.IsNaN(right))
x = arrangeSize.Width - internalChild.DesiredSize.Width - right;
}
}
internalChild.Arrange(new Rect(new Point(x, y), internalChild.DesiredSize));
}
return arrangeSize;
}
}
}
@@ -0,0 +1,136 @@
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using HandyControl.Data;
namespace HandyControl.Controls
{
public abstract class LoadingBase : ContentControl
{
protected Storyboard Storyboard;
public static readonly DependencyProperty IsRunningProperty = DependencyProperty.Register(
"IsRunning", typeof(bool), typeof(LoadingBase), new PropertyMetadata(ValueBoxes.TrueBox, (o, args) =>
{
var ctl = (LoadingBase) o;
var v = (bool) args.NewValue;
if (v)
{
ctl.Storyboard?.Resume();
}
else
{
ctl.Storyboard?.Pause();
}
}));
public bool IsRunning
{
get => (bool) GetValue(IsRunningProperty);
set => SetValue(IsRunningProperty, value);
}
public static readonly DependencyProperty DotCountProperty = DependencyProperty.Register(
"DotCount", typeof(int), typeof(LoadingBase),
new FrameworkPropertyMetadata(5, FrameworkPropertyMetadataOptions.AffectsRender));
public static readonly DependencyProperty DotIntervalProperty = DependencyProperty.Register(
"DotInterval", typeof(double), typeof(LoadingBase),
new FrameworkPropertyMetadata(10.0, FrameworkPropertyMetadataOptions.AffectsRender));
public static readonly DependencyProperty DotBorderBrushProperty = DependencyProperty.Register(
"DotBorderBrush", typeof(Brush), typeof(LoadingBase),
new FrameworkPropertyMetadata(default(Brush), FrameworkPropertyMetadataOptions.AffectsRender));
public static readonly DependencyProperty DotBorderThicknessProperty = DependencyProperty.Register(
"DotBorderThickness", typeof(double), typeof(LoadingBase),
new FrameworkPropertyMetadata(ValueBoxes.Double0Box, FrameworkPropertyMetadataOptions.AffectsRender));
public static readonly DependencyProperty DotDiameterProperty = DependencyProperty.Register(
"DotDiameter", typeof(double), typeof(LoadingBase),
new FrameworkPropertyMetadata(6.0, FrameworkPropertyMetadataOptions.AffectsRender));
public static readonly DependencyProperty DotSpeedProperty = DependencyProperty.Register(
"DotSpeed", typeof(double), typeof(LoadingBase),
new FrameworkPropertyMetadata(4.0, FrameworkPropertyMetadataOptions.AffectsRender));
public static readonly DependencyProperty DotDelayTimeProperty = DependencyProperty.Register(
"DotDelayTime", typeof(double), typeof(LoadingBase),
new FrameworkPropertyMetadata(80.0, FrameworkPropertyMetadataOptions.AffectsRender));
protected readonly Canvas PrivateCanvas = new Canvas
{
ClipToBounds = true
};
protected LoadingBase()
{
Content = PrivateCanvas;
}
public int DotCount
{
get => (int) GetValue(DotCountProperty);
set => SetValue(DotCountProperty, value);
}
public double DotInterval
{
get => (double) GetValue(DotIntervalProperty);
set => SetValue(DotIntervalProperty, value);
}
public Brush DotBorderBrush
{
get => (Brush) GetValue(DotBorderBrushProperty);
set => SetValue(DotBorderBrushProperty, value);
}
public double DotBorderThickness
{
get => (double) GetValue(DotBorderThicknessProperty);
set => SetValue(DotBorderThicknessProperty, value);
}
public double DotDiameter
{
get => (double) GetValue(DotDiameterProperty);
set => SetValue(DotDiameterProperty, value);
}
public double DotSpeed
{
get => (double) GetValue(DotSpeedProperty);
set => SetValue(DotSpeedProperty, value);
}
public double DotDelayTime
{
get => (double) GetValue(DotDelayTimeProperty);
set => SetValue(DotDelayTimeProperty, value);
}
protected abstract void UpdateDots();
protected override void OnRender(DrawingContext drawingContext)
{
base.OnRender(drawingContext);
UpdateDots();
}
protected Ellipse CreateEllipse(int index)
{
var ellipse = new Ellipse();
ellipse.SetBinding(WidthProperty, new Binding(DotDiameterProperty.Name) {Source = this});
ellipse.SetBinding(HeightProperty, new Binding(DotDiameterProperty.Name) {Source = this});
ellipse.SetBinding(Shape.FillProperty, new Binding(ForegroundProperty.Name) {Source = this});
ellipse.SetBinding(Shape.StrokeThicknessProperty, new Binding(DotBorderThicknessProperty.Name) {Source = this});
ellipse.SetBinding(Shape.StrokeProperty, new Binding(DotBorderBrushProperty.Name) {Source = this});
return ellipse;
}
}
}
@@ -0,0 +1,186 @@
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Media;
using System.Windows.Media.Animation;
namespace HandyControl.Controls
{
public class LoadingCircle : LoadingBase
{
public static readonly DependencyProperty DotOffSetProperty = DependencyProperty.Register(
"DotOffSet", typeof(double), typeof(LoadingCircle),
new FrameworkPropertyMetadata(20.0, FrameworkPropertyMetadataOptions.AffectsRender));
public double DotOffSet
{
get => (double) GetValue(DotOffSetProperty);
set => SetValue(DotOffSetProperty, value);
}
public static readonly DependencyProperty NeedHiddenProperty = DependencyProperty.Register(
"NeedHidden", typeof(bool), typeof(LoadingCircle),
new FrameworkPropertyMetadata(true, FrameworkPropertyMetadataOptions.AffectsRender));
public bool NeedHidden
{
get => (bool) GetValue(NeedHiddenProperty);
set => SetValue(NeedHiddenProperty, value);
}
static LoadingCircle()
{
DotSpeedProperty.OverrideMetadata(typeof(LoadingCircle),
new FrameworkPropertyMetadata(6.0, FrameworkPropertyMetadataOptions.AffectsRender));
DotDelayTimeProperty.OverrideMetadata(typeof(LoadingCircle),
new FrameworkPropertyMetadata(220.0, FrameworkPropertyMetadataOptions.AffectsRender));
}
protected sealed override void UpdateDots()
{
if (DotCount < 1) return;
PrivateCanvas.Children.Clear();
//定义动画
Storyboard = new Storyboard
{
RepeatBehavior = RepeatBehavior.Forever
};
//创建圆点
for (var i = 0; i < DotCount; i++)
{
var border = CreateBorder(i);
var framesMove = new DoubleAnimationUsingKeyFrames
{
BeginTime = TimeSpan.FromMilliseconds(DotDelayTime * i)
};
var subAngle = -DotInterval * i;
//开始位置
var frame0 = new LinearDoubleKeyFrame
{
Value = 0 + subAngle,
KeyTime = KeyTime.FromTimeSpan(TimeSpan.Zero)
};
//开始位置到第一次匀速开始
var frame1 = new LinearDoubleKeyFrame
{
Value = 180 + subAngle,
KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromSeconds(DotSpeed * (0.75 / 7)))
};
//第一次匀速开始到第一次匀速结束
var frame2 = new LinearDoubleKeyFrame
{
Value = 180 + DotOffSet + subAngle,
KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromSeconds(DotSpeed * (2.75 / 7)))
};
//第一次匀速结束到匀加速结束
var frame3 = new LinearDoubleKeyFrame
{
Value = 360 + subAngle,
KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromSeconds(DotSpeed * (3.5 / 7)))
};
//匀加速结束到匀减速结束
var frame4 = new LinearDoubleKeyFrame
{
Value = 540 + subAngle,
KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromSeconds(DotSpeed * (4.25 / 7)))
};
//匀减速结束到第二次匀速结束
var frame5 = new LinearDoubleKeyFrame
{
Value = 540 + DotOffSet + subAngle,
KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromSeconds(DotSpeed * (6.25 / 7)))
};
//第二次匀速结束到匀加速结束
var frame6 = new LinearDoubleKeyFrame
{
Value = 720 + subAngle,
KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromSeconds(DotSpeed))
};
framesMove.KeyFrames.Add(frame0);
framesMove.KeyFrames.Add(frame1);
framesMove.KeyFrames.Add(frame2);
framesMove.KeyFrames.Add(frame3);
framesMove.KeyFrames.Add(frame4);
framesMove.KeyFrames.Add(frame5);
framesMove.KeyFrames.Add(frame6);
Storyboard.SetTarget(framesMove, border);
Storyboard.SetTargetProperty(framesMove, new PropertyPath("(UIElement.RenderTransform).(TransformGroup.Children)[0].(RotateTransform.Angle)"));
Storyboard.Children.Add(framesMove);
if (NeedHidden)
{
//隐藏一段时间
var frame7 = new DiscreteObjectKeyFrame
{
Value = Visibility.Collapsed,
KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromSeconds(DotSpeed))
};
var frame8 = new DiscreteObjectKeyFrame
{
Value = Visibility.Collapsed,
KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromSeconds(DotSpeed + 0.4))
};
var frame9 = new DiscreteObjectKeyFrame
{
Value = Visibility.Visible,
KeyTime = KeyTime.FromTimeSpan(TimeSpan.Zero)
};
var framesVisibility = new ObjectAnimationUsingKeyFrames
{
BeginTime = TimeSpan.FromMilliseconds(DotDelayTime * i)
};
framesVisibility.KeyFrames.Add(frame9);
framesVisibility.KeyFrames.Add(frame7);
framesVisibility.KeyFrames.Add(frame8);
Storyboard.SetTarget(framesVisibility, border);
Storyboard.SetTargetProperty(framesVisibility, new PropertyPath("(UIElement.Visibility)"));
Storyboard.Children.Add(framesVisibility);
}
PrivateCanvas.Children.Add(border);
}
Storyboard.Begin();
}
private Border CreateBorder(int index)
{
var ellipse = CreateEllipse(index);
ellipse.HorizontalAlignment = HorizontalAlignment.Center;
ellipse.VerticalAlignment = VerticalAlignment.Bottom;
var rt = new RotateTransform
{
Angle = -DotInterval * index
};
var myTransGroup = new TransformGroup();
myTransGroup.Children.Add(rt);
var border = new Border
{
RenderTransformOrigin = new Point(0.5, 0.5),
RenderTransform = myTransGroup,
Child = ellipse,
Visibility = NeedHidden ? Visibility.Collapsed : Visibility.Visible
};
border.SetBinding(WidthProperty, new Binding("Width") { Source = this });
border.SetBinding(HeightProperty, new Binding("Height") { Source = this });
return border;
}
}
}
@@ -0,0 +1,98 @@
using System;
using System.Windows;
using System.Windows.Data;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
namespace HandyControl.Controls
{
public class LoadingLine : LoadingBase
{
private const double MoveLength = 80;
private const double UniformScale = .6;
public LoadingLine()
{
SetBinding(HeightProperty, new Binding("DotDiameter") { Source = this });
}
protected sealed override void UpdateDots()
{
if (DotCount < 1) return;
PrivateCanvas.Children.Clear();
//计算相关尺寸
var centerWidth = DotDiameter * DotCount + DotInterval * (DotCount - 1) + MoveLength;
var speedDownLength = (ActualWidth - MoveLength) / 2;
var speedUniformLength = centerWidth / 2;
//定义动画
Storyboard = new Storyboard
{
RepeatBehavior = RepeatBehavior.Forever
};
//创建圆点
for (var i = 0; i < DotCount; i++)
{
var ellipse = CreateEllipse(i);
var frames = new ThicknessAnimationUsingKeyFrames
{
BeginTime = TimeSpan.FromMilliseconds(DotDelayTime * i)
};
//开始位置
var frame0 = new LinearThicknessKeyFrame
{
Value = new Thickness(ellipse.Margin.Left, 0, 0, 0),
KeyTime = KeyTime.FromTimeSpan(TimeSpan.Zero)
};
//开始位置到匀速开始
var frame1 = new LinearThicknessKeyFrame
{
Value = new Thickness(speedDownLength + ellipse.Margin.Left, 0, 0, 0),
KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromSeconds(DotSpeed * (1 - UniformScale)/2))
};
//匀速开始到匀速结束
var frame2 = new LinearThicknessKeyFrame
{
Value = new Thickness(speedDownLength + speedUniformLength + ellipse.Margin.Left, 0, 0, 0),
KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromSeconds(DotSpeed * (1 + UniformScale) / 2))
};
//匀速结束到匀加速结束
var frame3 = new LinearThicknessKeyFrame
{
Value = new Thickness(ActualWidth + ellipse.Margin.Left + speedUniformLength, 0, 0, 0),
KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromSeconds(DotSpeed))
};
frames.KeyFrames.Add(frame0);
frames.KeyFrames.Add(frame1);
frames.KeyFrames.Add(frame2);
frames.KeyFrames.Add(frame3);
Storyboard.SetTarget(frames, ellipse);
Storyboard.SetTargetProperty(frames, new PropertyPath(MarginProperty));
Storyboard.Children.Add(frames);
PrivateCanvas.Children.Add(ellipse);
}
Storyboard.Begin();
}
private new Ellipse CreateEllipse(int index)
{
var ellipse = base.CreateEllipse(index);
ellipse.HorizontalAlignment = HorizontalAlignment.Left;
ellipse.VerticalAlignment = VerticalAlignment.Top;
ellipse.Margin = new Thickness(-(DotInterval + DotDiameter) * index, 0, 0, 0);
return ellipse;
}
}
}
@@ -0,0 +1,189 @@
using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using HandyControl.Data;
namespace HandyControl.Controls
{
public class AnimationPath : Shape
{
private bool _isLoaded;
/// <summary>
/// 故事版
/// </summary>
private Storyboard _storyboard;
/// <summary>
/// 路径
/// </summary>
public static readonly DependencyProperty DataProperty = DependencyProperty.Register(nameof(Data),
typeof(Geometry), typeof(AnimationPath), new FrameworkPropertyMetadata(null,
OnPropertiesChanged));
private static void OnPropertiesChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is AnimationPath path)
{
path.UpdatePath();
}
}
/// <summary>
/// 路径
/// </summary>
public Geometry Data
{
get => (Geometry)GetValue(DataProperty);
set => SetValue(DataProperty, value);
}
protected override Geometry DefiningGeometry => Data ?? Geometry.Empty;
/// <summary>
/// 路径长度
/// </summary>
public static readonly DependencyProperty PathLengthProperty = DependencyProperty.Register(
"PathLength", typeof(double), typeof(AnimationPath), new FrameworkPropertyMetadata(ValueBoxes.Double0Box,
OnPropertiesChanged));
/// <summary>
/// 路径长度
/// </summary>
public double PathLength
{
get => (double)GetValue(PathLengthProperty);
set => SetValue(PathLengthProperty, value);
}
/// <summary>
/// 动画间隔时间
/// </summary>
public static readonly DependencyProperty DurationProperty = DependencyProperty.Register(
"Duration", typeof(Duration), typeof(AnimationPath), new FrameworkPropertyMetadata(new Duration(TimeSpan.FromSeconds(2)),
OnPropertiesChanged));
/// <summary>
/// 动画间隔时间
/// </summary>
public Duration Duration
{
get => (Duration)GetValue(DurationProperty);
set => SetValue(DurationProperty, value);
}
public static readonly DependencyProperty IsPlayingProperty = DependencyProperty.Register(
"IsPlaying", typeof(bool), typeof(AnimationPath), new FrameworkPropertyMetadata(true, (o, args) =>
{
var ctl = (AnimationPath)o;
var v = (bool)args.NewValue;
if (v)
{
ctl.UpdatePath();
}
else
{
ctl._storyboard?.Pause();
}
}));
/// <summary>
/// 是否正在播放动画
/// </summary>
public bool IsPlaying
{
get => (bool)GetValue(IsPlayingProperty);
set => SetValue(IsPlayingProperty, value);
}
public static readonly DependencyProperty RepeatBehaviorProperty = DependencyProperty.Register(
"RepeatBehavior", typeof(RepeatBehavior), typeof(AnimationPath), new PropertyMetadata(RepeatBehavior.Forever));
/// <summary>
/// 动画重复行为
/// </summary>
public RepeatBehavior RepeatBehavior
{
get => (RepeatBehavior)GetValue(RepeatBehaviorProperty);
set => SetValue(RepeatBehaviorProperty, value);
}
static AnimationPath()
{
StretchProperty.AddOwner(typeof(AnimationPath), new FrameworkPropertyMetadata(Stretch.Uniform,
OnPropertiesChanged));
StrokeThicknessProperty.AddOwner(typeof(AnimationPath), new FrameworkPropertyMetadata(1.0,
OnPropertiesChanged));
}
public AnimationPath()
{
Loaded += (s, e) =>
{
if (_isLoaded) return;
UpdatePath();
_isLoaded = true;
};
}
/// <summary>
/// 动画完成事件
/// </summary>
public static readonly RoutedEvent CompletedEvent =
EventManager.RegisterRoutedEvent("Completed", RoutingStrategy.Bubble,
typeof(EventHandler), typeof(AnimationPath));
/// <summary>
/// 动画完成事件
/// </summary>
public event EventHandler Completed
{
add => AddHandler(CompletedEvent, value);
remove => RemoveHandler(CompletedEvent, value);
}
private void UpdatePath()
{
if (!Duration.HasTimeSpan || !IsPlaying) return;
StrokeDashOffset = PathLength;
StrokeDashArray = new DoubleCollection(new List<double>
{
PathLength,
PathLength
});
//定义动画
_storyboard = new Storyboard
{
RepeatBehavior = RepeatBehavior
};
_storyboard.Completed += (s, e) => RaiseEvent(new RoutedEventArgs(CompletedEvent));
var frames = new DoubleAnimationUsingKeyFrames();
//开始位置
var frame0 = new LinearDoubleKeyFrame
{
Value = PathLength,
KeyTime = KeyTime.FromTimeSpan(TimeSpan.Zero)
};
//结束位置
var frame1 = new LinearDoubleKeyFrame
{
Value = -PathLength,
KeyTime = KeyTime.FromTimeSpan(Duration.TimeSpan)
};
frames.KeyFrames.Add(frame0);
frames.KeyFrames.Add(frame1);
Storyboard.SetTarget(frames, this);
Storyboard.SetTargetProperty(frames, new PropertyPath(StrokeDashOffsetProperty));
_storyboard.Children.Add(frames);
_storyboard.Begin();
}
}
}
@@ -0,0 +1,301 @@
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Input;
using System.Windows.Markup;
using System.Windows.Media;
using System.Windows.Threading;
using HandyControl.Data;
using HandyControl.Interactivity;
using HandyControl.Tools;
namespace HandyControl.Controls
{
/// <summary>
/// 轮播控件
/// </summary>
[DefaultProperty("Items")]
[ContentProperty("Items")]
[TemplatePart(Name = ElementPanelPage, Type = typeof(Panel))]
[TemplatePart(Name = ElementItemsControl, Type = typeof(ItemsPresenter))]
public class Carousel : ListBox
{
#region Constants
private const string ElementPanelPage = "PART_PanelPage";
private const string ElementItemsControl = "PART_ItemsControl";
#endregion Constants
#region Data
private Panel _panelPage;
private bool _appliedTemplate;
private ItemsPresenter _itemsControl;
private int _pageIndex = -1;
private Button _selectedButton;
private DispatcherTimer _updateTimer;
private readonly List<double> _widthList = new List<double>();
#endregion Data
public override void OnApplyTemplate()
{
_appliedTemplate = false;
_panelPage?.RemoveHandler(ButtonBase.ClickEvent, new RoutedEventHandler(ButtonPages_OnClick));
base.OnApplyTemplate();
_itemsControl = GetTemplateChild(ElementItemsControl) as ItemsPresenter;
_panelPage = GetTemplateChild(ElementPanelPage) as Panel;
CheckNull();
_panelPage.AddHandler(ButtonBase.ClickEvent, new RoutedEventHandler(ButtonPages_OnClick));
_appliedTemplate = true;
Update();
}
private void Update()
{
TimerSwitch(AutoRun);
UpdatePageButtons(_pageIndex);
}
private void CheckNull()
{
if (_itemsControl == null || _panelPage == null) throw new Exception();
}
public static readonly DependencyProperty AutoRunProperty = DependencyProperty.Register(
"AutoRun", typeof(bool), typeof(Carousel), new PropertyMetadata(ValueBoxes.FalseBox, (o, args) =>
{
var ctl = (Carousel)o;
ctl.TimerSwitch((bool)args.NewValue);
}));
public static readonly DependencyProperty IntervalProperty = DependencyProperty.Register(
"Interval", typeof(TimeSpan), typeof(Carousel), new PropertyMetadata(TimeSpan.FromSeconds(2)));
public static readonly DependencyProperty ExtendWidthProperty = DependencyProperty.Register(
"ExtendWidth", typeof(double), typeof(Carousel), new PropertyMetadata(ValueBoxes.Double0Box));
public double ExtendWidth
{
get => (double)GetValue(ExtendWidthProperty);
set => SetValue(ExtendWidthProperty, value);
}
public static readonly DependencyProperty IsCenterProperty = DependencyProperty.Register(
"IsCenter", typeof(bool), typeof(Carousel), new PropertyMetadata(ValueBoxes.FalseBox));
public bool IsCenter
{
get => (bool)GetValue(IsCenterProperty);
set => SetValue(IsCenterProperty, value);
}
public Carousel()
{
CommandBindings.Add(new CommandBinding(ControlCommands.Prev, ButtonPrev_OnClick));
CommandBindings.Add(new CommandBinding(ControlCommands.Next, ButtonNext_OnClick));
CommandBindings.Add(new CommandBinding(ControlCommands.Selected, ButtonPages_OnClick));
Loaded += (s, e) => UpdatePageButtons();
}
protected override void OnItemsChanged(NotifyCollectionChangedEventArgs e)
{
base.OnItemsChanged(e);
UpdatePageButtons();
}
/// <summary>
/// 是否自动跳转
/// </summary>
public bool AutoRun
{
get => (bool)GetValue(AutoRunProperty);
set => SetValue(AutoRunProperty, value);
}
/// <summary>
/// 跳转时间间隔
/// </summary>
public TimeSpan Interval
{
get => (TimeSpan)GetValue(IntervalProperty);
set => SetValue(IntervalProperty, value);
}
/// <summary>
/// 页码
/// </summary>
public int PageIndex
{
get => _pageIndex;
set
{
if (Items.Count == 0) return;
if (_pageIndex == value) return;
if (value < 0)
_pageIndex = Items.Count - 1;
else if (value >= Items.Count)
_pageIndex = 0;
else
_pageIndex = value;
UpdatePageButtons(_pageIndex);
}
}
/// <summary>
/// 计时器开关
/// </summary>
private void TimerSwitch(bool run)
{
if (!_appliedTemplate) return;
if (_updateTimer != null)
{
_updateTimer.Tick -= UpdateTimer_Tick;
_updateTimer.Stop();
_updateTimer = null;
}
if (!run) return;
_updateTimer = new DispatcherTimer
{
Interval = Interval
};
_updateTimer.Tick += UpdateTimer_Tick;
_updateTimer.Start();
}
private void UpdateTimer_Tick(object sender, EventArgs e)
{
if (IsMouseOver) return;
PageIndex++;
}
/// <summary>
/// 更新页按钮
/// </summary>
public void UpdatePageButtons(int index = -1)
{
if (!_appliedTemplate) return;
var count = Items.Count;
_widthList.Clear();
_widthList.Add(0);
var width = .0;
foreach (FrameworkElement item in Items)
{
item.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
width += item.DesiredSize.Width;
_widthList.Add(width);
}
_itemsControl.Width = _widthList.Last() + ExtendWidth;
_panelPage.Children.Clear();
for (var i = 0; i < count; i++)
{
_panelPage.Children.Add(CreatePateButton());
}
if (index == -1)
{
if (count > 0)
{
var button = _panelPage.Children[0];
button.RaiseEvent(new RoutedEventArgs(ButtonBase.ClickEvent, button));
}
}
else if (index >= 0 && index < count)
{
var button = _panelPage.Children[index];
button.RaiseEvent(new RoutedEventArgs(ButtonBase.ClickEvent, button));
UpdateItemsPosition();
}
}
/// <summary>
/// 更新项的位置
/// </summary>
private void UpdateItemsPosition()
{
if (!_appliedTemplate) return;
if (Items.Count == 0) return;
if (!IsCenter)
{
_itemsControl.BeginAnimation(MarginProperty,
AnimationHelper.CreateAnimation(new Thickness(-_widthList[PageIndex], 0, 0, 0)));
}
else
{
var ctl = (FrameworkElement)Items[PageIndex];
var ctlWidth = ctl.DesiredSize.Width;
_itemsControl.BeginAnimation(MarginProperty,
AnimationHelper.CreateAnimation(
new Thickness(-_widthList[PageIndex] + (ActualWidth - ctlWidth) / 2, 0, 0, 0)));
}
}
protected override void OnRenderSizeChanged(SizeChangedInfo sizeInfo)
{
base.OnRenderSizeChanged(sizeInfo);
UpdateItemsPosition();
}
/// <summary>
/// 创建页按钮
/// </summary>
/// <returns></returns>
private Button CreatePateButton()
{
var button = new Button
{
Style = ResourceHelper.GetResource<Style>(ResourceToken.ButtonCustom),
Content = new Border
{
Width = 10,
Height = 10,
CornerRadius = new CornerRadius(5),
Background = Brushes.White,
Margin = new Thickness(5, 0, 5, 0),
BorderThickness = new Thickness(1),
BorderBrush = ResourceHelper.GetResource<Brush>(ResourceToken.PrimaryBrush)
}
};
return button;
}
private void ButtonPages_OnClick(object sender, RoutedEventArgs e)
{
if (_selectedButton != null && _selectedButton.Content is Border borderOri)
borderOri.Background = Brushes.White;
_selectedButton = e.OriginalSource as Button;
if (_selectedButton != null && _selectedButton.Content is Border border)
border.Background = ResourceHelper.GetResource<Brush>(ResourceToken.PrimaryBrush);
var index = _panelPage.Children.IndexOf(_selectedButton);
if (index != -1)
PageIndex = index;
}
private void ButtonPrev_OnClick(object sender, RoutedEventArgs e) => PageIndex--;
private void ButtonNext_OnClick(object sender, RoutedEventArgs e) => PageIndex++;
}
}
@@ -0,0 +1,91 @@
using System.Windows;
using System.Windows.Controls.Primitives;
using HandyControl.Data;
using HandyControl.Expression.Shapes;
namespace HandyControl.Controls
{
[TemplatePart(Name = IndicatorTemplateName, Type = typeof(Arc))]
public class CircleProgressBar : RangeBase
{
private const string IndicatorTemplateName = "PART_Indicator";
public static readonly DependencyProperty ArcThicknessProperty = DependencyProperty.Register(
"ArcThickness", typeof(double), typeof(CircleProgressBar), new PropertyMetadata(ValueBoxes.Double0Box));
public static readonly DependencyProperty ShowTextProperty = DependencyProperty.Register(
"ShowText", typeof(bool), typeof(CircleProgressBar), new PropertyMetadata(ValueBoxes.TrueBox));
public static readonly DependencyProperty TextProperty = DependencyProperty.Register(
"Text", typeof(string), typeof(CircleProgressBar), new PropertyMetadata(default(string)));
private Arc _indicator;
static CircleProgressBar()
{
FocusableProperty.OverrideMetadata(typeof(CircleProgressBar),
new FrameworkPropertyMetadata(ValueBoxes.FalseBox));
MaximumProperty.OverrideMetadata(typeof(CircleProgressBar),
new FrameworkPropertyMetadata(ValueBoxes.Double100Box));
}
public string Text
{
get => (string) GetValue(TextProperty);
set => SetValue(TextProperty, value);
}
public bool ShowText
{
get => (bool) GetValue(ShowTextProperty);
set => SetValue(ShowTextProperty, value);
}
public double ArcThickness
{
get => (double) GetValue(ArcThicknessProperty);
set => SetValue(ArcThicknessProperty, value);
}
private void SetProgressBarIndicatorAngle()
{
if (_indicator == null) return;
var minimum = Minimum;
var maximum = Maximum;
var num = Value;
_indicator.EndAngle = (maximum <= minimum ? 0 : (num - minimum) / (maximum - minimum)) * 360;
}
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
_indicator = GetTemplateChild(IndicatorTemplateName) as Arc;
if (_indicator != null)
{
_indicator.StartAngle = 0;
_indicator.EndAngle = 0;
}
SetProgressBarIndicatorAngle();
}
protected override void OnMinimumChanged(double oldMinimum, double newMinimum)
{
base.OnMinimumChanged(oldMinimum, newMinimum);
SetProgressBarIndicatorAngle();
}
protected override void OnMaximumChanged(double oldMaximum, double newMaximum)
{
base.OnMaximumChanged(oldMaximum, newMaximum);
SetProgressBarIndicatorAngle();
}
protected override void OnValueChanged(double oldValue, double newValue)
{
base.OnValueChanged(oldValue, newValue);
SetProgressBarIndicatorAngle();
}
}
}
@@ -0,0 +1,682 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using HandyControl.Data;
using HandyControl.Interactivity;
using HandyControl.Tools.Extension;
namespace HandyControl.Controls
{
/// <summary>
/// 颜色拾取器
/// </summary>
[TemplatePart(Name = ElementBorderColor, Type = typeof(Border))]
[TemplatePart(Name = ElementBorderPicker, Type = typeof(Border))]
[TemplatePart(Name = ElementBorderDrag, Type = typeof(Border))]
[TemplatePart(Name = ElementPanelColor, Type = typeof(Panel))]
[TemplatePart(Name = ElementSliderColor, Type = typeof(Panel))]
[TemplatePart(Name = ElementSliderOpacity, Type = typeof(Panel))]
[TemplatePart(Name = ElementPanelRgb, Type = typeof(Panel))]
public class ColorPicker : Control, ISingleOpen
{
#region Constants
private const string ElementBorderColor = "PART_BorderColor";
private const string ElementBorderPicker = "PART_BorderPicker";
private const string ElementBorderDrag = "PART_BorderDrag";
private const string ElementPanelColor = "PART_PanelColor";
private const string ElementSliderColor = "PART_SliderColor";
private const string ElementSliderOpacity = "PART_SliderOpacity";
private const string ElementPanelRgb = "PART_PanelRgb";
#endregion Constants
#region Data
private Panel _panelRgb;
private Border _borderColor;
private Border _borderPicker;
private Border _borderDrag;
private Panel _panelColor;
private Slider _sliderColor;
private Slider _sliderOpacity;
private bool _appliedTemplate;
/// <summary>
/// 当前显示的颜色类型
/// </summary>
private int _colorType;
/// <summary>
/// 是否已经加载控件
/// </summary>
private bool _isLoaded;
/// <summary>
/// 是否需要更新小球位置
/// </summary>
private bool _isNeedUpdatePicker = true;
/// <summary>
/// 是否在拖动小球
/// </summary>
private bool _isOnDragging;
/// <summary>
/// 是否需要更新信息
/// </summary>
private bool IsNeedUpdateInfo { get; set; } = true;
/// <summary>
/// 颜色选取面板宽度
/// </summary>
private const double ColorPanelWidth = 230;
/// <summary>
/// 颜色选取面板高度
/// </summary>
private const double ColorPanelHeight = 122;
/// <summary>
/// 预设的颜色(一共18个,两行)
/// </summary>
private readonly List<string> _colorPresetList = new List<string>
{
"#f44336",
"#e91e63",
"#9c27b0",
"#673ab7",
"#3f51b5",
"#2196f3",
"#03a9f4",
"#00bcd4",
"#009688",
"#4caf50",
"#8bc34a",
"#cddc39",
"#ffeb3b",
"#ffc107",
"#ff9800",
"#ff5722",
"#795548",
"#9e9e9e"
};
/// <summary>
/// 颜色范围集合
/// </summary>
private readonly List<ColorRange> _colorRangeList = new List<ColorRange>
{
new ColorRange
{
Color1 = Color.FromRgb(255, 0, 0),
Color2 = Color.FromRgb(255, 0, 255)
},
new ColorRange
{
Color1 = Color.FromRgb(255, 0, 255),
Color2 = Color.FromRgb(0, 0, 255)
},
new ColorRange
{
Color1 = Color.FromRgb(0, 0, 255),
Color2 = Color.FromRgb(0, 255, 255)
},
new ColorRange
{
Color1 = Color.FromRgb(0, 255, 255),
Color2 = Color.FromRgb(0, 255, 0)
},
new ColorRange
{
Color1 = Color.FromRgb(0, 255, 0),
Color2 = Color.FromRgb(255, 255, 0)
},
new ColorRange
{
Color1 = Color.FromRgb(255, 255, 0),
Color2 = Color.FromRgb(255, 0, 0)
}
};
/// <summary>
/// 颜色分隔集合
/// </summary>
private readonly List<Color> _colorSeparateList = new List<Color>
{
Color.FromRgb(255, 0, 0),
Color.FromRgb(255, 0, 255),
Color.FromRgb(0, 0, 255),
Color.FromRgb(0, 255, 255),
Color.FromRgb(0, 255, 0),
Color.FromRgb(255, 255, 0)
};
#endregion Data
#region Public Events
/// <summary>
/// 颜色改变事件
/// </summary>
public static readonly RoutedEvent SelectedColorChangedEvent =
EventManager.RegisterRoutedEvent("SelectedColorChanged", RoutingStrategy.Bubble,
typeof(EventHandler<FunctionEventArgs<Color>>), typeof(ColorPicker));
/// <summary>
/// 颜色改变事件
/// </summary>
public event EventHandler<FunctionEventArgs<Color>> SelectedColorChanged
{
add => AddHandler(SelectedColorChangedEvent, value);
remove => RemoveHandler(SelectedColorChangedEvent, value);
}
/// <summary>
/// 取消事件
/// </summary>
public static readonly RoutedEvent CanceledEvent =
EventManager.RegisterRoutedEvent("Canceled", RoutingStrategy.Bubble,
typeof(EventHandler), typeof(ColorPicker));
/// <summary>
/// 取消事件
/// </summary>
public event EventHandler Canceled
{
add => AddHandler(CanceledEvent, value);
remove => RemoveHandler(CanceledEvent, value);
}
#endregion Public Events
#region Properties
internal static readonly DependencyProperty ChannelAProperty = DependencyProperty.Register(
"ChannelA", typeof(int), typeof(ColorPicker), new PropertyMetadata(255));
internal int ChannelA
{
get => (int)GetValue(ChannelAProperty);
set => SetValue(ChannelAProperty, value);
}
internal static readonly DependencyProperty ChannelRProperty = DependencyProperty.Register(
"ChannelR", typeof(int), typeof(ColorPicker), new PropertyMetadata(255));
internal int ChannelR
{
get => (int)GetValue(ChannelRProperty);
set => SetValue(ChannelRProperty, value);
}
internal static readonly DependencyProperty ChannelGProperty = DependencyProperty.Register(
"ChannelG", typeof(int), typeof(ColorPicker), new PropertyMetadata(255));
internal int ChannelG
{
get => (int)GetValue(ChannelGProperty);
set => SetValue(ChannelGProperty, value);
}
internal static readonly DependencyProperty ChannelBProperty = DependencyProperty.Register(
"ChannelB", typeof(int), typeof(ColorPicker), new PropertyMetadata(255));
internal int ChannelB
{
get => (int)GetValue(ChannelBProperty);
set => SetValue(ChannelBProperty, value);
}
public static readonly DependencyProperty SelectedBrushProperty = DependencyProperty.Register(
"SelectedBrush", typeof(SolidColorBrush), typeof(ColorPicker), new PropertyMetadata(Brushes.White,
(o, args) =>
{
var ctl = (ColorPicker)o;
var v = (SolidColorBrush)args.NewValue;
if (ctl.IsNeedUpdateInfo)
{
ctl.IsNeedUpdateInfo = false;
ctl.ChannelR = v.Color.R;
ctl.ChannelG = v.Color.G;
ctl.ChannelB = v.Color.B;
ctl.ChannelA = v.Color.A;
ctl.IsNeedUpdateInfo = true;
}
ctl.UpdateStatus(v.Color);
ctl.SelectedBrushWithoutOpacity = new SolidColorBrush(Color.FromRgb(v.Color.R, v.Color.G, v.Color.B));
}));
/// <summary>
/// 当前选中的颜色
/// </summary>
public SolidColorBrush SelectedBrush
{
get => (SolidColorBrush)GetValue(SelectedBrushProperty);
set => SetValue(SelectedBrushProperty, value);
}
internal static readonly DependencyProperty SelectedBrushWithoutOpacityProperty = DependencyProperty.Register(
"SelectedBrushWithoutOpacity", typeof(SolidColorBrush), typeof(ColorPicker), new PropertyMetadata(Brushes.White));
internal SolidColorBrush SelectedBrushWithoutOpacity
{
get => (SolidColorBrush)GetValue(SelectedBrushWithoutOpacityProperty);
set => SetValue(SelectedBrushWithoutOpacityProperty, value);
}
internal static readonly DependencyProperty BackColorProperty = DependencyProperty.Register(
"BackColor", typeof(SolidColorBrush), typeof(ColorPicker), new PropertyMetadata(Brushes.Red));
internal SolidColorBrush BackColor
{
get => (SolidColorBrush)GetValue(BackColorProperty);
set => SetValue(BackColorProperty, value);
}
internal static readonly DependencyProperty ShowListProperty = DependencyProperty.Register(
"ShowList", typeof(List<bool>), typeof(ColorPicker), new PropertyMetadata(new List<bool>
{
true, false, false
}));
internal List<bool> ShowList
{
get => (List<bool>)GetValue(ShowListProperty);
set => SetValue(ShowListProperty, value);
}
#endregion Properties
/// <summary>
/// 当前显示的颜色类型
/// </summary>
private int ColorType
{
get => _colorType;
set
{
if (value < 0)
{
_colorType = 1;
}
else if (value > 1)
{
_colorType = 0;
}
else
{
_colorType = value;
}
var list = new List<bool>();
for (int i = 0; i < 2; i++)
{
list.Add(false);
}
list[_colorType] = true;
ShowList = list;
}
}
public ColorPicker()
{
CommandBindings.Add(new CommandBinding(ControlCommands.Confirm, ButtonConfirm_OnClick));
CommandBindings.Add(new CommandBinding(ControlCommands.Switch, ButtonSwitch_OnClick));
CommandBindings.Add(new CommandBinding(ControlCommands.Cancel, ButtonCancel_OnClick));
Loaded += (s, e) =>
{
if (!_isLoaded)
{
Init();
_isLoaded = true;
}
};
}
public override void OnApplyTemplate()
{
_appliedTemplate = false;
if (_sliderColor != null)
{
_sliderColor.ValueChanged -= SliderColor_OnValueChanged;
}
if (_sliderOpacity != null)
{
_sliderOpacity.ValueChanged -= SliderOpacity_OnValueChanged;
}
_panelColor?.Children.Clear();
_panelRgb?.RemoveHandler(NumericUpDown.ValueChangedEvent, new EventHandler<FunctionEventArgs<double>>(NumericUpDownRgb_OnValueChanged));
base.OnApplyTemplate();
_borderColor = GetTemplateChild(ElementBorderColor) as Border;
_borderDrag = GetTemplateChild(ElementBorderDrag) as Border;
_borderPicker = GetTemplateChild(ElementBorderPicker) as Border;
_panelColor = GetTemplateChild(ElementPanelColor) as Panel;
_sliderColor = GetTemplateChild(ElementSliderColor) as Slider;
_sliderOpacity = GetTemplateChild(ElementSliderOpacity) as Slider;
_panelRgb = GetTemplateChild(ElementPanelRgb) as Panel;
if (_sliderColor != null)
{
_sliderColor.ValueChanged += SliderColor_OnValueChanged;
}
if (_sliderOpacity != null)
{
_sliderOpacity.ValueChanged += SliderOpacity_OnValueChanged;
}
if (_borderDrag != null)
{
var behavior = new MouseDragElementBehavior();
behavior.DragFinished += MouseDragElementBehavior_OnDragFinished;
behavior.DragBegun += MouseDragElementBehavior_OnDragging;
behavior.Dragging += MouseDragElementBehavior_OnDragging;
var collection = Interaction.GetBehaviors(_borderDrag);
collection.Add(behavior);
}
_appliedTemplate = true;
if (_isLoaded)
{
Init();
}
_panelRgb?.AddHandler(NumericUpDown.ValueChangedEvent, new EventHandler<FunctionEventArgs<double>>(NumericUpDownRgb_OnValueChanged));
}
/// <summary>
/// 初始化
/// </summary>
private void Init()
{
UpdateStatus(SelectedBrush.Color);
_panelColor.Children.Clear();
foreach (var item in _colorPresetList)
{
_panelColor.Children.Add(CreateColorButton(item));
}
}
/// <summary>
/// 创建颜色按钮
/// </summary>
/// <returns></returns>
private Button CreateColorButton(string colorStr)
{
var color = ColorConverter.ConvertFromString(colorStr) ?? default(Color);
var brush = new SolidColorBrush((Color)color);
var button = new Button
{
Margin = new Thickness(0, 0, 12, 12),
Style = FindResource("ButtonCustom") as Style,
Content = new Border
{
Background = brush,
Width = 12,
Height = 12,
CornerRadius = new CornerRadius(2)
}
};
button.Click += (s, e) =>
{
SelectedBrush = brush;
ChannelA = byte.MaxValue;
};
return button;
}
/// <summary>
/// 内部更新
/// </summary>
private void UpdateStatus(Color color)
{
if (_isOnDragging) return;
var r = color.R;
var g = color.G;
var b = color.B;
var list = new List<byte>
{
r,
g,
b
};
var max = list.Max();
var min = list.Min();
if (min == max)
{
if (!(r == g && b == g))
{
BackColor = Brushes.Red;
IsNeedUpdateInfo = false;
if (!_sliderColor.IsMouseOver && !_sliderOpacity.IsMouseOver)
{
_sliderColor.Value = 0;
}
IsNeedUpdateInfo = true;
}
}
else
{
var maxIndex = list.IndexOf(max);
var minIndex = list.IndexOf(min);
var commonIndex = 3 - maxIndex - minIndex;
if (commonIndex == 3)
{
BackColor = Brushes.Red;
IsNeedUpdateInfo = false;
if (!_sliderColor.IsMouseOver && !_sliderOpacity.IsMouseOver)
{
_sliderColor.Value = 0;
}
IsNeedUpdateInfo = true;
}
else
{
var common = list[commonIndex];
list[maxIndex] = 255;
list[minIndex] = 0;
common = (byte)(255 * (min - common) / (double)(min - max));
list[commonIndex] = common;
BackColor = new SolidColorBrush(Color.FromRgb(list[0], list[1], list[2]));
list[commonIndex] = 0;
var cIndex = _colorSeparateList.IndexOf(Color.FromRgb(list[0], list[1], list[2]));
int sub;
var direc = 0;
if (cIndex < 5 && cIndex > 0)
{
var nextColorList = _colorSeparateList[cIndex + 1].ToList();
var prevColorList = _colorSeparateList[cIndex - 1].ToList();
if (nextColorList[minIndex] > 0)
{
var target = prevColorList[commonIndex];
direc = 1;
sub = target - common;
}
else
{
sub = common;
}
}
else if (cIndex == 0)
{
sub = common;
if (minIndex == 2)
{
sub = 255 - common;
direc = -5;
}
}
else
{
sub = 255 - common;
}
var scale = sub / 255.0;
var scaleTotal = cIndex - direc + scale;
IsNeedUpdateInfo = false;
if (!_sliderColor.IsMouseOver && !_sliderOpacity.IsMouseOver)
{
_sliderColor.Value = scaleTotal;
}
IsNeedUpdateInfo = true;
}
}
var matrix = _borderPicker.RenderTransform.Value;
var x = max == 0 ? 0 : (1 - min / (double)max) * ColorPanelWidth;
var y = (1 - max / 255.0) * ColorPanelHeight;
if (_isNeedUpdatePicker)
{
_borderPicker.RenderTransform = new MatrixTransform(matrix.M11, matrix.M12, matrix.M21, matrix.M22, x, y);
}
}
private void SliderColor_OnValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
{
if (!_appliedTemplate || !IsNeedUpdateInfo) return;
var index = Math.Min(5, (int)Math.Floor(e.NewValue));
var sub = e.NewValue - index;
var range = _colorRangeList[index];
var color = range.GetColor(sub);
BackColor = new SolidColorBrush(color);
var matrix = _borderPicker.RenderTransform.Value;
_isNeedUpdatePicker = false;
UpdateColorWhenDrag(new Point(matrix.OffsetX, matrix.OffsetY));
_isNeedUpdatePicker = true;
}
private void SliderOpacity_OnValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
{
if (!_appliedTemplate || !IsNeedUpdateInfo) return;
var color = SelectedBrush.Color;
SelectedBrush = new SolidColorBrush(Color.FromArgb((byte)_sliderOpacity.Value, color.R, color.G, color.B));
}
private void MouseDragElementBehavior_OnDragging(object sender, MouseEventArgs e)
{
var p = e.GetPosition(_borderColor);
_isOnDragging = true;
UpdateColorWhenDrag(p);
_isOnDragging = false;
}
/// <summary>
/// 拖动时更新颜色
/// </summary>
private void UpdateColorWhenDrag(Point p)
{
var matrix = _borderPicker.RenderTransform.Value;
if (p.X < 0)
{
p.X = 0;
}
else if (p.X > ColorPanelWidth)
{
p.X = ColorPanelWidth;
}
if (p.Y < 0)
{
p.Y = 0;
}
else if (p.Y > ColorPanelHeight)
{
p.Y = ColorPanelHeight;
}
if (_isNeedUpdatePicker)
{
_borderPicker.RenderTransform = new MatrixTransform(matrix.M11, matrix.M12, matrix.M21, matrix.M22, p.X, p.Y);
}
var scaleX = p.X / ColorPanelWidth;
var scaleY = 1 - p.Y / ColorPanelHeight;
var colorYLeft = Color.FromRgb((byte)(255 * scaleY), (byte)(255 * scaleY), (byte)(255 * scaleY));
var colorYRight = Color.FromRgb((byte)(BackColor.Color.R * scaleY), (byte)(BackColor.Color.G * scaleY), (byte)(BackColor.Color.B * scaleY));
var subR = colorYLeft.R - colorYRight.R;
var subG = colorYLeft.G - colorYRight.G;
var subB = colorYLeft.B - colorYRight.B;
var color = Color.FromArgb((byte)_sliderOpacity.Value, (byte)(colorYLeft.R - subR * scaleX),
(byte)(colorYLeft.G - subG * scaleX), (byte)(colorYLeft.B - subB * scaleX));
SelectedBrush = new SolidColorBrush(color);
}
private void MouseDragElementBehavior_OnDragFinished(object sender, MouseEventArgs e) => _borderDrag.RenderTransform = new MatrixTransform();
private void ButtonSwitch_OnClick(object sender, RoutedEventArgs e) => ColorType++;
// ReSharper disable once UnusedParameter.Local
private void NumericUpDownRgb_OnValueChanged(object sender, FunctionEventArgs<double> e)
{
if (!_appliedTemplate || !IsNeedUpdateInfo) return;
if (e.OriginalSource is NumericUpDown ctl && ctl.Tag is string tag)
{
var color = SelectedBrush.Color;
IsNeedUpdateInfo = false;
switch (tag)
{
case "R":
{
SelectedBrush = new SolidColorBrush(Color.FromArgb(color.A, (byte)e.Info, color.G, color.B));
break;
}
case "G":
{
SelectedBrush = new SolidColorBrush(Color.FromArgb(color.A, color.R, (byte)e.Info, color.B));
break;
}
case "B":
{
SelectedBrush = new SolidColorBrush(Color.FromArgb(color.A, color.R, color.G, (byte)e.Info));
break;
}
}
IsNeedUpdateInfo = true;
}
}
private void ButtonConfirm_OnClick(object sender, RoutedEventArgs e)
=> RaiseEvent(new FunctionEventArgs<Color>(SelectedColorChangedEvent, this)
{
Info = SelectedBrush.Color
});
private void ButtonCancel_OnClick(object sender, RoutedEventArgs e) => RaiseEvent(new RoutedEventArgs(CanceledEvent));
public void Dispose() => System.Windows.Window.GetWindow(this)?.Close();
public bool CanDispose { get; } = true;
}
}
@@ -0,0 +1,482 @@
using System;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Threading;
using HandyControl.Data;
using HandyControl.Interactivity;
using HandyControl.Tools;
using HandyControl.Tools.Extension;
namespace HandyControl.Controls
{
/// <summary>
/// 消息提醒
/// </summary>
[TemplatePart(Name = ElementPanelMore, Type = typeof(Panel))]
[TemplatePart(Name = ElementGridMain, Type = typeof(Grid))]
[TemplatePart(Name = ElementButtonClose, Type = typeof(Button))]
public class Growl : Control
{
#region Constants
private const string ElementPanelMore = "PART_PanelMore";
private const string ElementGridMain = "PART_GridMain";
private const string ElementButtonClose = "PART_ButtonClose";
#endregion Constants
#region Data
private Panel _panelMore;
private Grid _gridMain;
private Button _buttonClose;
private bool _showCloseButton;
private bool _staysOpen;
private int _waitTime = 6;
/// <summary>
/// 消息容器
/// </summary>
public static Panel GrowlPanel { get; set; }
/// <summary>
/// 计数
/// </summary>
private int _tickCount;
/// <summary>
/// 关闭计时器
/// </summary>
private DispatcherTimer _timerClose;
#endregion Data
public Growl()
{
CommandBindings.Add(new CommandBinding(ControlCommands.Close, ButtonClose_OnClick));
CommandBindings.Add(new CommandBinding(ControlCommands.Cancel, ButtonCancel_OnClick));
CommandBindings.Add(new CommandBinding(ControlCommands.Confirm, ButtonOk_OnClick));
MouseEnter += Growl_MouseEnter;
MouseLeave += Growl_MouseLeave;
}
private void Growl_MouseLeave(object sender, MouseEventArgs e) => _buttonClose.Collapse();
private void Growl_MouseEnter(object sender, MouseEventArgs e) => _buttonClose.Show(_showCloseButton);
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
_panelMore = GetTemplateChild(ElementPanelMore) as Panel;
_gridMain = GetTemplateChild(ElementGridMain) as Grid;
_buttonClose = GetTemplateChild(ElementButtonClose) as Button;
CheckNull();
Update();
}
private void CheckNull()
{
if (_panelMore == null || _gridMain == null || _buttonClose == null) throw new Exception();
}
private Func<bool, bool> ActionBeforeClose { get; set; }
internal static readonly DependencyProperty CancelStrProperty = DependencyProperty.Register(
"CancelStr", typeof(string), typeof(Growl), new PropertyMetadata(default(string)));
internal static readonly DependencyProperty ConfirmStrProperty = DependencyProperty.Register(
"ConfirmStr", typeof(string), typeof(Growl), new PropertyMetadata(default(string)));
public static readonly DependencyProperty ShowDateTimeProperty = DependencyProperty.Register(
"ShowDateTime", typeof(bool), typeof(Growl), new PropertyMetadata(ValueBoxes.TrueBox));
public static readonly DependencyProperty MessageProperty = DependencyProperty.Register(
"Message", typeof(string), typeof(Growl), new PropertyMetadata(default(string)));
public static readonly DependencyProperty TimeProperty = DependencyProperty.Register(
"Time", typeof(DateTime), typeof(Growl), new PropertyMetadata(default(DateTime)));
public static readonly DependencyProperty IconProperty = DependencyProperty.Register(
"Icon", typeof(Geometry), typeof(Growl), new PropertyMetadata(default(Geometry)));
public static readonly DependencyProperty IconBrushProperty = DependencyProperty.Register(
"IconBrush", typeof(Brush), typeof(Growl), new PropertyMetadata(default(Brush)));
public static readonly DependencyProperty TypeProperty = DependencyProperty.Register(
"Type", typeof(InfoType), typeof(Growl), new PropertyMetadata(default(InfoType)));
public static readonly DependencyProperty GrowlParentProperty = DependencyProperty.RegisterAttached(
"GrowlParent", typeof(bool), typeof(Growl), new PropertyMetadata(ValueBoxes.FalseBox, (o, args) =>
{
if (o is Panel panel)
{
SetGrowlPanel(panel);
}
}));
public static void SetGrowlParent(DependencyObject element, bool value) => element.SetValue(GrowlParentProperty, value);
public static bool GetGrowlParent(DependencyObject element) => (bool)element.GetValue(GrowlParentProperty);
public InfoType Type
{
get => (InfoType)GetValue(TypeProperty);
set => SetValue(TypeProperty, value);
}
internal string CancelStr
{
get => (string)GetValue(CancelStrProperty);
set => SetValue(CancelStrProperty, value);
}
internal string ConfirmStr
{
get => (string)GetValue(ConfirmStrProperty);
set => SetValue(ConfirmStrProperty, value);
}
public bool ShowDateTime
{
get => (bool)GetValue(ShowDateTimeProperty);
set => SetValue(ShowDateTimeProperty, value);
}
public string Message
{
get => (string)GetValue(MessageProperty);
set => SetValue(MessageProperty, value);
}
public DateTime Time
{
get => (DateTime)GetValue(TimeProperty);
set => SetValue(TimeProperty, value);
}
public Geometry Icon
{
get => (Geometry)GetValue(IconProperty);
set => SetValue(IconProperty, value);
}
public Brush IconBrush
{
get => (Brush)GetValue(IconBrushProperty);
set => SetValue(IconBrushProperty, value);
}
/// <summary>
/// 开始计时器
/// </summary>
private void StartTimer()
{
_timerClose = new DispatcherTimer
{
Interval = TimeSpan.FromSeconds(1)
};
_timerClose.Tick += delegate
{
if (IsMouseOver)
{
_tickCount = 0;
return;
}
_tickCount++;
if (_tickCount >= _waitTime) Close();
};
_timerClose.Start();
}
/// <summary>
/// 消息容器
/// </summary>
/// <param name="panel"></param>
private static void SetGrowlPanel(Panel panel)
{
GrowlPanel = panel;
var menuItem = new MenuItem
{
Header = Properties.Langs.Lang.Clear
};
menuItem.Click += (s, e) =>
{
foreach (var item in GrowlPanel.Children.OfType<Growl>())
{
item.Close();
}
};
GrowlPanel.ContextMenu = new ContextMenu
{
Items =
{
menuItem
}
};
var behavior = new FluidMoveBehavior
{
AppliesTo = FluidMoveScope.Children,
Duration = new Duration(TimeSpan.FromMilliseconds(400))
};
var collection = Interaction.GetBehaviors(GrowlPanel);
collection.Add(behavior);
}
private void Update()
{
if (ActionBeforeClose != null)
{
_staysOpen = true;
_showCloseButton = false;
_panelMore.IsEnabled = true;
_panelMore.Show();
}
var transform = new TranslateTransform
{
X = MaxWidth
};
_gridMain.RenderTransform = transform;
transform.BeginAnimation(TranslateTransform.XProperty, AnimationHelper.CreateAnimation(0));
if (!_staysOpen) StartTimer();
}
/// <summary>
/// 显示信息
/// </summary>
/// <param name="growlInfo"></param>
private static void Show(GrowlInfo growlInfo)
{
var ctl = new Growl
{
Message = growlInfo.Message,
Time = DateTime.Now,
Icon = ResourceHelper.GetResource<Geometry>(growlInfo.IconKey),
IconBrush = ResourceHelper.GetResource<Brush>(growlInfo.IconBrushKey),
_showCloseButton = growlInfo.ShowCloseButton,
ActionBeforeClose = growlInfo.ActionBeforeClose,
_staysOpen = growlInfo.StaysOpen,
ShowDateTime = growlInfo.ShowDateTime,
ConfirmStr = growlInfo.ConfirmStr,
CancelStr = growlInfo.CancelStr,
Type = growlInfo.Type,
_waitTime = Math.Max(growlInfo.WaitTime, 2)
};
GrowlPanel.Children.Insert(0, ctl);
}
/// <summary>
/// 成功
/// </summary>
/// <param name="message"></param>
public static void Success(string message) => Success(new GrowlInfo
{
Message = message
});
/// <summary>
/// 成功
/// </summary>
/// <param name="growlInfo"></param>
public static void Success(GrowlInfo growlInfo)
{
if (growlInfo == null) throw new ArgumentNullException(nameof(growlInfo));
if (!growlInfo.IsCustom)
{
growlInfo.IconKey = ResourceToken.SuccessGeometry;
growlInfo.IconBrushKey = ResourceToken.SuccessBrush;
growlInfo.Type = InfoType.Success;
}
Show(growlInfo);
}
/// <summary>
/// 消息
/// </summary>
/// <param name="message"></param>
public static void Info(string message) => Info(new GrowlInfo
{
Message = message
});
/// <summary>
/// 消息
/// </summary>
/// <param name="growlInfo"></param>
public static void Info(GrowlInfo growlInfo)
{
if (growlInfo == null) throw new ArgumentNullException(nameof(growlInfo));
if (!growlInfo.IsCustom)
{
growlInfo.IconKey = ResourceToken.InfoGeometry;
growlInfo.IconBrushKey = ResourceToken.InfoBrush;
growlInfo.Type = InfoType.Info;
}
Show(growlInfo);
}
/// <summary>
/// 警告
/// </summary>
/// <param name="message"></param>
public static void Warning(string message) => Warning(new GrowlInfo
{
Message = message
});
/// <summary>
/// 警告
/// </summary>
/// <param name="growlInfo"></param>
public static void Warning(GrowlInfo growlInfo)
{
if (growlInfo == null) throw new ArgumentNullException(nameof(growlInfo));
if (!growlInfo.IsCustom)
{
growlInfo.IconKey = ResourceToken.WarningGeometry;
growlInfo.IconBrushKey = ResourceToken.WarningBrush;
growlInfo.Type = InfoType.Warning;
}
Show(growlInfo);
}
/// <summary>
/// 错误
/// </summary>
/// <param name="message"></param>
public static void Error(string message) => Error(new GrowlInfo
{
Message = message
});
/// <summary>
/// 错误
/// </summary>
/// <param name="growlInfo"></param>
public static void Error(GrowlInfo growlInfo)
{
if (growlInfo == null) throw new ArgumentNullException(nameof(growlInfo));
if (!growlInfo.IsCustom)
{
growlInfo.IconKey = ResourceToken.ErrorGeometry;
growlInfo.IconBrushKey = ResourceToken.DangerBrush;
growlInfo.StaysOpen = true;
growlInfo.Type = InfoType.Error;
}
Show(growlInfo);
}
/// <summary>
/// 严重
/// </summary>
/// <param name="message"></param>
public static void Fatal(string message) => Fatal(new GrowlInfo
{
Message = message
});
/// <summary>
/// 严重
/// </summary>
/// <param name="growlInfo"></param>
public static void Fatal(GrowlInfo growlInfo)
{
if (growlInfo == null) throw new ArgumentNullException(nameof(growlInfo));
if (!growlInfo.IsCustom)
{
growlInfo.IconKey = ResourceToken.FatalGeometry;
growlInfo.IconBrushKey = ResourceToken.PrimaryTextBrush;
growlInfo.StaysOpen = true;
growlInfo.ShowCloseButton = false;
growlInfo.Type = InfoType.Fatal;
if (GrowlPanel.ContextMenu != null) GrowlPanel.ContextMenu.Opacity = 0;
}
Show(growlInfo);
}
/// <summary>
/// 询问
/// </summary>
/// <param name="message"></param>
/// <param name="actionBeforeClose"></param>
public static void Ask(string message, Func<bool, bool> actionBeforeClose) => Ask(new GrowlInfo
{
Message = message,
ActionBeforeClose = actionBeforeClose
});
/// <summary>
/// 询问
/// </summary>
/// <param name="growlInfo"></param>
public static void Ask(GrowlInfo growlInfo)
{
if (growlInfo == null) throw new ArgumentNullException(nameof(growlInfo));
if (!growlInfo.IsCustom)
{
growlInfo.IconKey = ResourceToken.AskGeometry;
growlInfo.IconBrushKey = ResourceToken.AccentBrush;
growlInfo.StaysOpen = true;
growlInfo.ShowCloseButton = false;
growlInfo.Type = InfoType.Ask;
}
Show(growlInfo);
}
private void ButtonClose_OnClick(object sender, RoutedEventArgs e) => Close();
/// <summary>
/// 关闭
/// </summary>
private void Close()
{
_timerClose?.Stop();
var transform = new TranslateTransform();
_gridMain.RenderTransform = transform;
var animation = AnimationHelper.CreateAnimation(MaxWidth);
animation.Completed += delegate { GrowlPanel.Children.Remove(this); };
transform.BeginAnimation(TranslateTransform.XProperty, animation);
}
/// <summary>
/// 清除
/// </summary>
public static void Clear()
{
GrowlPanel.Children.Clear();
if (GrowlPanel.ContextMenu != null)
{
GrowlPanel.ContextMenu.IsOpen = false;
GrowlPanel.ContextMenu.Opacity = 1;
}
}
private void ButtonCancel_OnClick(object sender, RoutedEventArgs e)
{
if (ActionBeforeClose?.Invoke(false) == true)
{
Close();
}
}
private void ButtonOk_OnClick(object sender, RoutedEventArgs e)
{
if (ActionBeforeClose?.Invoke(true) == true)
{
Close();
}
}
}
}
@@ -0,0 +1,362 @@
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using HandyControl.Data;
using HandyControl.Interactivity;
using HandyControl.Tools;
using HandyControl.Tools.Extension;
namespace HandyControl.Controls
{
/// <summary>
/// 页码
/// </summary>
[TemplatePart(Name = ElementButtonLeft, Type = typeof(Button))]
[TemplatePart(Name = ElementButtonRight, Type = typeof(Button))]
[TemplatePart(Name = ElementButtonFirst, Type = typeof(RadioButton))]
[TemplatePart(Name = ElementTextBlockLeft, Type = typeof(TextBlock))]
[TemplatePart(Name = ElementPanelMain, Type = typeof(Panel))]
[TemplatePart(Name = ElementTextBlockRight, Type = typeof(TextBlock))]
[TemplatePart(Name = ElementButtonLast, Type = typeof(RadioButton))]
public class Pagination : Control
{
#region Constants
private const string ElementButtonLeft = "PART_ButtonLeft";
private const string ElementButtonRight = "PART_ButtonRight";
private const string ElementButtonFirst = "PART_ButtonFirst";
private const string ElementTextBlockLeft = "PART_TextBlockLeft";
private const string ElementPanelMain = "PART_PanelMain";
private const string ElementTextBlockRight = "PART_TextBlockRight";
private const string ElementButtonLast = "PART_ButtonLast";
#endregion Constants
#region Data
private Button _buttonLeft;
private Button _buttonRight;
private RadioButton _buttonFirst;
private TextBlock _textBlockLeft;
private Panel _panelMain;
private TextBlock _textBlockRight;
private RadioButton _buttonLast;
private bool _appliedTemplate;
#endregion Data
#region Public Events
/// <summary>
/// 页面更新事件
/// </summary>
public static readonly RoutedEvent PageUpdatedEvent =
EventManager.RegisterRoutedEvent("PageUpdated", RoutingStrategy.Bubble,
typeof(EventHandler<FunctionEventArgs<int>>), typeof(Pagination));
/// <summary>
/// 页面更新事件
/// </summary>
public event EventHandler<FunctionEventArgs<int>> PageUpdated
{
add => AddHandler(PageUpdatedEvent, value);
remove => RemoveHandler(PageUpdatedEvent, value);
}
#endregion Public Events
public Pagination()
{
CommandBindings.Add(new CommandBinding(ControlCommands.Prev, ButtonPrev_OnClick));
CommandBindings.Add(new CommandBinding(ControlCommands.Next, ButtonNext_OnClick));
CommandBindings.Add(new CommandBinding(ControlCommands.Selected, ToggleButton_OnChecked));
}
#region Public Properties
#region MaxPageCount
/// <summary>
/// 最大页数
/// </summary>
public static readonly DependencyProperty MaxPageCountProperty = DependencyProperty.Register(
"MaxPageCount", typeof(int), typeof(Pagination), new PropertyMetadata(1, (o, args) =>
{
if (o is Pagination pagination && args.NewValue is int value)
{
if (pagination.PageIndex > pagination.MaxPageCount)
{
pagination.PageIndex = pagination.MaxPageCount;
}
pagination.Show(value > 1);
pagination.Update();
}
}, (o, value) =>
{
if (!(o is Pagination)) return 1;
var intValue = (int)value;
if (intValue < 1)
{
return 1;
}
return intValue;
}));
/// <summary>
/// 最大页数
/// </summary>
public int MaxPageCount
{
get => (int)GetValue(MaxPageCountProperty);
set => SetValue(MaxPageCountProperty, value);
}
#endregion MaxPageCount
#region DataCountPerPage
/// <summary>
/// 每页的数据量
/// </summary>
public static readonly DependencyProperty DataCountPerPageProperty = DependencyProperty.Register(
"DataCountPerPage", typeof(int), typeof(Pagination), new PropertyMetadata(20, (o, args) =>
{
if (o is Pagination pagination)
{
pagination.Update();
}
}, (o, value) =>
{
if (!(o is Pagination)) return 1;
var intValue = (int)value;
if (intValue < 1)
{
return 1;
}
return intValue;
}));
/// <summary>
/// 每页的数据量
/// </summary>
public int DataCountPerPage
{
get => (int)GetValue(DataCountPerPageProperty);
set => SetValue(DataCountPerPageProperty, value);
}
#endregion
#region PageIndex
/// <summary>
/// 当前页
/// </summary>
public static readonly DependencyProperty PageIndexProperty = DependencyProperty.Register(
"PageIndex", typeof(int), typeof(Pagination), new PropertyMetadata(1, (o, args) =>
{
if (o is Pagination pagination && args.NewValue is int value)
{
pagination.Update();
pagination.RaiseEvent(new FunctionEventArgs<int>(PageUpdatedEvent, pagination)
{
Info = value
});
}
}, (o, value) =>
{
if (!(o is Pagination pagination)) return 1;
var intValue = (int)value;
if (intValue < 0)
{
return 0;
}
if (intValue > pagination.MaxPageCount) return pagination.MaxPageCount;
return intValue;
}));
/// <summary>
/// 当前页
/// </summary>
public int PageIndex
{
get => (int)GetValue(PageIndexProperty);
set => SetValue(PageIndexProperty, value);
}
#endregion PageIndex
#region MaxPageInterval
/// <summary>
/// 表示当前选中的按钮距离左右两个方向按钮的最大间隔(4表示间隔4个按钮,如果超过则用省略号表示)
/// </summary>
public static readonly DependencyProperty MaxPageIntervalProperty = DependencyProperty.Register(
"MaxPageInterval", typeof(int), typeof(Pagination), new PropertyMetadata(3, (o, args) =>
{
if (o is Pagination pagination)
{
pagination.Update();
}
}), value =>
{
var intValue = (int)value;
return intValue >= 0;
});
/// <summary>
/// 表示当前选中的按钮距离左右两个方向按钮的最大间隔(4表示间隔4个按钮,如果超过则用省略号表示)
/// </summary>
public int MaxPageInterval
{
get => (int)GetValue(MaxPageIntervalProperty);
set => SetValue(MaxPageIntervalProperty, value);
}
#endregion MaxPageInterval
#endregion
#region Public Methods
public override void OnApplyTemplate()
{
_appliedTemplate = false;
base.OnApplyTemplate();
_buttonLeft = GetTemplateChild(ElementButtonLeft) as Button;
_buttonRight = GetTemplateChild(ElementButtonRight) as Button;
_buttonFirst = GetTemplateChild(ElementButtonFirst) as RadioButton;
_textBlockLeft = GetTemplateChild(ElementTextBlockLeft) as TextBlock;
_panelMain = GetTemplateChild(ElementPanelMain) as Panel;
_textBlockRight = GetTemplateChild(ElementTextBlockRight) as TextBlock;
_buttonLast = GetTemplateChild(ElementButtonLast) as RadioButton;
CheckNull();
_appliedTemplate = true;
Update();
}
#endregion Public Methods
#region Private Methods
private void CheckNull()
{
if (_buttonLeft == null || _buttonRight == null || _buttonFirst == null ||
_textBlockLeft == null || _panelMain == null || _textBlockRight == null ||
_buttonLast == null) throw new Exception();
}
/// <summary>
/// 更新
/// </summary>
private void Update()
{
if (!_appliedTemplate) return;
_buttonLeft.IsEnabled = PageIndex > 1;
_buttonRight.IsEnabled = PageIndex < MaxPageCount;
if (MaxPageInterval == 0)
{
_buttonFirst.Collapse();
_buttonLast.Collapse();
_textBlockLeft.Collapse();
_textBlockRight.Collapse();
_panelMain.Children.Clear();
var selectButton = CreateButton(PageIndex);
_panelMain.Children.Add(selectButton);
selectButton.IsChecked = true;
return;
}
_buttonFirst.Show();
_buttonLast.Show();
_textBlockLeft.Show();
_textBlockRight.Show();
//更新最后一页
if (MaxPageCount == 1)
{
_buttonLast.Collapse();
}
else
{
_buttonLast.Show();
_buttonLast.Tag = MaxPageCount.ToString();
}
//更新省略号
var right = MaxPageCount - PageIndex;
var left = PageIndex - 1;
_textBlockRight.Show(right > MaxPageInterval);
_textBlockLeft.Show(left > MaxPageInterval);
//更新中间部分
_panelMain.Children.Clear();
if (PageIndex > 1 && PageIndex < MaxPageCount)
{
var selectButton = CreateButton(PageIndex);
_panelMain.Children.Add(selectButton);
selectButton.IsChecked = true;
}
else if (PageIndex == 1)
{
_buttonFirst.IsChecked = true;
}
else
{
_buttonLast.IsChecked = true;
}
var sub = PageIndex;
for (int i = 0; i < MaxPageInterval - 1; i++)
{
if (--sub > 1)
{
_panelMain.Children.Insert(0, CreateButton(sub));
}
else
{
break;
}
}
var add = PageIndex;
for (int i = 0; i < MaxPageInterval - 1; i++)
{
if (++add < MaxPageCount)
{
_panelMain.Children.Add(CreateButton(add));
}
else
{
break;
}
}
}
private void ButtonPrev_OnClick(object sender, RoutedEventArgs e) => PageIndex--;
private void ButtonNext_OnClick(object sender, RoutedEventArgs e) => PageIndex++;
private RadioButton CreateButton(int page)
{
return new RadioButton
{
Style = ResourceHelper.GetResource<Style>(ResourceToken.PaginationButtonStyle),
Tag = page.ToString()
};
}
private void ToggleButton_OnChecked(object sender, RoutedEventArgs e)
{
if (!(e.OriginalSource is RadioButton button)) return;
if (button.IsChecked == false) return;
PageIndex = int.Parse(button.Tag.ToString());
}
#endregion Private Methods
}
}
@@ -0,0 +1,43 @@
using System.Windows;
using System.Windows.Controls;
using HandyControl.Tools;
namespace HandyControl.Controls
{
public class RadioGroup : ItemsControl
{
protected override DependencyObject GetContainerForItemOverride() => new RadioButton();
protected override bool IsItemItsOwnContainerOverride(object item) => item is RadioButton;
public static readonly DependencyProperty OrientationProperty = DependencyProperty.Register(
"Orientation", typeof(Orientation), typeof(RadioGroup), new PropertyMetadata(default(Orientation)));
public Orientation Orientation
{
get => (Orientation)GetValue(OrientationProperty);
set => SetValue(OrientationProperty, value);
}
public static readonly DependencyProperty ItemStyleSelectorProperty = DependencyProperty.Register(
"ItemStyleSelector", typeof(StyleSelector), typeof(RadioGroup), new PropertyMetadata(new RadioGroupItemStyleSelector()));
public StyleSelector ItemStyleSelector
{
get => (StyleSelector)GetValue(ItemStyleSelectorProperty);
set => SetValue(ItemStyleSelectorProperty, value);
}
protected override void OnVisualChildrenChanged(DependencyObject visualAdded, DependencyObject visualRemoved)
{
base.OnVisualChildrenChanged(visualAdded, visualRemoved);
var count = Items.Count;
for (int i = 0; i < count; i++)
{
var item = (RadioButton)Items[i];
item.Style = ItemStyleSelector?.SelectStyle(item, this);
}
}
}
}
@@ -0,0 +1,219 @@
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using HandyControl.Data;
using HandyControl.Tools;
namespace HandyControl.Controls
{
public class ScrollViewer : System.Windows.Controls.ScrollViewer
{
private double _totalVerticalOffset;
private double _totalHorizontalOffset;
private bool _isRunning;
/// <summary>
/// 滚动方向
/// </summary>
public static readonly DependencyProperty OrientationProperty = DependencyProperty.Register(
"Orientation", typeof(Orientation), typeof(ScrollViewer), new PropertyMetadata(Orientation.Vertical));
/// <summary>
/// 滚动方向
/// </summary>
public Orientation Orientation
{
get => (Orientation) GetValue(OrientationProperty);
set => SetValue(OrientationProperty, value);
}
/// <summary>
/// 是否响应鼠标滚轮操作
/// </summary>
public static readonly DependencyProperty CanMouseWheelProperty = DependencyProperty.Register(
"CanMouseWheel", typeof(bool), typeof(ScrollViewer), new PropertyMetadata(ValueBoxes.TrueBox));
/// <summary>
/// 是否响应鼠标滚轮操作
/// </summary>
public bool CanMouseWheel
{
get => (bool) GetValue(CanMouseWheelProperty);
set => SetValue(CanMouseWheelProperty, value);
}
protected override void OnMouseWheel(MouseWheelEventArgs e)
{
if (!CanMouseWheel) return;
if (!IsEnableInertia)
{
if (Orientation == Orientation.Vertical)
{
base.OnMouseWheel(e);
}
else
{
_totalHorizontalOffset = HorizontalOffset;
CurrentHorizontalOffset = HorizontalOffset;
_totalHorizontalOffset = Math.Min(Math.Max(0, _totalHorizontalOffset - e.Delta), ScrollableWidth);
CurrentHorizontalOffset = _totalHorizontalOffset;
}
return;
}
e.Handled = true;
if (Orientation == Orientation.Vertical)
{
if (!_isRunning)
{
_totalVerticalOffset = VerticalOffset;
CurrentVerticalOffset = VerticalOffset;
}
_totalVerticalOffset = Math.Min(Math.Max(0, _totalVerticalOffset - e.Delta), ScrollableHeight);
ScrollToVerticalOffsetInternal(_totalVerticalOffset);
}
else
{
if (!_isRunning)
{
_totalHorizontalOffset = HorizontalOffset;
CurrentHorizontalOffset = HorizontalOffset;
}
_totalHorizontalOffset = Math.Min(Math.Max(0, _totalHorizontalOffset - e.Delta), ScrollableWidth);
ScrollToHorizontalOffsetInternal(_totalHorizontalOffset);
}
}
internal void ScrollToVerticalOffsetInternal(double offset, double milliseconds = 500)
{
var animation = AnimationHelper.CreateAnimation(offset, milliseconds);
animation.FillBehavior = FillBehavior.Stop;
animation.Completed += (s, e1) =>
{
CurrentVerticalOffset = offset;
_isRunning = false;
};
_isRunning = true;
BeginAnimation(CurrentVerticalOffsetProperty, animation);
}
internal void ScrollToHorizontalOffsetInternal(double offset, double milliseconds = 500)
{
var animation = AnimationHelper.CreateAnimation(offset, milliseconds);
animation.FillBehavior = FillBehavior.Stop;
animation.Completed += (s, e1) =>
{
CurrentHorizontalOffset = offset;
_isRunning = false;
};
_isRunning = true;
BeginAnimation(CurrentHorizontalOffsetProperty, animation);
}
protected override HitTestResult HitTestCore(PointHitTestParameters hitTestParameters) =>
IsPenetrating ? null : base.HitTestCore(hitTestParameters);
/// <summary>
/// 是否支持惯性
/// </summary>
public static readonly DependencyProperty IsEnableInertiaProperty = DependencyProperty.RegisterAttached(
"IsEnableInertia", typeof(bool), typeof(ScrollViewer), new PropertyMetadata(ValueBoxes.FalseBox));
public static void SetIsEnableInertia(DependencyObject element, bool value)
{
element.SetValue(IsEnableInertiaProperty, value);
}
public static bool GetIsEnableInertia(DependencyObject element)
{
return (bool) element.GetValue(IsEnableInertiaProperty);
}
/// <summary>
/// 是否支持惯性
/// </summary>
public bool IsEnableInertia
{
get => (bool)GetValue(IsEnableInertiaProperty);
set => SetValue(IsEnableInertiaProperty, value);
}
/// <summary>
/// 控件是否可以穿透点击
/// </summary>
public static readonly DependencyProperty IsPenetratingProperty = DependencyProperty.RegisterAttached(
"IsPenetrating", typeof(bool), typeof(ScrollViewer), new PropertyMetadata(ValueBoxes.FalseBox));
/// <summary>
/// 控件是否可以穿透点击
/// </summary>
public bool IsPenetrating
{
get => (bool)GetValue(IsPenetratingProperty);
set => SetValue(IsPenetratingProperty, value);
}
public static void SetIsPenetrating(DependencyObject element, bool value)
{
element.SetValue(IsPenetratingProperty, value);
}
public static bool GetIsPenetrating(DependencyObject element)
{
return (bool) element.GetValue(IsPenetratingProperty);
}
/// <summary>
/// 当前垂直滚动偏移
/// </summary>
private static readonly DependencyProperty CurrentVerticalOffsetProperty = DependencyProperty.Register(
"CurrentVerticalOffset", typeof(double), typeof(ScrollViewer), new PropertyMetadata(default(double), OnCurrentVerticalOffsetChanged));
private static void OnCurrentVerticalOffsetChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is ScrollViewer ctl && e.NewValue is double v)
{
ctl.ScrollToVerticalOffset(v);
}
}
/// <summary>
/// 当前垂直滚动偏移
/// </summary>
private double CurrentVerticalOffset
{
// ReSharper disable once UnusedMember.Local
get => (double) GetValue(CurrentVerticalOffsetProperty);
set => SetValue(CurrentVerticalOffsetProperty, value);
}
/// <summary>
/// 当前水平滚动偏移
/// </summary>
public static readonly DependencyProperty CurrentHorizontalOffsetProperty = DependencyProperty.Register(
"CurrentHorizontalOffset", typeof(double), typeof(ScrollViewer), new PropertyMetadata(default(double), OnCurrentHorizontalOffsetChanged));
private static void OnCurrentHorizontalOffsetChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is ScrollViewer ctl && e.NewValue is double v)
{
ctl.ScrollToHorizontalOffset(v);
}
}
/// <summary>
/// 当前水平滚动偏移
/// </summary>
public double CurrentHorizontalOffset
{
get => (double) GetValue(CurrentHorizontalOffsetProperty);
set => SetValue(CurrentHorizontalOffsetProperty, value);
}
}
}
@@ -0,0 +1,38 @@
using System.Windows;
using System.Windows.Controls.Primitives;
using System.Windows.Markup;
using System.Windows.Media;
namespace HandyControl.Controls
{
[ContentProperty(nameof(Status))]
public class Shield : ButtonBase
{
public static readonly DependencyProperty SubjectProperty = DependencyProperty.Register(
"Subject", typeof(string), typeof(Shield), new PropertyMetadata(default(string)));
public string Subject
{
get => (string) GetValue(SubjectProperty);
set => SetValue(SubjectProperty, value);
}
public static readonly DependencyProperty StatusProperty = DependencyProperty.Register(
"Status", typeof(object), typeof(Shield), new PropertyMetadata(default(object)));
public object Status
{
get => GetValue(StatusProperty);
set => SetValue(StatusProperty, value);
}
public static readonly DependencyProperty ColorProperty = DependencyProperty.Register(
"Color", typeof(Brush), typeof(Shield), new PropertyMetadata(default(Brush)));
public Brush Color
{
get => (Brush) GetValue(ColorProperty);
set => SetValue(ColorProperty, value);
}
}
}
@@ -0,0 +1,70 @@
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using HandyControl.Data;
namespace HandyControl.Controls
{
public class CirclePanel : Panel
{
public static readonly DependencyProperty DiameterProperty = DependencyProperty.Register(
"Diameter", typeof(double), typeof(CirclePanel), new FrameworkPropertyMetadata(170.0, FrameworkPropertyMetadataOptions.AffectsMeasure));
public double Diameter
{
get => (double) GetValue(DiameterProperty);
set => SetValue(DiameterProperty, value);
}
public static readonly DependencyProperty KeepVerticalProperty = DependencyProperty.Register(
"KeepVertical", typeof(bool), typeof(CirclePanel), new FrameworkPropertyMetadata(ValueBoxes.FalseBox, FrameworkPropertyMetadataOptions.AffectsMeasure));
public bool KeepVertical
{
get => (bool) GetValue(KeepVerticalProperty);
set => SetValue(KeepVerticalProperty, value);
}
public static readonly DependencyProperty OffsetAngleProperty = DependencyProperty.Register(
"OffsetAngle", typeof(double), typeof(CirclePanel), new FrameworkPropertyMetadata(ValueBoxes.Double0Box, FrameworkPropertyMetadataOptions.AffectsMeasure));
public double OffsetAngle
{
get => (double) GetValue(OffsetAngleProperty);
set => SetValue(OffsetAngleProperty, value);
}
// ReSharper disable once RedundantAssignment
protected override Size MeasureOverride(Size availableSize)
{
if (Children.Count == 0) return new Size(Diameter, Diameter);
availableSize = new Size(Diameter, Diameter);
var i = 0;
var perDeg = 360.0 / Children.Count;
var radius = Diameter / 2;
foreach (UIElement element in Children)
{
element.Measure(availableSize);
var centerX = element.DesiredSize.Width / 2.0;
var centerY = element.DesiredSize.Height / 2.0;
var angle = perDeg * i++ + OffsetAngle;
var transform = new RotateTransform
{
CenterX = centerX,
CenterY = centerY,
Angle = KeepVertical ? 0 : angle
};
element.RenderTransform = transform;
var r = Math.PI * angle / 180.0;
var x = radius * Math.Cos(r);
var y = radius * Math.Sin(r);
var rectX = x + availableSize.Width / 2 - centerX;
var rectY = y + availableSize.Height / 2 - centerY;
element.Arrange(new Rect(rectX, rectY, element.DesiredSize.Width, element.DesiredSize.Height));
}
return availableSize;
}
}
}
@@ -0,0 +1,85 @@
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using HandyControl.Data;
namespace HandyControl.Controls
{
public class WaterfallPanel : Panel
{
public static readonly DependencyProperty GroupsProperty = DependencyProperty.Register(
"Groups", typeof(int), typeof(WaterfallPanel), new FrameworkPropertyMetadata(ValueBoxes.Int2Box, FrameworkPropertyMetadataOptions.AffectsMeasure), IsGroupsValid);
public int Groups
{
get => (int) GetValue(GroupsProperty);
set => SetValue(GroupsProperty, value);
}
private static bool IsGroupsValid(object value)
{
var v = (int)value;
return v >= 1;
}
public static readonly DependencyProperty OrientationProperty = DependencyProperty.Register(
"Orientation", typeof(Orientation), typeof(WaterfallPanel), new FrameworkPropertyMetadata(
Orientation.Horizontal, FrameworkPropertyMetadataOptions.AffectsMeasure));
public Orientation Orientation
{
get => (Orientation) GetValue(OrientationProperty);
set => SetValue(OrientationProperty, value);
}
protected override Size MeasureOverride(Size constraint)
{
if (Groups < 1) return constraint;
var children = InternalChildren;
Size panelSize;
if (Orientation == Orientation.Horizontal)
{
var heightArr = new double[Groups].ToList();
var itemWidth = constraint.Width / Groups;
if (double.IsNaN(itemWidth) || double.IsInfinity(itemWidth)) return constraint;
for (int i = 0, count = children.Count; i < count; i++)
{
var child = children[i];
if (child == null) continue;
child.Measure(constraint);
var minIndex = heightArr.IndexOf(heightArr.Min());
var minY = heightArr[minIndex];
child.Arrange(new Rect(new Point(minIndex * itemWidth, minY), new Size(itemWidth, child.DesiredSize.Height)));
heightArr[minIndex] = minY + child.DesiredSize.Height;
}
panelSize = new Size(constraint.Width, heightArr.Max());
}
else
{
var widthArr = new double[Groups].ToList();
var itemHeight = constraint.Height / Groups;
if (double.IsNaN(itemHeight) || double.IsInfinity(itemHeight)) return constraint;
for (int i = 0, count = children.Count; i < count; i++)
{
var child = children[i];
if (child == null) continue;
child.Measure(constraint);
var minIndex = widthArr.IndexOf(widthArr.Min());
var minX = widthArr[minIndex];
child.Arrange(new Rect(new Point(minX, minIndex * itemHeight), new Size(child.DesiredSize.Width, itemHeight)));
widthArr[minIndex] = minX + child.DesiredSize.Width;
}
panelSize = new Size(widthArr.Max(), constraint.Height);
}
return panelSize;
}
}
}
@@ -0,0 +1,232 @@
using System.Linq;
using System.Windows;
using System.Windows.Input;
using System.Windows.Media;
using HandyControl.Data;
using HandyControl.Tools;
namespace HandyControl.Controls
{
public class Rate : RegularItemsControl
{
static Rate()
{
ItemWidthProperty.OverrideMetadata(typeof(Rate), new PropertyMetadata(ValueBoxes.Double20Box));
ItemHeightProperty.OverrideMetadata(typeof(Rate), new PropertyMetadata(ValueBoxes.Double20Box));
}
public static readonly DependencyProperty AllowHalfProperty = DependencyProperty.Register(
"AllowHalf", typeof(bool), typeof(Rate), new FrameworkPropertyMetadata(ValueBoxes.FalseBox, FrameworkPropertyMetadataOptions.Inherits));
public static readonly DependencyProperty AllowClearProperty = DependencyProperty.Register(
"AllowClear", typeof(bool), typeof(Rate), new FrameworkPropertyMetadata(ValueBoxes.TrueBox, FrameworkPropertyMetadataOptions.Inherits));
public static readonly DependencyProperty IconProperty = DependencyProperty.Register(
"Icon", typeof(Geometry), typeof(Rate), new FrameworkPropertyMetadata(default(Geometry), FrameworkPropertyMetadataOptions.Inherits));
public static readonly DependencyProperty CountProperty = DependencyProperty.Register(
"Count", typeof(int), typeof(Rate), new PropertyMetadata(ValueBoxes.Int5Box));
public static readonly DependencyProperty DefaultValueProperty = DependencyProperty.Register(
"DefaultValue", typeof(double), typeof(Rate), new PropertyMetadata(ValueBoxes.Double0Box));
public static readonly DependencyProperty ValueProperty = DependencyProperty.Register(
"Value", typeof(double), typeof(Rate), new PropertyMetadata(ValueBoxes.Double0Box));
public static readonly DependencyProperty TextProperty = DependencyProperty.Register(
"Text", typeof(string), typeof(Rate), new PropertyMetadata(default(string)));
public static readonly DependencyProperty ShowTextProperty = DependencyProperty.Register(
"ShowText", typeof(bool), typeof(Rate), new PropertyMetadata(ValueBoxes.FalseBox));
public static readonly DependencyProperty IsReadOnlyProperty = DependencyProperty.Register(
"IsReadOnly", typeof(bool), typeof(Rate), new PropertyMetadata(ValueBoxes.FalseBox));
private bool _isLoaded;
public Rate()
{
AddHandler(RateItem.SelectedChangedEvent, new RoutedEventHandler(RateItemSelectedChanged));
AddHandler(RateItem.ValueChangedEvent, new RoutedEventHandler(RateItemValueChanged));
Loaded += (s, e) =>
{
if (DesignerHelper.IsInDesignMode) return;
if (_isLoaded) return;
_isLoaded = true;
if (Value <= 0)
{
if (DefaultValue > 0)
{
Value = DefaultValue;
UpdateItems();
}
}
else
{
UpdateItems();
}
};
}
public bool AllowHalf
{
get => (bool) GetValue(AllowHalfProperty);
set => SetValue(AllowHalfProperty, value);
}
public bool AllowClear
{
get => (bool) GetValue(AllowClearProperty);
set => SetValue(AllowClearProperty, value);
}
public Geometry Icon
{
get => (Geometry) GetValue(IconProperty);
set => SetValue(IconProperty, value);
}
public int Count
{
get => (int) GetValue(CountProperty);
set => SetValue(CountProperty, value);
}
public double DefaultValue
{
get => (double) GetValue(DefaultValueProperty);
set => SetValue(DefaultValueProperty, value);
}
public double Value
{
get => (double) GetValue(ValueProperty);
set => SetValue(ValueProperty, value);
}
public string Text
{
get => (string) GetValue(TextProperty);
set => SetValue(TextProperty, value);
}
public bool ShowText
{
get => (bool) GetValue(ShowTextProperty);
set => SetValue(ShowTextProperty, value);
}
public bool IsReadOnly
{
get => (bool)GetValue(IsReadOnlyProperty);
set => SetValue(IsReadOnlyProperty, value);
}
private void RateItemValueChanged(object sender, RoutedEventArgs e)
{
Value =
(from RateItem item in Items where item.IsSelected select item.IsHalf ? 0.5 : 1).Sum();
}
private void RateItemSelectedChanged(object sender, RoutedEventArgs e)
{
if (e.OriginalSource is RateItem rateItem)
{
var index = rateItem.Index;
for (var i = 0; i < index; i++)
{
if (Items[i] is RateItem item)
{
item.IsSelected = true;
item.IsHalf = false;
}
}
for (var i = index; i < Count; i++)
{
if (Items[i] is RateItem item)
{
item.IsSelected = false;
item.IsHalf = false;
}
}
}
}
protected override bool IsItemItsOwnContainerOverride(object item)
{
return item is RateItem;
}
protected override DependencyObject GetContainerForItemOverride()
{
return new RateItem();
}
public override void OnApplyTemplate()
{
if (!_isLoaded)
{
Items.Clear();
for (var i = 1; i <= Count; i++)
{
Items.Add(new RateItem
{
Index = i
});
}
}
base.OnApplyTemplate();
}
protected override void OnMouseLeave(MouseEventArgs e)
{
base.OnMouseLeave(e);
UpdateItems();
}
protected override void UpdateItems()
{
if (!_isLoaded) return;
var count = (int) Value;
for (var i = 0; i < count; i++)
{
if (Items[i] is RateItem rateItem)
{
rateItem.IsSelected = true;
rateItem.IsHalf = false;
}
}
if (Value > count)
{
if (Items[count] is RateItem rateItem)
{
rateItem.IsSelected = true;
rateItem.IsHalf = true;
}
count += 1;
}
for (var i = count; i < Count; i++)
{
if (Items[i] is RateItem rateItem)
{
rateItem.IsSelected = false;
rateItem.IsHalf = false;
}
}
}
public void Reset()
{
Value = DefaultValue;
}
}
}
@@ -0,0 +1,215 @@
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using HandyControl.Data;
using HandyControl.Tools.Extension;
namespace HandyControl.Controls
{
[TemplatePart(Name = ElementIcon, Type = typeof(FrameworkElement))]
public class RateItem : Control
{
private const string ElementIcon = "PART_Icon";
public static readonly DependencyProperty AllowClearProperty =
Rate.AllowClearProperty.AddOwner(typeof(RateItem));
public static readonly DependencyProperty AllowHalfProperty =
Rate.AllowHalfProperty.AddOwner(typeof(RateItem), new PropertyMetadata(OnAllowHalfChanged));
public static readonly DependencyProperty IconProperty = Rate.IconProperty.AddOwner(typeof(RateItem));
public static readonly DependencyProperty IsReadOnlyProperty = Rate.IsReadOnlyProperty.AddOwner(typeof(RateItem));
internal static readonly DependencyProperty IsSelectedProperty = DependencyProperty.Register(
"IsSelected", typeof(bool), typeof(RateItem),
new PropertyMetadata(ValueBoxes.FalseBox, OnIsSelectedChanged));
public static readonly RoutedEvent SelectedChangedEvent =
EventManager.RegisterRoutedEvent("SelectedChanged", RoutingStrategy.Bubble,
typeof(RoutedEventHandler), typeof(RateItem));
public static readonly RoutedEvent ValueChangedEvent =
EventManager.RegisterRoutedEvent("ValueChanged", RoutingStrategy.Bubble,
typeof(RoutedEventHandler), typeof(RateItem));
private FrameworkElement _icon;
private bool _isHalf;
private bool _isLoaded;
private bool _isMouseLeftButtonDown;
private bool _isSentValue;
public RateItem()
{
Loaded += (s, e) => _isLoaded = true;
}
public bool AllowClear
{
get => (bool) GetValue(AllowClearProperty);
set => SetValue(AllowClearProperty, value);
}
public bool AllowHalf
{
get => (bool) GetValue(AllowHalfProperty);
set => SetValue(AllowHalfProperty, value);
}
public Geometry Icon
{
get => (Geometry) GetValue(IconProperty);
set => SetValue(IconProperty, value);
}
internal bool IsSelected
{
get => (bool) GetValue(IsSelectedProperty);
set => SetValue(IsSelectedProperty, value);
}
public bool IsReadOnly
{
get => (bool)GetValue(IsReadOnlyProperty);
set => SetValue(IsReadOnlyProperty, value);
}
internal bool IsHalf
{
get => _isHalf;
set
{
if (_isHalf == value) return;
_isHalf = value;
if (_icon == null) return;
_icon.Width = value ? ActualWidth / 2 : ActualWidth;
}
}
internal int Index { get; set; }
private static void OnAllowHalfChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var ctl = (RateItem) d;
ctl.HandleMouseMoveEvent((bool) e.NewValue);
}
private static void OnIsSelectedChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var ctl = (RateItem) d;
ctl._icon?.Show((bool) e.NewValue);
}
public event RoutedEventHandler SelectedChanged
{
add => AddHandler(SelectedChangedEvent, value);
remove => RemoveHandler(SelectedChangedEvent, value);
}
public event RoutedEventHandler ValueChanged
{
add => AddHandler(ValueChangedEvent, value);
remove => RemoveHandler(ValueChangedEvent, value);
}
private void HandleMouseMoveEvent(bool handle)
{
if (handle)
{
MouseMove += RateItem_MouseMove;
}
else
{
MouseMove -= RateItem_MouseMove;
}
}
private void RateItem_MouseMove(object sender, MouseEventArgs e)
{
if (IsReadOnly) return;
if (!AllowHalf) return;
var p = e.GetPosition(this);
IsHalf = p.X < ActualWidth / 2;
}
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
_icon = GetTemplateChild(ElementIcon) as FrameworkElement;
if (_isLoaded)
{
if (_icon == null) return;
_icon.Show(IsSelected);
_icon.Width = IsHalf ? ActualWidth / 2 : ActualWidth;
}
}
protected override void OnMouseEnter(MouseEventArgs e)
{
base.OnMouseEnter(e);
if (IsReadOnly) return;
_isSentValue = false;
IsSelected = true;
RaiseEvent(new RoutedEventArgs(SelectedChangedEvent) {Source = this});
}
protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e)
{
base.OnMouseLeftButtonDown(e);
if (IsReadOnly) return;
_isMouseLeftButtonDown = true;
}
protected override void OnMouseLeave(MouseEventArgs e)
{
base.OnMouseLeave(e);
if (IsReadOnly) return;
_isMouseLeftButtonDown = false;
}
protected override void OnMouseLeftButtonUp(MouseButtonEventArgs e)
{
base.OnMouseLeftButtonUp(e);
if (IsReadOnly) return;
if (_isMouseLeftButtonDown)
{
if (Index == 1 && AllowClear)
{
if (IsSelected)
{
if (!_isSentValue)
{
RaiseEvent(new RoutedEventArgs(ValueChangedEvent) {Source = this});
_isMouseLeftButtonDown = false;
_isSentValue = true;
return;
}
_isSentValue = false;
IsSelected = false;
IsHalf = false;
}
else
{
IsSelected = true;
if (AllowHalf)
{
var p = e.GetPosition(this);
IsHalf = p.X < ActualWidth / 2;
}
}
}
RaiseEvent(new RoutedEventArgs(ValueChangedEvent) {Source = this});
_isMouseLeftButtonDown = false;
}
}
}
}
@@ -0,0 +1,59 @@
using System.Windows;
using System.Windows.Controls;
namespace HandyControl.Controls
{
public class CompareSlider : Slider
{
public static readonly DependencyProperty TargetContentProperty = DependencyProperty.Register(
"TargetContent", typeof(object), typeof(CompareSlider), new PropertyMetadata(default(object)));
public static readonly DependencyProperty SourceContentProperty = DependencyProperty.Register(
"SourceContent", typeof(object), typeof(CompareSlider), new PropertyMetadata(default(object)));
public object TargetContent
{
get => GetValue(TargetContentProperty);
set => SetValue(TargetContentProperty, value);
}
public object SourceContent
{
get => GetValue(SourceContentProperty);
set => SetValue(SourceContentProperty, value);
}
//private bool _isLoaded;
//private double _tempValue;
//public CompareSlider()
//{
// //Loaded += delegate
// //{
// // if (_isLoaded) return;
// // _isLoaded = true;
// // Value = _tempValue;
// //};
//}
////protected override void OnRender(DrawingContext drawingContext)
////{
//// base.OnRender(drawingContext);
//// Value = _tempValue;
////}
////public override void OnApplyTemplate()
////{
//// base.OnApplyTemplate();
//// _tempValue = Value;
//// Value = Orientation == Orientation.Horizontal ? Maximum : 0;
//// if (_isLoaded)
//// {
//// Value = _tempValue;
//// }
////}
}
}
@@ -0,0 +1,117 @@
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
namespace HandyControl.Controls
{
public class CompareTrack : Track
{
protected override Size ArrangeOverride(Size arrangeSize)
{
base.ArrangeOverride(arrangeSize);
var isVertical = Orientation == Orientation.Vertical;
ComputeSliderLengths(arrangeSize, isVertical, out var decreaseButtonLength, out var thumbLength,
out var increaseButtonLength);
var offset = new Point();
var pieceSize = arrangeSize;
var isDirectionReversed = IsDirectionReversed;
if (isVertical)
{
CoerceLength(ref decreaseButtonLength, arrangeSize.Height);
CoerceLength(ref increaseButtonLength, arrangeSize.Height);
CoerceLength(ref thumbLength, arrangeSize.Height);
offset.Y = isDirectionReversed ? decreaseButtonLength + thumbLength : 0.0;
pieceSize.Height = increaseButtonLength;
IncreaseRepeatButton?.Arrange(new Rect(offset, pieceSize));
offset.Y = isDirectionReversed ? 0.0 : increaseButtonLength + thumbLength;
pieceSize.Height = decreaseButtonLength;
if (DecreaseRepeatButton != null)
{
DecreaseRepeatButton.Arrange(new Rect(offset, pieceSize));
DecreaseRepeatButton.Height = pieceSize.Height;
}
offset.Y = isDirectionReversed ? decreaseButtonLength : increaseButtonLength;
pieceSize.Height = thumbLength;
Thumb?.Arrange(new Rect(offset, pieceSize));
}
else
{
CoerceLength(ref decreaseButtonLength, arrangeSize.Width);
CoerceLength(ref increaseButtonLength, arrangeSize.Width);
CoerceLength(ref thumbLength, arrangeSize.Width);
offset.X = isDirectionReversed ? increaseButtonLength + thumbLength : 0.0;
pieceSize.Width = decreaseButtonLength;
DecreaseRepeatButton?.Arrange(new Rect(offset, pieceSize));
offset.X = isDirectionReversed ? 0.0 : decreaseButtonLength + thumbLength;
pieceSize.Width = increaseButtonLength;
if (IncreaseRepeatButton != null)
{
IncreaseRepeatButton.Arrange(new Rect(offset, pieceSize));
IncreaseRepeatButton.Width = pieceSize.Width;
}
offset.X = isDirectionReversed ? increaseButtonLength : decreaseButtonLength;
pieceSize.Width = thumbLength;
Thumb?.Arrange(new Rect(offset, pieceSize));
}
return arrangeSize;
}
private void ComputeSliderLengths(Size arrangeSize, bool isVertical, out double decreaseButtonLength,
out double thumbLength, out double increaseButtonLength)
{
var min = Minimum;
var range = Math.Max(0.0, Maximum - min);
var offset = Math.Min(range, Value - min);
double trackLength;
// Compute thumb size
if (isVertical)
{
trackLength = arrangeSize.Height;
thumbLength = Thumb?.DesiredSize.Height ?? 0;
}
else
{
trackLength = arrangeSize.Width;
thumbLength = Thumb?.DesiredSize.Width ?? 0;
}
CoerceLength(ref thumbLength, trackLength);
var remainingTrackLength = trackLength - thumbLength;
decreaseButtonLength = remainingTrackLength * offset / range;
CoerceLength(ref decreaseButtonLength, remainingTrackLength);
increaseButtonLength = remainingTrackLength - decreaseButtonLength;
CoerceLength(ref increaseButtonLength, remainingTrackLength);
}
private static void CoerceLength(ref double componentLength, double trackLength)
{
if (componentLength < 0)
componentLength = 0.0;
else if (componentLength > trackLength || double.IsNaN(componentLength))
componentLength = trackLength;
}
}
}
@@ -0,0 +1,93 @@
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Input;
using System.Windows.Media;
using HandyControl.Data;
namespace HandyControl.Controls
{
[TemplatePart(Name = PreviewContentKey, Type = typeof(ContentControl))]
[TemplatePart(Name = TracKKey, Type = typeof(Track))]
public class PreviewSlider : Slider
{
private const string PreviewContentKey = "PART_ContentBorder";
private const string TracKKey = "PART_Track";
private ContentControl _previewContent;
private TranslateTransform _transform;
private Track _track;
/// <summary>
/// 预览内容
/// </summary>
public static readonly DependencyProperty PreviewContentProperty = DependencyProperty.Register(
"PreviewContent", typeof(object), typeof(PreviewSlider), new PropertyMetadata(default(object)));
/// <summary>
/// 预览内容
/// </summary>
public object PreviewContent
{
get => GetValue(PreviewContentProperty);
set => SetValue(PreviewContentProperty, value);
}
public static readonly DependencyProperty PreviewPositionProperty = DependencyProperty.Register(
"PreviewPosition", typeof(double), typeof(PreviewSlider), new PropertyMetadata(ValueBoxes.Double0Box));
public double PreviewPosition
{
get => (double) GetValue(PreviewPositionProperty);
set => SetValue(PreviewPositionProperty, value);
}
/// <summary>
/// 值改变事件
/// </summary>
public static readonly RoutedEvent PreviewPositionChangedEvent =
EventManager.RegisterRoutedEvent("PreviewPositionChanged", RoutingStrategy.Bubble,
typeof(EventHandler<FunctionEventArgs<double>>), typeof(PreviewSlider));
/// <summary>
/// 值改变事件
/// </summary>
public event EventHandler<FunctionEventArgs<double>> PreviewPositionChanged
{
add => AddHandler(PreviewPositionChangedEvent, value);
remove => RemoveHandler(PreviewPositionChangedEvent, value);
}
protected override void OnMouseMove(MouseEventArgs e)
{
base.OnMouseMove(e);
var p = e.GetPosition(_track);
_transform.X = p.X - _previewContent.ActualWidth/2;
PreviewPosition = p.X / _track.ActualWidth * Maximum;
RaiseEvent(new FunctionEventArgs<double>(PreviewPositionChangedEvent, this)
{
Info = PreviewPosition
});
}
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
_previewContent = Template.FindName(PreviewContentKey, this) as ContentControl;
_track = Template.FindName(TracKKey, this) as Track;
if (_previewContent != null)
{
_transform = new TranslateTransform();
_previewContent.RenderTransform = _transform;
}
}
}
}
@@ -0,0 +1,169 @@
using System;
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Input;
using System.Windows.Media;
using HandyControl.Data;
using HandyControl.Interactivity;
using HandyControl.Tools;
namespace HandyControl.Controls
{
[StyleTypedProperty(Property = "ItemContainerStyle", StyleTargetType = typeof(StepBarItem))]
[DefaultEvent("StepChanged")]
[TemplatePart(Name = ElementProgressBarBack, Type = typeof(ProgressBar))]
public class StepBar : ItemsControl
{
private ProgressBar _progressBarBack;
#region Constants
private const string ElementProgressBarBack = "PART_ProgressBarBack";
#endregion Constants
public StepBar()
{
CommandBindings.Add(new CommandBinding(ControlCommands.Next, (s, e) => Next()));
CommandBindings.Add(new CommandBinding(ControlCommands.Prev, (s, e) => Prev()));
ItemContainerGenerator.StatusChanged += ItemContainerGenerator_StatusChanged;
}
private void ItemContainerGenerator_StatusChanged(object sender, EventArgs e)
{
if (ItemContainerGenerator.Status == GeneratorStatus.ContainersGenerated)
{
var count = Items.Count;
if (count <= 0) return;
for (int i = 0; i < count; i++)
{
if (ItemContainerGenerator.ContainerFromIndex(i) is StepBarItem stepBarItem)
{
stepBarItem.Index = i + 1;
}
}
if (StepIndex < count)
{
for (int i = 0; i < StepIndex; i++)
{
if (ItemContainerGenerator.ContainerFromIndex(i) is StepBarItem stepBarItem)
{
stepBarItem.Status = StepStatus.Complete;
}
}
if (ItemContainerGenerator.ContainerFromIndex(StepIndex) is StepBarItem item)
{
item.Status = StepStatus.UnderWay;
}
}
}
}
protected override bool IsItemItsOwnContainerOverride(object item) => item is StepBarItem;
protected override DependencyObject GetContainerForItemOverride() => new StepBarItem();
/// <summary>
/// 步骤改变事件
/// </summary>
public static readonly RoutedEvent StepChangedEvent =
EventManager.RegisterRoutedEvent("StepChanged", RoutingStrategy.Bubble,
typeof(EventHandler<FunctionEventArgs<int>>), typeof(StepBar));
/// <summary>
/// 步骤改变事件
/// </summary>
[Category("Behavior")]
public event EventHandler<FunctionEventArgs<int>> StepChanged
{
add => AddHandler(StepChangedEvent, value);
remove => RemoveHandler(StepChangedEvent, value);
}
public static readonly DependencyProperty StepIndexProperty = DependencyProperty.Register(
"StepIndex", typeof(int), typeof(StepBar), new PropertyMetadata(default(int)));
public int StepIndex
{
get => (int)GetValue(StepIndexProperty);
protected set => SetValue(StepIndexProperty, value);
}
public static readonly DependencyProperty DockProperty = DependencyProperty.Register(
"Dock", typeof(Dock), typeof(StepBar), new PropertyMetadata(Dock.Top));
public Dock Dock
{
get => (Dock)GetValue(DockProperty);
set => SetValue(DockProperty, value);
}
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
_progressBarBack = GetTemplateChild(ElementProgressBarBack) as ProgressBar;
}
protected override void OnRender(DrawingContext drawingContext)
{
base.OnRender(drawingContext);
var colCount = Items.Count;
if (_progressBarBack == null || colCount <= 0) return;
_progressBarBack.Maximum = colCount - 1;
_progressBarBack.Value = StepIndex;
if (Dock == Dock.Top || Dock == Dock.Bottom)
{
_progressBarBack.Width = (colCount - 1) * (ActualWidth / colCount);
}
else
{
_progressBarBack.Height = (colCount - 1) * (ActualHeight / colCount);
}
}
public void Next()
{
StepIndex++;
if (StepIndex >= Items.Count)
{
StepIndex = Items.Count - 1;
return;
}
RaiseEvent(new FunctionEventArgs<int>(StepChangedEvent, this)
{
Info = StepIndex
});
if (ItemContainerGenerator.ContainerFromIndex(StepIndex - 1) is StepBarItem stepItemFinished)
stepItemFinished.Status = StepStatus.Complete;
if (ItemContainerGenerator.ContainerFromIndex(StepIndex) is StepBarItem stepItemSelected)
stepItemSelected.Status = StepStatus.UnderWay;
_progressBarBack?.BeginAnimation(RangeBase.ValueProperty, AnimationHelper.CreateAnimation(StepIndex));
}
public void Prev()
{
StepIndex--;
if (StepIndex < 0)
{
StepIndex = 0;
return;
}
RaiseEvent(new FunctionEventArgs<int>(StepChangedEvent, this)
{
Info = StepIndex
});
if (ItemContainerGenerator.ContainerFromIndex(StepIndex + 1) is StepBarItem stepItemWaiting)
stepItemWaiting.Status = StepStatus.Waiting;
if (ItemContainerGenerator.ContainerFromIndex(StepIndex) is StepBarItem stepItemSelected)
stepItemSelected.Status = StepStatus.UnderWay;
_progressBarBack?.BeginAnimation(RangeBase.ValueProperty, AnimationHelper.CreateAnimation(StepIndex));
}
}
}
@@ -0,0 +1,42 @@
using System.Windows;
using System.Windows.Controls;
using HandyControl.Data;
namespace HandyControl.Controls
{
/// <summary>
/// 步骤条单元项
/// </summary>
public class StepBarItem : ContentControl
{
/// <summary>
/// 步骤编号
/// </summary>
public static readonly DependencyProperty IndexProperty = DependencyProperty.Register(
"Index", typeof(int), typeof(StepBarItem), new PropertyMetadata(-1));
/// <summary>
/// 步骤编号
/// </summary>
public int Index
{
get => (int) GetValue(IndexProperty);
internal set => SetValue(IndexProperty, value);
}
/// <summary>
/// 步骤状态
/// </summary>
public static readonly DependencyProperty StatusProperty = DependencyProperty.Register(
"Status", typeof(StepStatus), typeof(StepBarItem), new PropertyMetadata(StepStatus.Waiting));
/// <summary>
/// 步骤状态
/// </summary>
public StepStatus Status
{
get => (StepStatus) GetValue(StatusProperty);
internal set => SetValue(StatusProperty, value);
}
}
}
@@ -0,0 +1,227 @@
using System;
using System.Collections.Specialized;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using HandyControl.Data;
using HandyControl.Tools.Extension;
namespace HandyControl.Controls
{
[TemplatePart(Name = OverflowButtonKey, Type = typeof(ContextMenuToggleButton))]
[TemplatePart(Name = HeaderPanelKey, Type = typeof(TabPanel))]
public class TabControl : System.Windows.Controls.TabControl
{
private const string OverflowButtonKey = "PART_OverflowButton";
private const string HeaderPanelKey = "PART_HeaderPanel";
private ContextMenuToggleButton _buttonOverflow;
private TabPanel _headerPanel;
/// <summary>
/// 是否为内部操作
/// </summary>
internal bool IsInternalAction;
/// <summary>
/// 是否启用动画
/// </summary>
public static readonly DependencyProperty IsEnableAnimationProperty = DependencyProperty.Register(
"IsEnableAnimation", typeof(bool), typeof(TabControl), new PropertyMetadata(ValueBoxes.FalseBox));
/// <summary>
/// 是否启用动画
/// </summary>
public bool IsEnableAnimation
{
get => (bool)GetValue(IsEnableAnimationProperty);
set => SetValue(IsEnableAnimationProperty, value);
}
/// <summary>
/// 是否可以拖动
/// </summary>
public static readonly DependencyProperty IsDraggableProperty = DependencyProperty.Register(
"IsDraggable", typeof(bool), typeof(TabControl), new PropertyMetadata(ValueBoxes.FalseBox));
/// <summary>
/// 是否可以拖动
/// </summary>
public bool IsDraggable
{
get => (bool)GetValue(IsDraggableProperty);
set => SetValue(IsDraggableProperty, value);
}
/// <summary>
/// 是否显示关闭按钮
/// </summary>
public static readonly DependencyProperty ShowCloseButtonProperty = DependencyProperty.Register(
"ShowCloseButton", typeof(bool), typeof(TabControl), new PropertyMetadata(ValueBoxes.FalseBox));
/// <summary>
/// 是否显示关闭按钮
/// </summary>
public bool ShowCloseButton
{
get => (bool)GetValue(ShowCloseButtonProperty);
set => SetValue(ShowCloseButtonProperty, value);
}
/// <summary>
/// 是否将标签填充
/// </summary>
public static readonly DependencyProperty IsEnableTabFillProperty = DependencyProperty.Register(
"IsEnableTabFill", typeof(bool), typeof(TabControl), new PropertyMetadata(ValueBoxes.FalseBox));
/// <summary>
/// 是否将标签填充
/// </summary>
public bool IsEnableTabFill
{
get => (bool)GetValue(IsEnableTabFillProperty);
set => SetValue(IsEnableTabFillProperty, value);
}
/// <summary>
/// 标签宽度
/// </summary>
public static readonly DependencyProperty TabItemWidthProperty = DependencyProperty.Register(
"TabItemWidth", typeof(double), typeof(TabControl), new PropertyMetadata(200.0));
/// <summary>
/// 标签宽度
/// </summary>
public double TabItemWidth
{
get => (double)GetValue(TabItemWidthProperty);
set => SetValue(TabItemWidthProperty, value);
}
/// <summary>
/// 标签高度
/// </summary>
public static readonly DependencyProperty TabItemHeightProperty = DependencyProperty.Register(
"TabItemHeight", typeof(double), typeof(TabControl), new PropertyMetadata(30.0));
/// <summary>
/// 标签高度
/// </summary>
public double TabItemHeight
{
get => (double)GetValue(TabItemHeightProperty);
set => SetValue(TabItemHeightProperty, value);
}
/// <summary>
/// 可见的标签数量
/// </summary>
private int _itemShowCount;
protected override void OnItemsChanged(NotifyCollectionChangedEventArgs e)
{
base.OnItemsChanged(e);
if (_headerPanel == null)
{
IsInternalAction = false;
return;
}
if (!IsEnableTabFill)
{
_itemShowCount = (int)(ActualWidth / TabItemWidth);
_buttonOverflow.Show(Items.Count > 0 && Items.Count >= _itemShowCount);
}
if (IsInternalAction)
{
IsInternalAction = false;
return;
}
if (IsEnableAnimation)
{
_headerPanel.ClearValue(TabPanel.FluidMoveDurationProperty);
}
else
{
_headerPanel.FluidMoveDuration = new Duration(TimeSpan.FromSeconds(0));
}
if (e.Action == NotifyCollectionChangedAction.Add)
{
foreach (TabItem item in e.NewItems)
{
item.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
item.IsSelected = true;
item.TabPanel = _headerPanel;
}
}
IsInternalAction = false;
}
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
_headerPanel = Template.FindName(HeaderPanelKey, this) as TabPanel;
if (IsEnableTabFill) return;
_buttonOverflow = Template.FindName(OverflowButtonKey, this) as ContextMenuToggleButton;
if (_buttonOverflow != null)
{
_itemShowCount = (int)(ActualWidth / TabItemWidth);
_buttonOverflow.Show(Items.Count > 0 && Items.Count >= _itemShowCount);
var menu = new ContextMenu
{
Placement = PlacementMode.Bottom,
PlacementTarget = _buttonOverflow
};
menu.Closed += (s, e) => _buttonOverflow.IsChecked = false;
_buttonOverflow.Menu = menu;
_buttonOverflow.Click += (s, e) =>
{
if (_buttonOverflow.IsChecked == true)
{
_buttonOverflow.Menu.Items.Clear();
foreach (TabItem item in Items)
{
var menuItem = new MenuItem
{
Header = item.Header,
Width = TabItemWidth,
IsChecked = item.IsSelected,
IsCheckable = true
};
menuItem.Click += delegate
{
_buttonOverflow.IsChecked = false;
var index = Items.IndexOf(item);
if (index >= _itemShowCount)
{
Items.Remove(item);
Items.Insert(0, item);
if (IsEnableAnimation)
{
_headerPanel.ClearValue(TabPanel.FluidMoveDurationProperty);
}
else
{
_headerPanel.FluidMoveDuration = new Duration(TimeSpan.FromSeconds(0));
}
_headerPanel.ForceUpdate = true;
_headerPanel.Measure(new Size(_headerPanel.DesiredSize.Width, ActualHeight));
_headerPanel.ForceUpdate = false;
}
item.IsSelected = true;
};
_buttonOverflow.Menu.Items.Add(menuItem);
}
}
};
}
}
}
}
@@ -0,0 +1,296 @@
using System;
using System.Collections;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using HandyControl.Interactivity;
using HandyControl.Tools;
namespace HandyControl.Controls
{
public class TabItem : System.Windows.Controls.TabItem
{
/// <summary>
/// 动画速度
/// </summary>
private const int AnimationSpeed = 150;
/// <summary>
/// 选项卡是否处于拖动状态
/// </summary>
private static bool ItemIsDragging;
/// <summary>
/// 选项卡是否等待被拖动
/// </summary>
private bool _isWaiting;
/// <summary>
/// 拖动中的选项卡坐标
/// </summary>
private Point _dragPoint;
/// <summary>
/// 鼠标按下时选项卡位置
/// </summary>
private int _mouseDownIndex;
/// <summary>
/// 鼠标按下时选项卡横向偏移
/// </summary>
private double _mouseDownOffsetX;
/// <summary>
/// 鼠标按下时的坐标
/// </summary>
private Point _mouseDownPoint;
/// <summary>
/// 右侧可移动的最大值
/// </summary>
private double _maxMoveRight;
/// <summary>
/// 左侧可移动的最大值
/// </summary>
private double _maxMoveLeft;
/// <summary>
/// 选项卡宽度
/// </summary>
public double ItemWidth { get; internal set; }
/// <summary>
/// 选项卡拖动等待距离(在鼠标移动了超过20个像素无关单位后,选项卡才开始被拖动)
/// </summary>
private const double WaitLength = 20;
/// <summary>
/// 选项卡是否处于拖动状态
/// </summary>
private bool _isDragging;
/// <summary>
/// 选项卡是否已经被拖动
/// </summary>
private bool _isDragged;
/// <summary>
/// 目标横向位移
/// </summary>
internal double TargetOffsetX { get; set; }
/// <summary>
/// 当前编号
/// </summary>
private int _currentIndex;
/// <summary>
/// 标签容器
/// </summary>
internal TabPanel TabPanel { get; set; }
/// <summary>
/// 当前编号
/// </summary>
internal int CurrentIndex
{
get => _currentIndex;
set
{
if (_currentIndex == value || value < 0) return;
var oldIndex = _currentIndex;
_currentIndex = value;
UpdateItemOffsetX(oldIndex);
}
}
/// <summary>
/// 更新选项卡横向偏移
/// </summary>
/// <param name="oldIndex"></param>
private void UpdateItemOffsetX(int oldIndex)
{
if (!_isDragging) return;
var moveItem = TabPanel.ItemDic[CurrentIndex];
moveItem.CurrentIndex -= CurrentIndex - oldIndex;
var offsetX = moveItem.TargetOffsetX;
var resultX = offsetX + (oldIndex - CurrentIndex) * ItemWidth;
TabPanel.ItemDic[CurrentIndex] = this;
TabPanel.ItemDic[moveItem.CurrentIndex] = moveItem;
moveItem.CreateAnimation(offsetX, resultX);
}
public TabItem()
{
CommandBindings.Add(new CommandBinding(ControlCommands.Close, Close));
CommandBindings.Add(new CommandBinding(ControlCommands.CloseAll, (s, e) =>
{
TabControlParent.IsInternalAction = true;
TabControlParent.Items.Clear();
}));
CommandBindings.Add(new CommandBinding(ControlCommands.CloseOther, (s, e) =>
{
TabControlParent.IsInternalAction = true;
var enumerator = ((IEnumerable)TabControlParent.Items).GetEnumerator();
while (enumerator.MoveNext())
{
var item = enumerator.Current;
if (!Equals(item) && item != null)
{
TabControlParent.Items.Remove(item);
enumerator = ((IEnumerable)TabControlParent.Items).GetEnumerator();
}
}
}));
}
private TabControl _tabControlParent;
private TabControl TabControlParent => _tabControlParent ?? (_tabControlParent = ItemsControl.ItemsControlFromItemContainer(this) as TabControl);
protected override void OnMouseRightButtonDown(MouseButtonEventArgs e)
{
base.OnMouseRightButtonDown(e);
IsSelected = true;
Focus();
}
/// <summary>
/// 关闭
/// </summary>
private void Close(object sender, RoutedEventArgs e)
{
if (TabControlParent.IsEnableAnimation)
{
TabPanel.ClearValue(TabPanel.FluidMoveDurationProperty);
}
else
{
TabPanel.FluidMoveDuration = new Duration(TimeSpan.FromSeconds(0));
}
TabControlParent.IsInternalAction = true;
TabControlParent.Items.Remove(this);
}
protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e)
{
base.OnMouseLeftButtonDown(e);
if (TabControlParent.IsDraggable && !ItemIsDragging && !_isDragging)
{
TabPanel.FluidMoveDuration = new Duration(TimeSpan.FromSeconds(0));
_mouseDownOffsetX = RenderTransform.Value.OffsetX;
var mx = TranslatePoint(new Point(), TabControlParent).X;
_mouseDownIndex = CalLocationIndex(mx);
_maxMoveLeft = -_mouseDownIndex * ItemWidth;
_maxMoveRight = TabControlParent.ActualWidth - ActualWidth + _maxMoveLeft;
_isDragging = true;
ItemIsDragging = true;
_isWaiting = true;
_dragPoint = e.GetPosition(TabControlParent);
_mouseDownPoint = _dragPoint;
CaptureMouse();
}
}
protected override void OnMouseMove(MouseEventArgs e)
{
base.OnMouseMove(e);
if (ItemIsDragging && _isDragging)
{
var subX = TranslatePoint(new Point(), TabControlParent).X;
CurrentIndex = CalLocationIndex(subX);
var p = e.GetPosition(TabControlParent);
var subLeft = p.X - _dragPoint.X;
var totalLeft = p.X - _mouseDownPoint.X;
if (Math.Abs(subLeft) <= WaitLength && _isWaiting) return;
_isWaiting = false;
_isDragged = true;
var left = subLeft + RenderTransform.Value.OffsetX;
if (totalLeft < _maxMoveLeft)
{
left = _maxMoveLeft + _mouseDownOffsetX;
}
else if (totalLeft > _maxMoveRight)
{
left = _maxMoveRight + _mouseDownOffsetX;
}
var t = new TranslateTransform(left, 0);
RenderTransform = t;
_dragPoint = p;
}
}
protected override void OnMouseLeftButtonUp(MouseButtonEventArgs e)
{
base.OnMouseLeftButtonUp(e);
ReleaseMouseCapture();
if (_isDragged)
{
var subX = TranslatePoint(new Point(), TabControlParent).X;
var index = CalLocationIndex(subX);
var left = index * ItemWidth;
var offsetX = RenderTransform.Value.OffsetX;
CreateAnimation(offsetX, offsetX - subX + left, index);
}
_isDragging = false;
ItemIsDragging = false;
_isDragged = false;
}
/// <summary>
/// 创建动画
/// </summary>
internal void CreateAnimation(double offsetX, double resultX, int index = -1)
{
void AnimationCompleted()
{
RenderTransform = new TranslateTransform(resultX, 0);
if (index == -1) return;
var parent = TabControlParent;
TabPanel.CanUpdate = false;
parent.IsInternalAction = true;
parent.Items.Remove(this);
parent.IsInternalAction = true;
parent.Items.Insert(index, this);
TabPanel.CanUpdate = true;
TabPanel.ForceUpdate = true;
TabPanel.Measure(new Size(TabPanel.DesiredSize.Width, ActualHeight));
TabPanel.ForceUpdate = false;
Focus();
IsSelected = true;
}
TargetOffsetX = resultX;
if (!TabControlParent.IsEnableAnimation)
{
AnimationCompleted();
return;
}
var animation = AnimationHelper.CreateAnimation(resultX, AnimationSpeed);
animation.FillBehavior = FillBehavior.Stop;
animation.Completed += (s1, e1) => AnimationCompleted();
var f = new TranslateTransform(offsetX, 0);
RenderTransform = f;
f.BeginAnimation(TranslateTransform.XProperty, animation);
}
/// <summary>
/// 计算选项卡当前合适的位置编号
/// </summary>
/// <param name="left"></param>
/// <returns></returns>
private int CalLocationIndex(double left)
{
var maxIndex = TabControlParent.Items.Count - 1;
var div = (int)(left / ItemWidth);
var rest = left % ItemWidth;
var result = rest / ItemWidth > .5 ? div + 1 : div;
return result > maxIndex ? maxIndex : result;
}
}
}
@@ -0,0 +1,175 @@
using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using HandyControl.Data;
using HandyControl.Tools;
namespace HandyControl.Controls
{
public class TabPanel : Panel
{
private int _itemCount;
/// <summary>
/// 是否可以更新
/// </summary>
internal bool CanUpdate = true;
/// <summary>
/// 选项卡字典
/// </summary>
internal Dictionary<int, TabItem> ItemDic = new Dictionary<int, TabItem>();
/// <summary>
/// 流式行为持续时间
/// </summary>
public static readonly DependencyProperty FluidMoveDurationProperty = DependencyProperty.Register(
"FluidMoveDuration", typeof(Duration), typeof(TabPanel), new PropertyMetadata(new Duration(TimeSpan.FromMilliseconds(200))));
/// <summary>
/// 流式行为持续时间
/// </summary>
public Duration FluidMoveDuration
{
get => (Duration)GetValue(FluidMoveDurationProperty);
set => SetValue(FluidMoveDurationProperty, value);
}
/// <summary>
/// 是否将标签填充
/// </summary>
public static readonly DependencyProperty IsEnableTabFillProperty = DependencyProperty.Register(
"IsEnableTabFill", typeof(bool), typeof(TabPanel), new PropertyMetadata(ValueBoxes.FalseBox));
/// <summary>
/// 是否将标签填充
/// </summary>
public bool IsEnableTabFill
{
get => (bool)GetValue(IsEnableTabFillProperty);
set => SetValue(IsEnableTabFillProperty, value);
}
/// <summary>
/// 标签宽度
/// </summary>
public static readonly DependencyProperty TabItemWidthProperty = DependencyProperty.Register(
"TabItemWidth", typeof(double), typeof(TabPanel), new PropertyMetadata(200.0));
/// <summary>
/// 标签宽度
/// </summary>
public double TabItemWidth
{
get => (double)GetValue(TabItemWidthProperty);
set => SetValue(TabItemWidthProperty, value);
}
/// <summary>
/// 标签高度
/// </summary>
public static readonly DependencyProperty TabItemHeightProperty = DependencyProperty.Register(
"TabItemHeight", typeof(double), typeof(TabPanel), new PropertyMetadata(30.0));
/// <summary>
/// 标签高度
/// </summary>
public double TabItemHeight
{
get => (double)GetValue(TabItemHeightProperty);
set => SetValue(TabItemHeightProperty, value);
}
/// <summary>
/// 是否可以强制更新
/// </summary>
internal bool ForceUpdate;
private Size _oldSize;
/// <summary>
/// 是否已经加载
/// </summary>
private bool _isLoaded;
protected override Size MeasureOverride(Size constraint)
{
if ((_itemCount == InternalChildren.Count || !CanUpdate) && !ForceUpdate) return _oldSize;
constraint.Height = TabItemHeight;
_itemCount = InternalChildren.Count;
var size = new Size();
ItemDic.Clear();
var count = InternalChildren.Count;
if (count == 0)
{
_oldSize = new Size();
return _oldSize;
}
constraint.Width += InternalChildren.Count;
var itemWidth = .0;
var arr = new int[count];
if (!IsEnableTabFill)
{
itemWidth = TabItemWidth;
}
else
{
if (TemplatedParent is TabControl tabControl)
{
arr = ArithmeticHelper.DivideInt2Arr((int)tabControl.ActualWidth + InternalChildren.Count, count);
}
}
for (var index = 0; index < count; index++)
{
if (IsEnableTabFill)
{
itemWidth = arr[index];
}
if (InternalChildren[index] is TabItem tabItem)
{
tabItem.RenderTransform = new TranslateTransform();
tabItem.MaxWidth = itemWidth;
var rect = new Rect
{
X = size.Width - tabItem.BorderThickness.Left,
Width = itemWidth,
Height = TabItemHeight
};
tabItem.Arrange(rect);
tabItem.ItemWidth = itemWidth - tabItem.BorderThickness.Left;
tabItem.CurrentIndex = index;
tabItem.TargetOffsetX = 0;
ItemDic[index] = tabItem;
size.Width += tabItem.ItemWidth;
}
}
size.Height = constraint.Height;
_oldSize = size;
return _oldSize;
}
public TabPanel()
{
Loaded += (s, e) =>
{
if (_isLoaded) return;
ForceUpdate = true;
Measure(new Size(DesiredSize.Width, ActualHeight));
ForceUpdate = false;
foreach (var item in ItemDic.Values)
{
item.TabPanel = this;
}
_isLoaded = true;
};
}
}
}
@@ -0,0 +1,243 @@
using System;
using System.ComponentModel;
using System.Globalization;
using System.Windows;
using System.Windows.Documents;
using System.Windows.Media;
using HandyControl.Data;
namespace HandyControl.Controls
{
public class OutlineText : FrameworkElement
{
private FormattedText _formattedText;
private Geometry _textGeometry;
static OutlineText()
{
SnapsToDevicePixelsProperty.OverrideMetadata(typeof(OutlineText), new FrameworkPropertyMetadata(ValueBoxes.TrueBox));
}
public static readonly DependencyProperty TextProperty = DependencyProperty.Register(
"Text", typeof(string), typeof(OutlineText), new FrameworkPropertyMetadata(
string.Empty,
FrameworkPropertyMetadataOptions.AffectsMeasure |
FrameworkPropertyMetadataOptions.AffectsRender, OnFormattedTextInvalidated));
public string Text
{
get => (string) GetValue(TextProperty);
set => SetValue(TextProperty, value);
}
public static readonly DependencyProperty TextAlignmentProperty = DependencyProperty.Register(
"TextAlignment", typeof(TextAlignment), typeof(OutlineText),
new PropertyMetadata(default(TextAlignment), OnFormattedTextUpdated));
public TextAlignment TextAlignment
{
get => (TextAlignment) GetValue(TextAlignmentProperty);
set => SetValue(TextAlignmentProperty, value);
}
public static readonly DependencyProperty TextTrimmingProperty = DependencyProperty.Register(
"TextTrimming", typeof(TextTrimming), typeof(OutlineText),
new PropertyMetadata(default(TextTrimming), OnFormattedTextInvalidated));
public TextTrimming TextTrimming
{
get => (TextTrimming) GetValue(TextTrimmingProperty);
set => SetValue(TextTrimmingProperty, value);
}
public static readonly DependencyProperty TextWrappingProperty = DependencyProperty.Register(
"TextWrapping", typeof(TextWrapping), typeof(OutlineText),
new PropertyMetadata(TextWrapping.NoWrap, OnFormattedTextInvalidated));
public TextWrapping TextWrapping
{
get => (TextWrapping) GetValue(TextWrappingProperty);
set => SetValue(TextWrappingProperty, value);
}
public static readonly DependencyProperty FillProperty = DependencyProperty.Register(
"Fill", typeof(Brush), typeof(OutlineText), new PropertyMetadata(Brushes.Black, OnFormattedTextUpdated));
public Brush Fill
{
get => (Brush) GetValue(FillProperty);
set => SetValue(FillProperty, value);
}
public static readonly DependencyProperty StrokeProperty = DependencyProperty.Register(
"Stroke", typeof(Brush), typeof(OutlineText), new PropertyMetadata(Brushes.Black, OnFormattedTextUpdated));
public Brush Stroke
{
get => (Brush) GetValue(StrokeProperty);
set => SetValue(StrokeProperty, value);
}
public static readonly DependencyProperty StrokeThicknessProperty = DependencyProperty.Register(
"StrokeThickness", typeof(double), typeof(OutlineText), new PropertyMetadata(ValueBoxes.Double0Box, OnFormattedTextUpdated));
public double StrokeThickness
{
get => (double) GetValue(StrokeThicknessProperty);
set => SetValue(StrokeThicknessProperty, value);
}
public static readonly DependencyProperty FontFamilyProperty = TextElement.FontFamilyProperty.AddOwner(
typeof(OutlineText),
new FrameworkPropertyMetadata(OnFormattedTextUpdated));
public FontFamily FontFamily
{
get => (FontFamily)GetValue(FontFamilyProperty);
set => SetValue(FontFamilyProperty, value);
}
public static readonly DependencyProperty FontSizeProperty = TextElement.FontSizeProperty.AddOwner(
typeof(OutlineText),
new FrameworkPropertyMetadata(OnFormattedTextUpdated));
[TypeConverter(typeof(FontSizeConverter))]
public double FontSize
{
get => (double)GetValue(FontSizeProperty);
set => SetValue(FontSizeProperty, value);
}
public static readonly DependencyProperty FontStretchProperty = TextElement.FontStretchProperty.AddOwner(
typeof(OutlineText),
new FrameworkPropertyMetadata(OnFormattedTextUpdated));
public FontStretch FontStretch
{
get => (FontStretch)GetValue(FontStretchProperty);
set => SetValue(FontStretchProperty, value);
}
public static readonly DependencyProperty FontStyleProperty = TextElement.FontStyleProperty.AddOwner(
typeof(OutlineText),
new FrameworkPropertyMetadata(OnFormattedTextUpdated));
public FontStyle FontStyle
{
get => (FontStyle)GetValue(FontStyleProperty);
set => SetValue(FontStyleProperty, value);
}
public static readonly DependencyProperty FontWeightProperty = TextElement.FontWeightProperty.AddOwner(
typeof(OutlineText),
new FrameworkPropertyMetadata(OnFormattedTextUpdated));
public FontWeight FontWeight
{
get => (FontWeight)GetValue(FontWeightProperty);
set => SetValue(FontWeightProperty, value);
}
protected override void OnRender(DrawingContext drawingContext)
{
EnsureGeometry();
drawingContext.DrawGeometry(Fill, new Pen(Stroke, StrokeThickness), _textGeometry);
}
private void EnsureFormattedText()
{
if (_formattedText != null || Text == null)
{
return;
}
#if netle45
_formattedText = new FormattedText(
Text,
CultureInfo.CurrentUICulture,
FlowDirection,
new Typeface(FontFamily, FontStyle, FontWeight, FontStretch),
FontSize, Fill);
#else
var source = PresentationSource.FromVisual(this);
var dpi = 1.0;
if (source?.CompositionTarget != null)
{
dpi = 96.0 * source.CompositionTarget.TransformToDevice.M11 / 96.0;
}
_formattedText = new FormattedText(
Text,
CultureInfo.CurrentUICulture,
FlowDirection,
new Typeface(FontFamily, FontStyle, FontWeight, FontStretch),
FontSize, Fill, dpi);
#endif
UpdateFormattedText();
}
private void EnsureGeometry()
{
if (_textGeometry != null)
{
return;
}
EnsureFormattedText();
_textGeometry = _formattedText.BuildGeometry(new Point(0, 0));
}
private void UpdateFormattedText()
{
if (_formattedText == null)
{
return;
}
_formattedText.MaxLineCount = TextWrapping == TextWrapping.NoWrap ? 1 : int.MaxValue;
_formattedText.TextAlignment = TextAlignment;
_formattedText.Trimming = TextTrimming;
_formattedText.SetFontSize(FontSize);
_formattedText.SetFontStyle(FontStyle);
_formattedText.SetFontWeight(FontWeight);
_formattedText.SetFontFamily(FontFamily);
_formattedText.SetFontStretch(FontStretch);
}
private static void OnFormattedTextUpdated(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var outlinedTextBlock = (OutlineText)d;
outlinedTextBlock.UpdateFormattedText();
outlinedTextBlock._textGeometry = null;
outlinedTextBlock.InvalidateMeasure();
outlinedTextBlock.InvalidateVisual();
}
private static void OnFormattedTextInvalidated(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var outlinedTextBlock = (OutlineText)d;
outlinedTextBlock._formattedText = null;
outlinedTextBlock._textGeometry = null;
outlinedTextBlock.InvalidateMeasure();
outlinedTextBlock.InvalidateVisual();
}
protected override Size MeasureOverride(Size availableSize)
{
EnsureFormattedText();
// constrain the formatted text according to the available size
// the Math.Min call is important - without this constraint (which seems arbitrary, but is the maximum allowable text width), things blow up when availableSize is infinite in both directions
// the Math.Max call is to ensure we don't hit zero, which will cause MaxTextHeight to throw
_formattedText.MaxTextWidth = Math.Min(3579139, availableSize.Width);
_formattedText.MaxTextHeight = Math.Max(0.0001d, availableSize.Height);
// return the desired size
return new Size(_formattedText.Width, _formattedText.Height);
}
}
}
@@ -0,0 +1,89 @@
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using HandyControl.Data;
using HandyControl.Interactivity;
namespace HandyControl.Controls
{
public class Tag : ContentControl
{
public Tag()
{
CommandBindings.Add(new CommandBinding(ControlCommands.Close, (s, e) =>
{
var argsClosing = new CancelRoutedEventArgs(ClosingEvent, this);
RaiseEvent(argsClosing);
if (argsClosing.Cancel) return;
RaiseEvent(new RoutedEventArgs(ClosedEvent, this));
}));
}
public static readonly DependencyProperty ShowCloseButtonProperty = DependencyProperty.Register(
"ShowCloseButton", typeof(bool), typeof(Tag), new PropertyMetadata(ValueBoxes.TrueBox));
public bool ShowCloseButton
{
get => (bool) GetValue(ShowCloseButtonProperty);
set => SetValue(ShowCloseButtonProperty, value);
}
public static readonly DependencyProperty SelectableProperty = DependencyProperty.Register(
"Selectable", typeof(bool), typeof(Tag), new PropertyMetadata(ValueBoxes.FalseBox));
public bool Selectable
{
get => (bool) GetValue(SelectableProperty);
set => SetValue(SelectableProperty, value);
}
public static readonly DependencyProperty IsSelectedProperty = DependencyProperty.Register(
"IsSelected", typeof(bool), typeof(Tag), new PropertyMetadata(ValueBoxes.FalseBox, (o, args) =>
{
var ctl = (Tag) o;
ctl.RaiseEvent(new RoutedEventArgs(SelectedEvent, ctl));
}));
public bool IsSelected
{
get => (bool) GetValue(IsSelectedProperty);
set => SetValue(IsSelectedProperty, value);
}
public static readonly RoutedEvent SelectedEvent = EventManager.RegisterRoutedEvent("Selected", RoutingStrategy.Bubble, typeof(EventHandler), typeof(Tag));
public event EventHandler Selected
{
add => AddHandler(SelectedEvent, value);
remove => RemoveHandler(SelectedEvent, value);
}
public static readonly RoutedEvent ClosingEvent = EventManager.RegisterRoutedEvent("Closing", RoutingStrategy.Bubble, typeof(EventHandler), typeof(Tag));
public event EventHandler Closing
{
add => AddHandler(ClosingEvent, value);
remove => RemoveHandler(ClosingEvent, value);
}
public static readonly RoutedEvent ClosedEvent = EventManager.RegisterRoutedEvent("Closed", RoutingStrategy.Bubble, typeof(EventHandler), typeof(Tag));
public event EventHandler Closed
{
add => AddHandler(ClosedEvent, value);
remove => RemoveHandler(ClosedEvent, value);
}
protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e)
{
base.OnMouseLeftButtonDown(e);
if (Selectable)
{
IsSelected = true;
}
}
}
}
@@ -0,0 +1,88 @@
using System;
using System.Windows;
using System.Windows.Controls;
using HandyControl.Data;
using HandyControl.Interactivity;
using HandyControl.Tools;
namespace HandyControl.Controls
{
public class TagPanel : WrapPanel
{
private Button _addTagButton;
private bool _isInternalAction;
public TagPanel()
{
var behavior = new FluidMoveBehavior
{
AppliesTo = FluidMoveScope.Children,
Duration = new Duration(TimeSpan.FromMilliseconds(200))
};
var collection = Interaction.GetBehaviors(this);
collection.Add(behavior);
AddHandler(Controls.Tag.ClosedEvent, new RoutedEventHandler(Tag_OnClosed));
}
private void Tag_OnClosed(object sender, RoutedEventArgs e) => Children.Remove(e.OriginalSource as Tag);
public static readonly RoutedEvent AddTagButtonClickEvent = EventManager.RegisterRoutedEvent("AddTagButtonClick", RoutingStrategy.Bubble, typeof(EventHandler), typeof(TagPanel));
public event EventHandler AddTagButtonClick
{
add => AddHandler(AddTagButtonClickEvent, value);
remove => RemoveHandler(AddTagButtonClickEvent, value);
}
public static readonly DependencyProperty ShowAddButtonProperty = DependencyProperty.Register(
"ShowAddButton", typeof(bool), typeof(TagPanel), new PropertyMetadata(ValueBoxes.FalseBox));
public bool ShowAddButton
{
get => (bool) GetValue(ShowAddButtonProperty);
set => SetValue(ShowAddButtonProperty, value);
}
public static readonly DependencyProperty TagMarginProperty = DependencyProperty.Register(
"TagMargin", typeof(Thickness), typeof(TagPanel), new PropertyMetadata(new Thickness(5)));
public Thickness TagMargin
{
get => (Thickness) GetValue(TagMarginProperty);
set => SetValue(TagMarginProperty, value);
}
protected override void OnVisualChildrenChanged(DependencyObject visualAdded, DependencyObject visualRemoved)
{
if (!(visualAdded is Tag tag)) return;
base.OnVisualChildrenChanged(visualAdded, visualRemoved);
tag.Margin = TagMargin;
if (_isInternalAction) return;
if (ShowAddButton)
{
_isInternalAction = true;
if (!Children.Contains(_addTagButton))
{
_addTagButton = new Button
{
Style = ResourceHelper.GetResource<Style>(ResourceToken.AddTagButtonStyle),
Margin = TagMargin
};
_addTagButton.Click += AddTagButton_Click;
Children.Add(_addTagButton);
}
else
{
Children.Remove(_addTagButton);
Children.Add(_addTagButton);
}
_isInternalAction = false;
}
}
private void AddTagButton_Click(object sender, RoutedEventArgs e) => RaiseEvent(new RoutedEventArgs(AddTagButtonClickEvent, this));
}
}
@@ -0,0 +1,431 @@
using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Data;
using System.Windows.Input;
using System.Windows.Media;
using HandyControl.Data;
using HandyControl.Tools;
namespace HandyControl.Controls
{
[TemplatePart(Name = ElementButtonAm, Type = typeof(RadioButton))]
[TemplatePart(Name = ElementButtonPm, Type = typeof(RadioButton))]
[TemplatePart(Name = ElementButtonConfirm, Type = typeof(Button))]
[TemplatePart(Name = ElementCanvas, Type = typeof(Canvas))]
[TemplatePart(Name = ElementBorderTitle, Type = typeof(Border))]
[TemplatePart(Name = ElementBorderClock, Type = typeof(Border))]
[TemplatePart(Name = ElementPanelNum, Type = typeof(CirclePanel))]
[TemplatePart(Name = ElementTimeStr, Type = typeof(TextBlock))]
public class Clock : Control
{
#region Constants
private const string ElementButtonAm = "PART_ButtonAm";
private const string ElementButtonPm = "PART_ButtonPm";
private const string ElementButtonConfirm = "PART_ButtonConfirm";
private const string ElementCanvas = "PART_Canvas";
private const string ElementBorderTitle = "PART_BorderTitle";
private const string ElementBorderClock = "PART_BorderClock";
private const string ElementPanelNum = "PART_PanelNum";
private const string ElementTimeStr = "PART_TimeStr";
#endregion Constants
#region Data
private RadioButton _buttonAm;
private RadioButton _buttonPm;
private Button _buttonConfirm;
private Canvas _canvas;
private Border _borderTitle;
private Border _borderClock;
private ClockRadioButton _currentButton;
private RotateTransform _rotateTransformClock;
private CirclePanel _circlePanel;
private List<ClockRadioButton> _radioButtonList;
private TextBlock _blockTime;
private int _secValue;
private bool _appliedTemplate;
#endregion Data
#region Public Events
public static readonly RoutedEvent SelectedTimeChangedEvent =
EventManager.RegisterRoutedEvent("SelectedTimeChanged", RoutingStrategy.Direct,
typeof(EventHandler<FunctionEventArgs<DateTime?>>), typeof(Clock));
public event EventHandler<FunctionEventArgs<DateTime?>> SelectedTimeChanged
{
add => AddHandler(SelectedTimeChangedEvent, value);
remove => RemoveHandler(SelectedTimeChangedEvent, value);
}
public event EventHandler<FunctionEventArgs<DateTime>> DisplayTimeChanged;
public event Action Confirmed;
#endregion Public Events
#region Public Properties
public static readonly DependencyProperty SelectedTimeProperty = DependencyProperty.Register(
"SelectedTime", typeof(DateTime?), typeof(Clock), new PropertyMetadata(default(DateTime?), OnSelectedTimeChanged));
private static void OnSelectedTimeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var ctl = (Clock)d;
var v = (DateTime?)e.NewValue;
ctl.DisplayTime = v ?? DateTime.Now;
ctl.OnSelectedTimeChanged(new FunctionEventArgs<DateTime?>(SelectedTimeChangedEvent, ctl)
{
Info = v
});
}
public DateTime? SelectedTime
{
get => (DateTime?) GetValue(SelectedTimeProperty);
set => SetValue(SelectedTimeProperty, value);
}
public static readonly DependencyProperty TimeFormatProperty = DependencyProperty.Register(
"TimeFormat", typeof(string), typeof(Clock), new PropertyMetadata("HH:mm:ss"));
public string TimeFormat
{
get => (string) GetValue(TimeFormatProperty);
set => SetValue(TimeFormatProperty, value);
}
public static readonly DependencyProperty ClockRadioButtonStyleProperty = DependencyProperty.Register(
"ClockRadioButtonStyle", typeof(Style), typeof(Clock), new PropertyMetadata(default(Style)));
public Style ClockRadioButtonStyle
{
get => (Style) GetValue(ClockRadioButtonStyleProperty);
set => SetValue(ClockRadioButtonStyleProperty, value);
}
public static readonly DependencyProperty DisplayTimeProperty = DependencyProperty.Register(
"DisplayTime", typeof(DateTime), typeof(Clock), new FrameworkPropertyMetadata(DateTime.Now, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnDisplayTimeChanged));
private static void OnDisplayTimeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var ctl = (Clock) d;
var v = (DateTime) e.NewValue;
ctl.Update(v);
ctl.OnDisplayTimeChanged(new FunctionEventArgs<DateTime>(v));
}
public DateTime DisplayTime
{
get => (DateTime) GetValue(DisplayTimeProperty);
set => SetValue(DisplayTimeProperty, value);
}
internal static readonly DependencyProperty ShowConfirmButtonProperty = DependencyProperty.Register(
"ShowConfirmButton", typeof(bool), typeof(Clock), new PropertyMetadata(ValueBoxes.FalseBox));
internal bool ShowConfirmButton
{
get => (bool)GetValue(ShowConfirmButtonProperty);
set => SetValue(ShowConfirmButtonProperty, value);
}
private int SecValue
{
get => _secValue;
set
{
if (value < 0)
{
_secValue = 59;
}
else if (value > 59)
{
_secValue = 0;
}
else
{
_secValue = value;
}
}
}
#endregion Public Properties
#region Public Methods
public override void OnApplyTemplate()
{
_appliedTemplate = false;
if (_buttonAm != null)
{
_buttonAm.Click -= ButtonAm_OnClick;
}
if (_buttonPm != null)
{
_buttonPm.Click -= ButtonPm_OnClick;
}
if (_buttonConfirm != null)
{
_buttonConfirm.Click -= ButtonConfirm_OnClick;
}
if (_borderTitle != null)
{
_borderTitle.MouseWheel -= BorderTitle_OnMouseWheel;
}
if (_canvas != null)
{
_canvas.MouseWheel -= Canvas_OnMouseWheel;
_canvas.RemoveHandler(ButtonBase.ClickEvent, new RoutedEventHandler(Canvas_OnClick));
_canvas.MouseMove -= Canvas_OnMouseMove;
}
base.OnApplyTemplate();
_buttonAm = GetTemplateChild(ElementButtonAm) as RadioButton;
_buttonPm = GetTemplateChild(ElementButtonPm) as RadioButton;
_buttonConfirm = GetTemplateChild(ElementButtonConfirm) as Button;
_borderTitle = GetTemplateChild(ElementBorderTitle) as Border;
_canvas = GetTemplateChild(ElementCanvas) as Canvas;
_borderClock = GetTemplateChild(ElementBorderClock) as Border;
_circlePanel = GetTemplateChild(ElementPanelNum) as CirclePanel;
_blockTime = GetTemplateChild(ElementTimeStr) as TextBlock;
CheckNull();
_buttonAm.Click += ButtonAm_OnClick;
_buttonPm.Click += ButtonPm_OnClick;
_buttonConfirm.Click += ButtonConfirm_OnClick;
_borderTitle.MouseWheel += BorderTitle_OnMouseWheel;
_canvas.MouseWheel += Canvas_OnMouseWheel;
_canvas.AddHandler(ButtonBase.ClickEvent, new RoutedEventHandler(Canvas_OnClick));
_canvas.MouseMove += Canvas_OnMouseMove;
_rotateTransformClock = new RotateTransform();
_borderClock.RenderTransform = _rotateTransformClock;
_radioButtonList = new List<ClockRadioButton>();
for (var i = 0; i < 12; i++)
{
var num = i + 1;
var button = new ClockRadioButton
{
Num = num,
Content = num
};
button.SetBinding(StyleProperty, new Binding(ClockRadioButtonStyleProperty.Name) { Source = this });
_radioButtonList.Add(button);
_circlePanel.Children.Add(button);
}
_appliedTemplate = true;
if (SelectedTime.HasValue)
{
Update(SelectedTime.Value);
}
else
{
DisplayTime = DateTime.Now;
Update(DisplayTime);
}
}
#endregion Public Methods
#region Protected Methods
protected virtual void OnSelectedTimeChanged(FunctionEventArgs<DateTime?> e) => RaiseEvent(e);
protected virtual void OnDisplayTimeChanged(FunctionEventArgs<DateTime> e)
{
var handler = DisplayTimeChanged;
handler?.Invoke(this, e);
}
#endregion Protected Methods
#region Private Methods
private void CheckNull()
{
if (_buttonPm == null || _buttonAm == null || _buttonConfirm == null || _canvas == null ||
_borderTitle == null || _borderClock == null || _circlePanel == null ||
_blockTime == null) throw new Exception();
}
private void BorderTitle_OnMouseWheel(object sender, MouseWheelEventArgs e)
{
if (e.Delta < 0)
{
SecValue--;
Update();
}
else
{
SecValue++;
Update();
}
e.Handled = true;
}
private void Canvas_OnMouseWheel(object sender, MouseWheelEventArgs e)
{
var value = (int)_rotateTransformClock.Angle;
if (e.Delta < 0)
{
value += 6;
}
else
{
value -= 6;
}
if (value < 0)
{
value = value + 360;
}
_rotateTransformClock.Angle = value;
Update();
e.Handled = true;
}
private void Canvas_OnClick(object sender, RoutedEventArgs e)
{
_currentButton = e.OriginalSource as ClockRadioButton;
if (_currentButton != null)
{
Update();
}
}
private void Canvas_OnMouseMove(object sender, MouseEventArgs e)
{
if (e.LeftButton == MouseButtonState.Pressed)
{
var value = ArithmeticHelper.CalAngle(new Point(85, 85), e.GetPosition(_canvas)) + 90;
if (value < 0)
{
value = value + 360;
}
value = value - value % 6;
_rotateTransformClock.Angle = value;
Update();
}
}
private void Update()
{
if (!_appliedTemplate) return;
var hValue = _currentButton.Num;
if (_buttonPm.IsChecked == true)
{
hValue += 12;
if (hValue == 24) hValue = 12;
}
else if (hValue == 12)
{
hValue = 0;
}
if (hValue == 12 && _buttonAm.IsChecked == true)
{
_buttonPm.IsChecked = true;
_buttonAm.IsChecked = false;
}
if (_blockTime != null)
{
DisplayTime = GetDisplayTime();
_blockTime.Text = DisplayTime.ToString(TimeFormat);
}
}
/// <summary>
/// 更新
/// </summary>
/// <param name="time"></param>
internal void Update(DateTime time)
{
if (!_appliedTemplate) return;
var h = time.Hour;
var m = time.Minute;
if (h >= 12)
{
_buttonPm.IsChecked = true;
_buttonAm.IsChecked = false;
}
else
{
_buttonPm.IsChecked = false;
_buttonAm.IsChecked = true;
}
_rotateTransformClock.Angle = m * 6;
var hRest = h % 12;
if (hRest == 0) hRest = 12;
var ctl = _radioButtonList[hRest-1];
ctl.IsChecked = true;
ctl.RaiseEvent(new RoutedEventArgs { RoutedEvent = ButtonBase.ClickEvent });
_secValue = time.Second;
Update();
}
private void ButtonConfirm_OnClick(object sender, RoutedEventArgs e)
{
SelectedTime = DisplayTime;
Confirmed?.Invoke();
}
/// <summary>
/// 获取显示时间
/// </summary>
/// <returns></returns>
private DateTime GetDisplayTime()
{
var hValue = _currentButton.Num;
if (_buttonPm.IsChecked == true)
{
hValue += 12;
if (hValue == 24) hValue = 12;
}
else if (hValue == 12)
{
hValue = 0;
}
var now = DateTime.Now;
return new DateTime(now.Year, now.Month, now.Day, hValue, (int)Math.Abs(_rotateTransformClock.Angle) % 360 / 6, _secValue);
}
private void ButtonAm_OnClick(object sender, RoutedEventArgs e) => Update();
private void ButtonPm_OnClick(object sender, RoutedEventArgs e) => Update();
#endregion Private Methods
}
}
@@ -0,0 +1,10 @@
using System.Windows.Controls;
namespace HandyControl.Controls
{
public class ClockRadioButton : RadioButton
{
public int Num { get; set; }
}
}
@@ -0,0 +1,57 @@
using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Threading;
namespace HandyControl.Controls
{
public class FlipClock : Control
{
public static readonly DependencyProperty NumberListProperty = DependencyProperty.Register(
"NumberList", typeof(List<int>), typeof(FlipClock), new PropertyMetadata(new List<int> {0, 0, 0, 0, 0, 0}));
public List<int> NumberList
{
get => (List<int>) GetValue(NumberListProperty);
set => SetValue(NumberListProperty, value);
}
public static readonly DependencyProperty DisplayTimeProperty = DependencyProperty.Register(
"DisplayTime", typeof(DateTime), typeof(FlipClock), new PropertyMetadata(default(DateTime), OnDisplayTimeChanged));
private static void OnDisplayTimeChanged(DependencyObject s, DependencyPropertyChangedEventArgs e)
{
var ctl = (FlipClock) s;
var v = (DateTime) e.NewValue;
ctl.NumberList = new List<int>
{
v.Hour / 10,
v.Hour % 10,
v.Minute / 10,
v.Minute % 10,
v.Second / 10,
v.Second % 10
};
}
public DateTime DisplayTime
{
get => (DateTime) GetValue(DisplayTimeProperty);
set => SetValue(DisplayTimeProperty, value);
}
public FlipClock()
{
var dispatcherTimer = new DispatcherTimer
{
Interval = TimeSpan.FromMilliseconds(200)
};
dispatcherTimer.Tick += DispatcherTimer_Tick;
dispatcherTimer.Start();
}
private void DispatcherTimer_Tick(object sender, EventArgs e) => DisplayTime = DateTime.Now;
}
}
@@ -0,0 +1,284 @@
using System;
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Media.Media3D;
using HandyControl.Data;
namespace HandyControl.Controls
{
public class FlipNumber : Viewport3D
{
private bool _isLoaded;
private TextBlock _page1TextDown;
private TextBlock _page2TextUp;
private TextBlock _page2TextDown;
private TextBlock _page3TextUp;
private ContainerUIElement3D _page1;
private ContainerUIElement3D _page2;
private ContainerUIElement3D _page3;
private ContainerUIElement3D _content;
private readonly AxisAngleRotation3D _pageRotation3D;
private readonly DoubleAnimation _animation;
private bool _isAnimating;
public static readonly DependencyProperty CornerRadiusProperty = DependencyProperty.Register(
"CornerRadius", typeof(CornerRadius), typeof(FlipNumber), new PropertyMetadata(new CornerRadius(4)));
public CornerRadius CornerRadius
{
get => (CornerRadius)GetValue(CornerRadiusProperty);
set => SetValue(CornerRadiusProperty, value);
}
public static readonly DependencyProperty BackgroundProperty = DependencyProperty.Register(
"Background", typeof(Brush), typeof(FlipNumber), new PropertyMetadata(default(Brush)));
public Brush Background
{
get => (Brush)GetValue(BackgroundProperty);
set => SetValue(BackgroundProperty, value);
}
public static readonly DependencyProperty ForegroundProperty = DependencyProperty.Register(
"Foreground", typeof(Brush), typeof(FlipNumber), new PropertyMetadata(default(Brush)));
public Brush Foreground
{
get => (Brush)GetValue(ForegroundProperty);
set => SetValue(ForegroundProperty, value);
}
public static readonly DependencyProperty FontSizeProperty = DependencyProperty.Register(
"FontSize", typeof(double), typeof(FlipNumber), new PropertyMetadata(70.0));
[TypeConverter(typeof(FontSizeConverter))]
public double FontSize
{
get => (double)GetValue(FontSizeProperty);
set => SetValue(FontSizeProperty, value);
}
public static readonly DependencyProperty NumberProperty = DependencyProperty.Register(
"Number", typeof(int), typeof(FlipNumber), new PropertyMetadata(ValueBoxes.Int0Box, OnNumberChanged));
private static void OnNumberChanged(DependencyObject s, DependencyPropertyChangedEventArgs e) =>
((FlipNumber)s).OnNumberChanged();
public int Number
{
get => (int)GetValue(NumberProperty);
set => SetValue(NumberProperty, value);
}
public FlipNumber()
{
var visual3D = new ModelVisual3D
{
Content = new DirectionalLight()
};
Children.Add(visual3D);
_pageRotation3D = new AxisAngleRotation3D
{
Angle = 0,
Axis = new Vector3D(1, 0, 0)
};
_animation = new DoubleAnimation(0, 180, new Duration(TimeSpan.FromSeconds(0.8)))
{
FillBehavior = FillBehavior.Stop
};
_animation.Completed += Animation_Completed;
Loaded += (s, e) =>
{
if (_isLoaded) return;
_isLoaded = true;
InitNumber();
var transform3D = new RotateTransform3D(_pageRotation3D);
_page2.Transform = transform3D;
};
}
private void Animation_Completed(object sender, EventArgs e)
{
_isAnimating = false;
UpdateNumber();
}
private void InitNumber()
{
_page1 = new ContainerUIElement3D();
var num1 = Number > 8 ? 0 : Number + 1;
var page1NumDown = CreateNumber(num1, false, out _page1TextDown);
_page1.Children.Add(page1NumDown);
_page2 = new ContainerUIElement3D();
var page2NumUp = CreateNumber(num1, true, out _page2TextUp);
var page2NumDown = CreateNumber(Number, false, out _page2TextDown);
_page2.Children.Add(page2NumUp);
_page2.Children.Add(page2NumDown);
_page3 = new ContainerUIElement3D();
var page3NumUp = CreateNumber(Number, true, out _page3TextUp);
_page3.Children.Add(page3NumUp);
var transform3D = new RotateTransform3D(new AxisAngleRotation3D
{
Angle = 180,
Axis = new Vector3D(1, 0, 0)
});
_page3.Transform = transform3D;
_content = new ContainerUIElement3D
{
Children =
{
_page1,
_page2,
_page3
}
};
Children.Add(_content);
}
private bool CheckNull() => _page1TextDown != null && _page2TextUp != null && _page2TextDown != null && _page3TextUp != null;
private void OnNumberChanged()
{
if (!CheckNull()) return;
InitNewNumber();
if (_isAnimating)
{
_isAnimating = false;
UpdateNumber();
return;
}
_isAnimating = true;
_pageRotation3D.BeginAnimation(AxisAngleRotation3D.AngleProperty, _animation);
}
private void InitNewNumber()
{
var num1 = Number.ToString();
_page1TextDown.Text = num1;
_page2TextUp.Text = num1;
}
private void UpdateNumber()
{
_pageRotation3D.BeginAnimation(AxisAngleRotation3D.AngleProperty, null);
_pageRotation3D.Angle = 0;
var num = Number.ToString();
_page2TextDown.Text = num;
_page3TextUp.Text = num;
_isAnimating = false;
}
private Viewport2DVisual3D CreateNumber(int num, bool isUp, out TextBlock textBlock)
{
int flag;
var rotateTransform = new RotateTransform();
if (isUp)
{
flag = -1;
rotateTransform.Angle = 180;
}
else
{
flag = 1;
}
var halfWidth = ActualWidth / 2;
var quarterWidth = ActualWidth / 4;
var quarterHeight = ActualHeight / 4;
var meMaterial = new DiffuseMaterial();
Viewport2DVisual3D.SetIsVisualHostMaterial(meMaterial, true);
textBlock = new TextBlock
{
RenderTransformOrigin = new Point(0.5, 0.5),
Foreground = Foreground,
FontSize = FontSize,
VerticalAlignment = VerticalAlignment.Center,
HorizontalAlignment = HorizontalAlignment.Center,
TextAlignment = TextAlignment.Center,
Text = num.ToString(),
RenderTransform = rotateTransform,
Margin = new Thickness(0, 0, 0, -quarterHeight),
FontFamily = new FontFamily("Consolas")
};
var border = new Border
{
ClipToBounds = true,
CornerRadius = new CornerRadius(CornerRadius.TopLeft, CornerRadius.TopRight, 0, 0),
Background = Background,
Width = halfWidth,
Height = quarterHeight,
Child = textBlock
};
var positions = new Point3DCollection
{
new Point3D(-quarterWidth * flag, quarterHeight, 0),
new Point3D(-quarterWidth * flag, 0, 0),
new Point3D(quarterWidth * flag, 0, 0),
new Point3D(quarterWidth * flag, quarterHeight, 0)
};
var triangleIndices = new Int32Collection
{
0,
1,
2,
0,
2,
3
};
var textureCoordinates = new PointCollection
{
new Point(0, 0),
new Point(0, 1),
new Point(1, 1),
new Point(1, 0)
};
var geometry3D = new MeshGeometry3D
{
Positions = positions,
TriangleIndices = triangleIndices,
TextureCoordinates = textureCoordinates
};
var child = new Viewport2DVisual3D
{
Geometry = geometry3D,
Visual = border,
Material = meMaterial
};
return child;
}
}
}
@@ -0,0 +1,51 @@
using System;
using System.Windows.Controls;
namespace HandyControl.Controls
{
/// <summary>
/// 作为刻度使用的文字块
/// </summary>
internal class SpeTextBlock : TextBlock
{
public double X { get; set; }
public SpeTextBlock() => Width = 60;
public SpeTextBlock(double x) : this()
{
X = x;
Canvas.SetLeft(this, X);
}
/// <summary>
/// 时间
/// </summary>
private DateTime _time;
/// <summary>
/// 时间
/// </summary>
public DateTime Time
{
get => _time;
set
{
_time = value;
Text = $"{value.ToString(TimeFormat)}\r\n|";
}
}
/// <summary>
/// 时间格式
/// </summary>
public string TimeFormat { get; set; } = "HH:mm";
/// <summary>
/// 横向移动
/// </summary>
/// <param name="offsetX"></param>
public void MoveX(double offsetX) => Canvas.SetLeft(this, X + offsetX);
}
}
@@ -0,0 +1,475 @@
using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using HandyControl.Data;
using HandyControl.Interactivity;
using HandyControl.Tools;
namespace HandyControl.Controls
{
/// <summary>
/// 时间条
/// </summary>
[TemplatePart(Name = ElementBorderTop, Type = typeof(Border))]
[TemplatePart(Name = ElementTextBlockMove, Type = typeof(TextBlock))]
[TemplatePart(Name = ElementTextBlockSelected, Type = typeof(TextBlock))]
[TemplatePart(Name = ElementCanvasSpe, Type = typeof(Canvas))]
public class TimeBar : Control
{
#region Constants
private const string ElementBorderTop = "PART_BorderTop";
private const string ElementTextBlockMove = "PART_TextBlockMove";
private const string ElementTextBlockSelected = "PART_TextBlockSelected";
private const string ElementCanvasSpe = "PART_CanvasSpe";
#endregion Constants
#region Data
private Border _borderTop;
private TextBlock _textBlockMove;
private TextBlock _textBlockSelected;
private Canvas _canvasSpe;
#endregion Data
/// <summary>
/// 是否显示刻度字符串
/// </summary>
public static readonly DependencyProperty ShowSpeStrProperty = DependencyProperty.Register(
"ShowSpeStr", typeof(bool), typeof(TimeBar), new PropertyMetadata(ValueBoxes.FalseBox));
public static readonly DependencyProperty TimeFormatProperty = DependencyProperty.Register(
"TimeFormat", typeof(string), typeof(TimeBar), new PropertyMetadata("yyyy-MM-dd HH:mm:ss"));
public string TimeFormat
{
get => (string) GetValue(TimeFormatProperty);
set => SetValue(TimeFormatProperty, value);
}
/// <summary>
/// 刻度字符串
/// </summary>
internal static readonly DependencyProperty SpeStrProperty = DependencyProperty.Register(
"SpeStr", typeof(string), typeof(TimeBar), new PropertyMetadata(Properties.Langs.Lang.Interval1h));
/// <summary>
/// 选中时间
/// </summary>
internal static readonly DependencyProperty SelectedTimeProperty = DependencyProperty.Register(
"SelectedTime", typeof(DateTime), typeof(TimeBar), new PropertyMetadata(default(DateTime), OnSelectedTimeChanged));
private static void OnSelectedTimeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is TimeBar timeBar && timeBar._textBlockSelected != null)
{
timeBar._textBlockSelected.Text = ((DateTime) e.NewValue).ToString(timeBar.TimeFormat);
}
}
/// <summary>
/// 时间改变事件
/// </summary>
public static readonly RoutedEvent TimeChangedEvent =
EventManager.RegisterRoutedEvent("TimeChanged", RoutingStrategy.Bubble,
typeof(EventHandler<FunctionEventArgs<DateTime>>), typeof(TimeBar));
/// <summary>
/// 刻度集合
/// </summary>
private readonly List<SpeTextBlock> _speBlockList = new List<SpeTextBlock>();
/// <summary>
/// 初始化时时间
/// </summary>
private readonly DateTime _starTime;
/// <summary>
/// 时间段集合
/// </summary>
private readonly List<int> _timeSpeList = new List<int>
{
7200000,
3600000,
1800000,
600000,
300000,
60000,
30000
};
/// <summary>
/// 顶部border是否被按下
/// </summary>
private bool _borderTopIsMouseLeftButtonDown;
/// <summary>
/// 控件是否处于拖动中
/// </summary>
private bool _isDragging;
/// <summary>
/// 刻度单项宽度
/// </summary>
private double _itemWidth;
/// <summary>
/// 鼠标按下拖动时选中的时间
/// </summary>
private DateTime _mouseDownTime;
/// <summary>
/// 显示的刻度数目
/// </summary>
private int _speCount = 13;
/// <summary>
/// 刻度区间编号
/// </summary>
private int _speIndex = 1;
/// <summary>
/// 刻度单次偏移
/// </summary>
private double _tempOffsetX;
/// <summary>
/// 刻度总偏移
/// </summary>
private double _totalOffsetX;
private readonly bool _isLoaded;
public TimeBar()
{
_starTime = DateTime.Now;
SelectedTime = new DateTime(_starTime.Year, _starTime.Month, _starTime.Day, 0, 0, 0);
_starTime = SelectedTime;
_isLoaded = true;
SizeChanged += TimeBar_OnSizeChanged;
MouseMove += TimeBar_OnMouseMove;
}
public override void OnApplyTemplate()
{
if (_borderTop != null)
{
_borderTop.MouseLeftButtonDown -= BorderTop_OnMouseLeftButtonDown;
_borderTop.PreviewMouseLeftButtonUp -= BorderTop_OnPreviewMouseLeftButtonUp;
}
base.OnApplyTemplate();
_borderTop = GetTemplateChild(ElementBorderTop) as Border;
_textBlockMove = GetTemplateChild(ElementTextBlockMove) as TextBlock;
_textBlockSelected = GetTemplateChild(ElementTextBlockSelected) as TextBlock;
_canvasSpe = GetTemplateChild(ElementCanvasSpe) as Canvas;
CheckNull();
_borderTop.MouseLeftButtonDown += BorderTop_OnMouseLeftButtonDown;
_borderTop.PreviewMouseLeftButtonUp += BorderTop_OnPreviewMouseLeftButtonUp;
var behavior = new MouseDragElementBehaviorEx
{
LockY = true,
};
behavior.DragBegun += DragElementBehavior_OnDragBegun;
behavior.Dragging += MouseDragElementBehavior_OnDragging;
behavior.DragFinished += MouseDragElementBehavior_OnDragFinished;
var collection = Interaction.GetBehaviors(_borderTop);
collection.Add(behavior);
if (_isLoaded)
{
TimeBar_OnSizeChanged(null, null);
}
_textBlockSelected.Text = SelectedTime.ToString(TimeFormat);
}
private void CheckNull()
{
if (_borderTop == null || _textBlockMove == null|| _textBlockSelected == null || _canvasSpe == null) throw new Exception();
}
/// <summary>
/// 是否显示刻度字符串
/// </summary>
public bool ShowSpeStr
{
get => (bool)GetValue(ShowSpeStrProperty);
set => SetValue(ShowSpeStrProperty, value);
}
/// <summary>
/// 刻度字符串
/// </summary>
internal string SpeStr
{
get => (string)GetValue(SpeStrProperty);
set => SetValue(SpeStrProperty, value);
}
/// <summary>
/// 选中时间
/// </summary>
internal DateTime SelectedTime
{
get => (DateTime)GetValue(SelectedTimeProperty);
set => SetValue(SelectedTimeProperty, value);
}
/// <summary>
/// 刻度区间编号
/// </summary>
public int SpeIndex
{
get => _speIndex;
private set
{
if (_speIndex == value) return;
if (value < 0)
{
SpeStr = Properties.Langs.Lang.Interval2h;
_speIndex = 0;
return;
}
if (value > 6)
{
SpeStr = Properties.Langs.Lang.Interval30s;
_speIndex = 6;
return;
}
SetSpeTimeFormat("HH:mm");
switch (value)
{
case 0:
SpeStr = Properties.Langs.Lang.Interval2h;
break;
case 1:
SpeStr = Properties.Langs.Lang.Interval1h;
break;
case 2:
SpeStr = Properties.Langs.Lang.Interval30m;
break;
case 3:
SpeStr = Properties.Langs.Lang.Interval10m;
break;
case 4:
SpeStr = Properties.Langs.Lang.Interval5m;
break;
case 5:
SpeStr = Properties.Langs.Lang.Interval1m;
break;
case 6:
SetSpeTimeFormat("HH:mm:ss");
SpeStr = Properties.Langs.Lang.Interval30s;
break;
}
_speIndex = value;
}
}
/// <summary>
/// 时间改变事件
/// </summary>
public event EventHandler<FunctionEventArgs<DateTime>> TimeChanged
{
add => AddHandler(TimeChangedEvent, value);
remove => RemoveHandler(TimeChangedEvent, value);
}
/// <summary>
/// 设置刻度时间格式
/// </summary>
/// <param name="format"></param>
private void SetSpeTimeFormat(string format)
{
foreach (var item in _speBlockList)
item.TimeFormat = format;
}
/// <summary>
/// 设置显示的时间
/// </summary>
/// <param name="time"></param>
public void SetShowTime(DateTime time)
{
if (_isDragging || _borderTopIsMouseLeftButtonDown) return;
SelectedTime = time;
_totalOffsetX = (_starTime - SelectedTime).TotalMilliseconds / _timeSpeList[SpeIndex] * _itemWidth;
Update();
TimeBar_OnMouseMove(null, null);
}
/// <summary>
/// 更新
/// </summary>
private void Update()
{
var rest = (_totalOffsetX + _tempOffsetX) % _itemWidth;
for (var i = 0; i < _speCount; i++)
{
var item = _speBlockList[i];
item.MoveX(rest + (_itemWidth - item.Width) / 2);
}
var sub = rest <= 0 ? _speCount / 2 : _speCount / 2 - 1;
for (var i = 0; i < _speCount; i++)
_speBlockList[i].Time = TimeConvert(SelectedTime).AddMilliseconds((i - sub) * _timeSpeList[_speIndex]);
}
/// <summary>
/// 时间转换
/// </summary>
/// <param name="time"></param>
/// <returns></returns>
private DateTime TimeConvert(DateTime time)
{
switch (_speIndex)
{
case 0:
return new DateTime(time.Year, time.Month, time.Day, time.Hour / 2 * 2, 0, 0);
case 1:
return new DateTime(time.Year, time.Month, time.Day, time.Hour, 0, 0);
case 2:
return new DateTime(time.Year, time.Month, time.Day, time.Hour, time.Minute / 30 * 30, 0);
case 3:
return new DateTime(time.Year, time.Month, time.Day, time.Hour, time.Minute / 10 * 10, 0);
case 4:
return new DateTime(time.Year, time.Month, time.Day, time.Hour, time.Minute / 5 * 5, 0);
case 5:
return new DateTime(time.Year, time.Month, time.Day, time.Hour, time.Minute, 0);
case 6:
return new DateTime(time.Year, time.Month, time.Day, time.Hour, time.Minute, time.Second / 30 * 30);
default:
return time;
}
}
/// <summary>
/// 鼠标滚轮滚动时改变刻度区间
/// </summary>
/// <param name="e"></param>
protected override void OnMouseWheel(MouseWheelEventArgs e)
{
base.OnMouseWheel(e);
if (Mouse.LeftButton == MouseButtonState.Pressed) return;
SpeIndex += e.Delta > 0 ? 1 : -1;
_totalOffsetX = (_starTime - SelectedTime).TotalMilliseconds / _timeSpeList[SpeIndex] * _itemWidth;
Update();
TimeBar_OnMouseMove(null, null);
e.Handled = true;
}
private void MouseDragElementBehavior_OnDragging(object sender, MouseEventArgs e)
{
_isDragging = true;
_tempOffsetX = _borderTop.RenderTransform.Value.OffsetX;
SelectedTime = _mouseDownTime -
TimeSpan.FromMilliseconds(_tempOffsetX / _itemWidth * _timeSpeList[_speIndex]);
Update();
_borderTopIsMouseLeftButtonDown = false;
}
private void MouseDragElementBehavior_OnDragFinished(object sender, MouseEventArgs e)
{
_tempOffsetX = 0;
_totalOffsetX = (_totalOffsetX + _borderTop.RenderTransform.Value.OffsetX) % ActualWidth;
_borderTop.RenderTransform = new TranslateTransform();
RaiseEvent(new FunctionEventArgs<DateTime>(TimeChangedEvent, this)
{
Info = SelectedTime
});
_isDragging = false;
}
private void DragElementBehavior_OnDragBegun(object sender, MouseEventArgs e) => _mouseDownTime = SelectedTime;
private void TimeBar_OnSizeChanged(object sender, SizeChangedEventArgs e)
{
_speBlockList.Clear();
_canvasSpe.Children.Clear();
_speCount = (int)(ActualWidth / 800 * 9) | 1;
var itemWidthOld = _itemWidth;
_itemWidth = ActualWidth / _speCount;
_totalOffsetX = _itemWidth / itemWidthOld * _totalOffsetX % ActualWidth;
if (double.IsNaN(_totalOffsetX))
_totalOffsetX = 0;
var rest = (_totalOffsetX + _tempOffsetX) % _itemWidth;
var sub = rest <= 0 || double.IsNaN(rest) ? _speCount / 2 : _speCount / 2 - 1;
for (var i = 0; i < _speCount; i++)
{
var block = new SpeTextBlock
{
Time = TimeConvert(SelectedTime).AddMilliseconds((i - sub) * _timeSpeList[_speIndex]),
TextAlignment = TextAlignment.Center,
TimeFormat = "HH:mm"
};
_speBlockList.Add(block);
_canvasSpe.Children.Add(block);
}
if (_speIndex == 6)
SetSpeTimeFormat("HH:mm:ss");
ShowSpeStr = ActualWidth > 320;
for (var i = 0; i < _speCount; i++)
{
var item = _speBlockList[i];
item.X = _itemWidth * i;
item.MoveX((_itemWidth - item.Width) / 2);
}
Update();
TimeBar_OnMouseMove(null, null);
}
private void TimeBar_OnMouseMove(object sender, MouseEventArgs e)
{
var p = Mouse.GetPosition(this);
var mlliseconds = (p.X - ActualWidth / 2) / _itemWidth * _timeSpeList[_speIndex];
if (double.IsInfinity(mlliseconds)) return;
_textBlockMove.Text = mlliseconds < 0
? (SelectedTime - TimeSpan.FromMilliseconds(-mlliseconds)).ToString(TimeFormat)
: (SelectedTime + TimeSpan.FromMilliseconds(mlliseconds)).ToString(TimeFormat);
_textBlockMove.Margin = new Thickness(p.X - _textBlockMove.ActualWidth / 2, 2, 0, 0);
}
private void BorderTop_OnMouseLeftButtonDown(object sender, MouseButtonEventArgs e) => _borderTopIsMouseLeftButtonDown = true;
private void BorderTop_OnPreviewMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
if (_borderTopIsMouseLeftButtonDown)
{
_borderTopIsMouseLeftButtonDown = false;
var p = Mouse.GetPosition(this);
_tempOffsetX = ActualWidth / 2 - p.X;
SelectedTime -= TimeSpan.FromMilliseconds(_tempOffsetX / _itemWidth * _timeSpeList[_speIndex]);
Update();
_totalOffsetX = (_totalOffsetX + _tempOffsetX) % ActualWidth;
_tempOffsetX = 0;
TimeBar_OnMouseMove(null, null);
RaiseEvent(new FunctionEventArgs<DateTime>(TimeChangedEvent, this)
{
Info = SelectedTime
});
}
}
}
}
@@ -0,0 +1,59 @@
using System.Runtime.InteropServices;
using System.Windows.Interop;
using System.Windows.Media;
using HandyControl.Data;
using HandyControl.Tools;
namespace HandyControl.Controls
{
public class BlurWindow : Window
{
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
EnableBlur(this);
}
public static SystemVersionInfo SystemVersionInfo { get; set; }
internal static void EnableBlur(Window window)
{
var accentPolicy = new ExternDllHelper.ACCENTPOLICY();
var accentPolicySize = Marshal.SizeOf(accentPolicy);
if (SystemVersionInfo >= SystemVersionInfo.Windows10_1809)
{
accentPolicy.AccentState = ExternDllHelper.ACCENTSTATE.ACCENT_ENABLE_ACRYLICBLURBEHIND;
}
else if (SystemVersionInfo >= SystemVersionInfo.Windows10)
{
accentPolicy.AccentState = ExternDllHelper.ACCENTSTATE.ACCENT_ENABLE_BLURBEHIND;
}
else
{
var colorValue = ResourceHelper.GetResource<uint>(ResourceToken.BlurGradientValue);
var color = ColorHelper.ToColor(colorValue);
color = Color.FromRgb(color.R, color.G, color.B);
window.Background = new SolidColorBrush(color);
return;
}
accentPolicy.AccentFlags = 2;
accentPolicy.GradientColor = ResourceHelper.GetResource<uint>(ResourceToken.BlurGradientValue);
var accentPtr = Marshal.AllocHGlobal(accentPolicySize);
Marshal.StructureToPtr(accentPolicy, accentPtr, false);
var data = new ExternDllHelper.WINCOMPATTRDATA
{
Attribute = ExternDllHelper.WINDOWCOMPOSITIONATTRIB.WCA_ACCENT_POLICY,
DataSize = accentPolicySize,
Data = accentPtr
};
ExternDllHelper.SetWindowCompositionAttribute(new WindowInteropHelper(window).Handle, ref data);
Marshal.FreeHGlobal(accentPtr);
}
}
}
@@ -0,0 +1,129 @@
using System;
using System.IO;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media.Imaging;
using HandyControl.Data;
using HandyControl.Interactivity;
namespace HandyControl.Controls
{
/// <summary>
/// 图片浏览器
/// </summary>
[TemplatePart(Name = ElementGridTop, Type = typeof(Grid))]
[TemplatePart(Name = ElementImageViewer, Type = typeof(ImageViewer))]
public class ImageBrowser : Window
{
#region Constants
private const string ElementGridTop = "PART_GridTop";
private const string ElementImageViewer = "PART_ImageViewer";
#endregion Constants
#region Data
private Grid _gridTop;
private ImageViewer _imageViewer;
#endregion Data
static ImageBrowser()
{
IsFullScreenProperty.AddOwner(typeof(ImageBrowser), new PropertyMetadata(ValueBoxes.FalseBox));
}
public ImageBrowser()
{
CommandBindings.Add(new CommandBinding(ControlCommands.Close, ButtonClose_OnClick));
WindowStartupLocation = WindowStartupLocation.CenterScreen;
WindowStyle = WindowStyle.None;
AllowsTransparency = true;
}
/// <summary>
/// 带一个图片Uri的构造函数
/// </summary>
/// <param name="uri"></param>
public ImageBrowser(Uri uri) : this()
{
Loaded += (s, e) =>
{
try
{
_imageViewer.ImageSource = BitmapFrame.Create(uri);
_imageViewer.ImgPath = uri.AbsolutePath;
if (File.Exists(_imageViewer.ImgPath))
{
var info = new FileInfo(_imageViewer.ImgPath);
_imageViewer.ImgSize = info.Length;
}
}
catch
{
MessageBox.Show(Properties.Langs.Lang.ErrorImgPath);
}
};
}
/// <summary>
/// 带一个图片路径的构造函数
/// </summary>
/// <param name="path"></param>
public ImageBrowser(string path) : this(new Uri(path))
{
}
public override void OnApplyTemplate()
{
if (_gridTop != null)
{
_gridTop.MouseLeftButtonDown -= GridTop_OnMouseLeftButtonDown;
}
if (_imageViewer != null)
{
_imageViewer.MouseLeftButtonDown -= ImageViewer_MouseLeftButtonDown;
}
base.OnApplyTemplate();
_gridTop = GetTemplateChild(ElementGridTop) as Grid;
_imageViewer = GetTemplateChild(ElementImageViewer) as ImageViewer;
if (_gridTop != null)
{
_gridTop.MouseLeftButtonDown += GridTop_OnMouseLeftButtonDown;
}
if (_imageViewer != null)
{
_imageViewer.MouseLeftButtonDown += ImageViewer_MouseLeftButtonDown;
}
}
private void ButtonClose_OnClick(object sender, RoutedEventArgs e) => Close();
private void GridTop_OnMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
if (e.LeftButton == MouseButtonState.Pressed)
{
DragMove();
}
}
private void ImageViewer_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
if (e.LeftButton == MouseButtonState.Pressed && !(_imageViewer.ImageWidth > ActualWidth || _imageViewer.ImageHeight > ActualHeight))
{
DragMove();
}
}
}
}
@@ -0,0 +1,327 @@
using System.ComponentModel;
using System.Linq;
using System.Windows;
using System.Windows.Input;
using System.Windows.Media;
using HandyControl.Data;
using HandyControl.Interactivity;
using HandyControl.Tools;
namespace HandyControl.Controls
{
/// <summary>
/// 消息框
/// </summary>
public sealed class MessageBox : Window
{
private MessageBoxResult _messageBoxResult = MessageBoxResult.Cancel;
private bool _showOk;
private bool _showCancel;
private bool _showYes;
private bool _showNo;
public static readonly DependencyProperty MessageProperty = DependencyProperty.Register(
"Message", typeof(string), typeof(MessageBox), new PropertyMetadata(default(string)));
public string Message
{
get => (string) GetValue(MessageProperty);
set => SetValue(MessageProperty, value);
}
public static readonly DependencyProperty ImageProperty = DependencyProperty.Register(
"Image", typeof(Geometry), typeof(MessageBox), new PropertyMetadata(default(Geometry)));
public Geometry Image
{
get => (Geometry) GetValue(ImageProperty);
set => SetValue(ImageProperty, value);
}
public static readonly DependencyProperty ImageBrushProperty = DependencyProperty.Register(
"ImageBrush", typeof(Brush), typeof(MessageBox), new PropertyMetadata(default(Brush)));
public Brush ImageBrush
{
get => (Brush) GetValue(ImageBrushProperty);
set => SetValue(ImageBrushProperty, value);
}
public static readonly DependencyProperty ShowImageProperty = DependencyProperty.Register(
"ShowImage", typeof(bool), typeof(MessageBox), new PropertyMetadata(ValueBoxes.FalseBox));
public bool ShowImage
{
get => (bool) GetValue(ShowImageProperty);
set => SetValue(ShowImageProperty, value);
}
private MessageBox()
{
CommandBindings.Add(new CommandBinding(ControlCommands.Confirm, (s, e) =>
{
_messageBoxResult = MessageBoxResult.OK;
Close();
}, (s, e) => e.CanExecute = _showOk));
CommandBindings.Add(new CommandBinding(ControlCommands.Cancel, (s, e) =>
{
_messageBoxResult = MessageBoxResult.Cancel;
Close();
}, (s, e) => e.CanExecute = _showCancel));
CommandBindings.Add(new CommandBinding(ControlCommands.Yes, (s, e) =>
{
_messageBoxResult = MessageBoxResult.Yes;
Close();
}, (s, e) => e.CanExecute = _showYes));
CommandBindings.Add(new CommandBinding(ControlCommands.No, (s, e) =>
{
_messageBoxResult = MessageBoxResult.No;
Close();
}, (s, e) => e.CanExecute = _showNo));
}
/// <summary>
/// 成功
/// </summary>
/// <param name="messageBoxText"></param>
/// <param name="caption"></param>
public static MessageBoxResult Success(string messageBoxText, string caption = null)
{
var messageBox = CreateMessageBox(null, messageBoxText, caption, MessageBoxButton.OK, MessageBoxImage.None, MessageBoxResult.OK);
SetButtonStatus(messageBox, MessageBoxButton.OK);
messageBox.ShowImage = true;
messageBox.Image = ResourceHelper.GetResource<Geometry>(ResourceToken.SuccessGeometry);
messageBox.ImageBrush = ResourceHelper.GetResource<Brush>(ResourceToken.SuccessBrush);
messageBox.ShowDialog();
return messageBox._messageBoxResult;
}
/// <summary>
/// 消息
/// </summary>
/// <param name="messageBoxText"></param>
/// <param name="caption"></param>
public static MessageBoxResult Info(string messageBoxText, string caption = null)
{
var messageBox = CreateMessageBox(null, messageBoxText, caption, MessageBoxButton.OK, MessageBoxImage.Information, MessageBoxResult.OK);
SetButtonStatus(messageBox, MessageBoxButton.OK);
SetImage(messageBox, MessageBoxImage.Information);
messageBox.ShowDialog();
return messageBox._messageBoxResult;
}
/// <summary>
/// 警告
/// </summary>
/// <param name="messageBoxText"></param>
/// <param name="caption"></param>
public static MessageBoxResult Warning(string messageBoxText, string caption = null)
{
var messageBox = CreateMessageBox(null, messageBoxText, caption, MessageBoxButton.OK, MessageBoxImage.Warning, MessageBoxResult.OK);
SetButtonStatus(messageBox, MessageBoxButton.OK);
SetImage(messageBox, MessageBoxImage.Warning);
messageBox.ShowDialog();
return messageBox._messageBoxResult;
}
/// <summary>
/// 错误
/// </summary>
/// <param name="messageBoxText"></param>
/// <param name="caption"></param>
public static MessageBoxResult Error(string messageBoxText, string caption = null)
{
var messageBox = CreateMessageBox(null, messageBoxText, caption, MessageBoxButton.OK, MessageBoxImage.Error, MessageBoxResult.OK);
SetButtonStatus(messageBox, MessageBoxButton.OK);
SetImage(messageBox, MessageBoxImage.Error);
messageBox.ShowDialog();
return messageBox._messageBoxResult;
}
/// <summary>
/// 严重
/// </summary>
/// <param name="messageBoxText"></param>
/// <param name="caption"></param>
public static MessageBoxResult Fatal(string messageBoxText, string caption = null)
{
var messageBox = CreateMessageBox(null, messageBoxText, caption, MessageBoxButton.OK, MessageBoxImage.None, MessageBoxResult.OK);
SetButtonStatus(messageBox, MessageBoxButton.OK);
messageBox.ShowImage = true;
messageBox.Image = ResourceHelper.GetResource<Geometry>(ResourceToken.FatalGeometry);
messageBox.ImageBrush = ResourceHelper.GetResource<Brush>(ResourceToken.PrimaryTextBrush);
messageBox.ShowDialog();
return messageBox._messageBoxResult;
}
/// <summary>
/// 询问
/// </summary>
/// <param name="messageBoxText"></param>
/// <param name="caption"></param>
public static MessageBoxResult Ask(string messageBoxText, string caption = null)
{
var messageBox = CreateMessageBox(null, messageBoxText, caption, MessageBoxButton.OKCancel, MessageBoxImage.Question, MessageBoxResult.Cancel);
SetButtonStatus(messageBox, MessageBoxButton.OKCancel);
SetImage(messageBox, MessageBoxImage.Question);
messageBox.ShowDialog();
return messageBox._messageBoxResult;
}
public static MessageBoxResult CustomShow(string messageBoxText, string caption = null, MessageBoxButton button = MessageBoxButton.OK, string iconKey = "", string iconBrushKey = "", MessageBoxResult defaultResult = MessageBoxResult.None)
{
var messageBox = CreateMessageBox(null, messageBoxText, caption, button, MessageBoxImage.None, defaultResult);
messageBox.ShowImage = true;
messageBox.Image = ResourceHelper.GetResource<Geometry>(iconKey);
messageBox.ImageBrush = ResourceHelper.GetResource<Brush>(iconBrushKey);
messageBox.ShowDialog();
return messageBox._messageBoxResult;
}
public static MessageBoxResult Show(string messageBoxText, string caption = null, MessageBoxButton button = MessageBoxButton.OK, MessageBoxImage icon = MessageBoxImage.None, MessageBoxResult defaultResult = MessageBoxResult.None)
{
return Show(null, messageBoxText, caption, button, icon, defaultResult);
}
public static MessageBoxResult Show(System.Windows.Window owner, string messageBoxText, string caption = null, MessageBoxButton button = MessageBoxButton.OK, MessageBoxImage icon = MessageBoxImage.None, MessageBoxResult defaultResult = MessageBoxResult.None)
{
var messageBox = CreateMessageBox(owner, messageBoxText, caption, button, icon, defaultResult);
SetButtonStatus(messageBox, button);
SetImage(messageBox, icon);
messageBox.ShowDialog();
return messageBox._messageBoxResult;
}
private static MessageBox CreateMessageBox(
System.Windows.Window owner,
string messageBoxText,
string caption,
MessageBoxButton button,
MessageBoxImage icon,
MessageBoxResult defaultResult)
{
if (!IsValidMessageBoxButton(button))
{
throw new InvalidEnumArgumentException(nameof(button), (int)button, typeof(MessageBoxButton));
}
if (!IsValidMessageBoxImage(icon))
{
throw new InvalidEnumArgumentException(nameof(icon), (int)icon, typeof(MessageBoxImage));
}
if (!IsValidMessageBoxResult(defaultResult))
{
throw new InvalidEnumArgumentException(nameof(defaultResult), (int)defaultResult, typeof(MessageBoxResult));
}
var ownerWindow = owner ?? Application.Current.Windows.OfType<Window>().SingleOrDefault(x => x.IsActive);
var ownerIsNull = ownerWindow is null;
return new MessageBox
{
Message = messageBoxText,
Owner = ownerWindow,
WindowStartupLocation = ownerIsNull ? WindowStartupLocation.CenterScreen : WindowStartupLocation.CenterOwner,
ShowTitle = true,
Title = caption,
Topmost = ownerIsNull,
_messageBoxResult = defaultResult
};
}
private static void SetButtonStatus(MessageBox messageBox, MessageBoxButton messageBoxButton)
{
switch (messageBoxButton)
{
case MessageBoxButton.OK:
messageBox._showOk = true;
break;
case MessageBoxButton.OKCancel:
messageBox._showOk = true;
messageBox._showCancel = true;
break;
case MessageBoxButton.YesNo:
messageBox._showYes = true;
messageBox._showNo = true;
break;
case MessageBoxButton.YesNoCancel:
messageBox._showYes = true;
messageBox._showNo = true;
messageBox._showCancel = true;
break;
}
}
private static void SetImage(MessageBox messageBox, MessageBoxImage messageBoxImage)
{
var iconKey = string.Empty;
var iconBrushKey = string.Empty;
switch (messageBoxImage)
{
case MessageBoxImage.Error:
iconKey = ResourceToken.ErrorGeometry;
iconBrushKey = ResourceToken.DangerBrush;
break;
case MessageBoxImage.Question:
iconKey = ResourceToken.AskGeometry;
iconBrushKey = ResourceToken.AccentBrush;
break;
case MessageBoxImage.Warning:
iconKey = ResourceToken.WarningGeometry;
iconBrushKey = ResourceToken.WarningBrush;
break;
case MessageBoxImage.Information:
iconKey = ResourceToken.InfoGeometry;
iconBrushKey = ResourceToken.InfoBrush;
break;
}
if (string.IsNullOrEmpty(iconKey)) return;
messageBox.ShowImage = true;
messageBox.Image = ResourceHelper.GetResource<Geometry>(iconKey);
messageBox.ImageBrush = ResourceHelper.GetResource<Brush>(iconBrushKey);
}
private static bool IsValidMessageBoxButton(MessageBoxButton value)
{
return value == MessageBoxButton.OK
|| value == MessageBoxButton.OKCancel
|| value == MessageBoxButton.YesNo
|| value == MessageBoxButton.YesNoCancel;
}
private static bool IsValidMessageBoxImage(MessageBoxImage value)
{
return value == MessageBoxImage.Asterisk
|| value == MessageBoxImage.Error
|| value == MessageBoxImage.Exclamation
|| value == MessageBoxImage.Hand
|| value == MessageBoxImage.Information
|| value == MessageBoxImage.None
|| value == MessageBoxImage.Question
|| value == MessageBoxImage.Stop
|| value == MessageBoxImage.Warning;
}
private static bool IsValidMessageBoxResult(MessageBoxResult value)
{
return value == MessageBoxResult.Cancel
|| value == MessageBoxResult.No
|| value == MessageBoxResult.None
|| value == MessageBoxResult.OK
|| value == MessageBoxResult.Yes;
}
}
}
@@ -0,0 +1,235 @@
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using HandyControl.Data;
using HandyControl.Interactivity;
using HandyControl.Tools;
namespace HandyControl.Controls
{
/// <summary>
/// 弹出窗口
/// </summary>
[TemplatePart(Name = ElementMainBorder, Type = typeof(Border))]
[TemplatePart(Name = ElementTitleBlock, Type = typeof(TextBlock))]
public class PopupWindow : System.Windows.Window
{
#region Constants
private const string ElementMainBorder = "PART_MainBorder";
private const string ElementTitleBlock = "PART_TitleBlock";
#endregion Constants
private Border _mainBorder;
private TextBlock _titleBlock;
private bool _showBackground = true;
private FrameworkElement _targetElement;
public override void OnApplyTemplate()
{
if (_titleBlock != null)
{
_titleBlock.MouseLeftButtonDown -= TitleBlock_OnMouseLeftButtonDown;
}
base.OnApplyTemplate();
_mainBorder = GetTemplateChild(ElementMainBorder) as Border;
_titleBlock = GetTemplateChild(ElementTitleBlock) as TextBlock;
if (_titleBlock != null)
{
_titleBlock.MouseLeftButtonDown += TitleBlock_OnMouseLeftButtonDown;
}
if (PopupElement != null)
{
_mainBorder.Child = PopupElement;
}
}
internal static readonly DependencyProperty ContentStrProperty = DependencyProperty.Register(
"ContentStr", typeof(string), typeof(PopupWindow), new PropertyMetadata(default(string)));
internal string ContentStr
{
get => (string) GetValue(ContentStrProperty);
set => SetValue(ContentStrProperty, value);
}
private bool IsDialog { get; set; }
public PopupWindow()
{
CommandBindings.Add(new CommandBinding(ControlCommands.Close, CloseButton_OnClick));
CommandBindings.Add(new CommandBinding(ControlCommands.Confirm, ButtonOk_OnClick));
CommandBindings.Add(new CommandBinding(ControlCommands.Cancel, ButtonCancle_OnClick));
Closing += (sender, args) =>
{
if (!IsDialog)
Owner?.Focus();
};
Loaded += (s, e) =>
{
if (!_showBackground)
{
var point = ArithmeticHelper.CalSafePoint(_targetElement, PopupElement, BorderThickness);
Left = point.X;
Top = point.Y;
Opacity = 1;
}
};
try
{
Owner = Application.Current.MainWindow;
}
catch
{
// ignored
}
}
public PopupWindow(System.Windows.Window owner) : this() => Owner = owner;
private void CloseButton_OnClick(object sender, RoutedEventArgs e) => Close();
public FrameworkElement PopupElement { get; set; }
public static readonly DependencyProperty ShowTitleProperty = DependencyProperty.Register(
"ShowTitle", typeof(bool), typeof(PopupWindow), new PropertyMetadata(ValueBoxes.TrueBox));
public bool ShowTitle
{
get => (bool) GetValue(ShowTitleProperty);
set => SetValue(ShowTitleProperty, value);
}
public static readonly DependencyProperty ShowCancelProperty = DependencyProperty.Register(
"ShowCancel", typeof(bool), typeof(PopupWindow), new PropertyMetadata(ValueBoxes.FalseBox));
public bool ShowCancel
{
get => (bool)GetValue(ShowCancelProperty);
set => SetValue(ShowCancelProperty, value);
}
public static readonly DependencyProperty ShowBorderProperty = DependencyProperty.Register(
"ShowBorder", typeof(bool), typeof(PopupWindow), new PropertyMetadata(ValueBoxes.FalseBox));
public bool ShowBorder
{
get => (bool) GetValue(ShowBorderProperty);
set => SetValue(ShowBorderProperty, value);
}
private void TitleBlock_OnMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
if (e.LeftButton == MouseButtonState.Pressed)
{
DragMove();
}
}
public void Show(FrameworkElement element, bool showBackground = true)
{
if (!showBackground)
{
Opacity = 0;
AllowsTransparency = true;
WindowStyle = WindowStyle.None;
ShowTitle = false;
MinWidth = 0;
MinHeight = 0;
}
_showBackground = showBackground;
_targetElement = element;
Show();
}
public void ShowDialog(FrameworkElement element, bool showBackground = true)
{
if (!showBackground)
{
Opacity = 0;
AllowsTransparency = true;
WindowStyle = WindowStyle.None;
ShowTitle = false;
MinWidth = 0;
MinHeight = 0;
}
_showBackground = showBackground;
_targetElement = element;
ShowDialog();
}
public void Show(System.Windows.Window element, Point point)
{
Left = element.Left + point.X;
Top = element.Top + point.Y;
Show();
}
public static void Show(string message)
{
var window = new PopupWindow
{
AllowsTransparency = true,
WindowStyle = WindowStyle.None,
ContentStr = message,
WindowStartupLocation = WindowStartupLocation.CenterScreen,
Background = ResourceHelper.GetResource<Brush>(ResourceToken.PrimaryBrush)
};
window.Show();
}
public static bool? ShowDialog(string message, string title = default(string), bool showCancel = false)
{
var window = new PopupWindow
{
AllowsTransparency = true,
WindowStyle = WindowStyle.None,
ContentStr = message,
IsDialog = true,
WindowStartupLocation = WindowStartupLocation.CenterScreen,
ShowBorder = true,
Title = string.IsNullOrEmpty(title) ? Properties.Langs.Lang.Tip : title,
ShowCancel = showCancel
};
return window.ShowDialog();
}
private void ButtonOk_OnClick(object sender, RoutedEventArgs e)
{
if (IsDialog)
{
DialogResult = true;
}
Close();
}
private void ButtonCancle_OnClick(object sender, RoutedEventArgs e)
{
if (IsDialog)
{
DialogResult = false;
}
Close();
}
protected override void OnClosed(EventArgs e)
{
base.OnClosed(e);
PopupElement = null;
}
}
}
@@ -0,0 +1,375 @@
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Input;
using System.Windows.Media;
using HandyControl.Data;
using HandyControl.Tools;
using HandyControl.Tools.Extension;
#if netle40
using Microsoft.Windows.Shell;
#else
using System.Windows.Shell;
#endif
namespace HandyControl.Controls
{
[TemplatePart(Name = ElementNonClientArea, Type = typeof(UIElement))]
public class Window : System.Windows.Window
{
private const string ElementNonClientArea = "PART_NonClientArea";
public static readonly DependencyProperty NonClientAreaContentProperty = DependencyProperty.Register(
"NonClientAreaContent", typeof(object), typeof(Window), new PropertyMetadata(default(object)));
public static readonly DependencyProperty CloseButtonHoverBackgroundProperty = DependencyProperty.Register(
"CloseButtonHoverBackground", typeof(Brush), typeof(Window),
new PropertyMetadata(default(Brush)));
public static readonly DependencyProperty CloseButtonHoverForegroundProperty =
DependencyProperty.Register(
"CloseButtonHoverForeground", typeof(Brush), typeof(Window),
new PropertyMetadata(default(Brush)));
public static readonly DependencyProperty CloseButtonBackgroundProperty = DependencyProperty.Register(
"CloseButtonBackground", typeof(Brush), typeof(Window), new PropertyMetadata(Brushes.Transparent));
public static readonly DependencyProperty CloseButtonForegroundProperty = DependencyProperty.Register(
"CloseButtonForeground", typeof(Brush), typeof(Window),
new PropertyMetadata(Brushes.White));
public static readonly DependencyProperty OtherButtonBackgroundProperty = DependencyProperty.Register(
"OtherButtonBackground", typeof(Brush), typeof(Window), new PropertyMetadata(Brushes.Transparent));
public static readonly DependencyProperty OtherButtonForegroundProperty = DependencyProperty.Register(
"OtherButtonForeground", typeof(Brush), typeof(Window),
new PropertyMetadata(Brushes.White));
public static readonly DependencyProperty OtherButtonHoverBackgroundProperty = DependencyProperty.Register(
"OtherButtonHoverBackground", typeof(Brush), typeof(Window),
new PropertyMetadata(default(Brush)));
public static readonly DependencyProperty OtherButtonHoverForegroundProperty =
DependencyProperty.Register(
"OtherButtonHoverForeground", typeof(Brush), typeof(Window),
new PropertyMetadata(default(Brush)));
public static readonly DependencyProperty NonClientAreaBackgroundProperty = DependencyProperty.Register(
"NonClientAreaBackground", typeof(Brush), typeof(Window),
new PropertyMetadata(default(Brush)));
public static readonly DependencyProperty NonClientAreaForegroundProperty = DependencyProperty.Register(
"NonClientAreaForeground", typeof(Brush), typeof(Window),
new PropertyMetadata(default(Brush)));
public static readonly DependencyProperty NonClientAreaHeightProperty = DependencyProperty.Register(
"NonClientAreaHeight", typeof(double), typeof(Window),
new PropertyMetadata(28.0));
public static readonly DependencyProperty ShowNonClientAreaProperty = DependencyProperty.Register(
"ShowNonClientArea", typeof(bool), typeof(Window),
new PropertyMetadata(ValueBoxes.TrueBox, OnShowNonClientAreaChanged));
public static readonly DependencyProperty ShowTitleProperty = DependencyProperty.Register(
"ShowTitle", typeof(bool), typeof(Window), new PropertyMetadata(ValueBoxes.FalseBox));
public static readonly DependencyProperty IsFullScreenProperty = DependencyProperty.Register(
"IsFullScreen", typeof(bool), typeof(Window),
new PropertyMetadata(ValueBoxes.FalseBox, OnIsFullScreenChanged));
private bool _isFullScreen;
private UIElement _nonClientArea;
private bool _showNonClientArea = true;
private double _tempNonClientAreaHeight;
private Thickness _tempThickness;
private WindowState _tempWindowState;
private WindowStyle _tempWindowStyle;
private ResizeMode _tempResizeMode;
public Window()
{
var chrome = new WindowChrome
{
CornerRadius = new CornerRadius(),
GlassFrameThickness = new Thickness(0, 0, 0, 1)
};
BindingOperations.SetBinding(chrome, WindowChrome.CaptionHeightProperty,
new Binding(NonClientAreaHeightProperty.Name) {Source = this});
WindowChrome.SetWindowChrome(this, chrome);
Loaded += delegate
{
_tempThickness = BorderThickness;
if (WindowState == WindowState.Maximized)
{
BorderThickness = new Thickness();
}
CommandBindings.Add(new CommandBinding(SystemCommands.MinimizeWindowCommand,
(s, e) => WindowState = WindowState.Minimized));
CommandBindings.Add(new CommandBinding(SystemCommands.MaximizeWindowCommand,
(s, e) => WindowState = WindowState.Maximized));
CommandBindings.Add(new CommandBinding(SystemCommands.RestoreWindowCommand,
(s, e) => WindowState = WindowState.Normal));
CommandBindings.Add(new CommandBinding(SystemCommands.CloseWindowCommand, (s, e) => Close()));
CommandBindings.Add(new CommandBinding(SystemCommands.ShowSystemMenuCommand, ShowSystemMenu));
_tempNonClientAreaHeight = NonClientAreaHeight;
_tempWindowState = WindowState;
_tempWindowStyle = WindowStyle;
_tempResizeMode = ResizeMode;
SwitchIsFullScreen(_isFullScreen);
SwitchShowNonClientArea(_showNonClientArea);
};
}
public Brush CloseButtonBackground
{
get => (Brush) GetValue(CloseButtonBackgroundProperty);
set => SetValue(CloseButtonBackgroundProperty, value);
}
public Brush CloseButtonForeground
{
get => (Brush) GetValue(CloseButtonForegroundProperty);
set => SetValue(CloseButtonForegroundProperty, value);
}
public Brush OtherButtonBackground
{
get => (Brush) GetValue(OtherButtonBackgroundProperty);
set => SetValue(OtherButtonBackgroundProperty, value);
}
public Brush OtherButtonForeground
{
get => (Brush) GetValue(OtherButtonForegroundProperty);
set => SetValue(OtherButtonForegroundProperty, value);
}
public double NonClientAreaHeight
{
get => (double) GetValue(NonClientAreaHeightProperty);
set => SetValue(NonClientAreaHeightProperty, value);
}
public bool IsFullScreen
{
get => (bool) GetValue(IsFullScreenProperty);
set => SetValue(IsFullScreenProperty, value);
}
public object NonClientAreaContent
{
get => GetValue(NonClientAreaContentProperty);
set => SetValue(NonClientAreaContentProperty, value);
}
public Brush CloseButtonHoverBackground
{
get => (Brush) GetValue(CloseButtonHoverBackgroundProperty);
set => SetValue(CloseButtonHoverBackgroundProperty, value);
}
public Brush CloseButtonHoverForeground
{
get => (Brush) GetValue(CloseButtonHoverForegroundProperty);
set => SetValue(CloseButtonHoverForegroundProperty, value);
}
public Brush OtherButtonHoverBackground
{
get => (Brush) GetValue(OtherButtonHoverBackgroundProperty);
set => SetValue(OtherButtonHoverBackgroundProperty, value);
}
public Brush OtherButtonHoverForeground
{
get => (Brush) GetValue(OtherButtonHoverForegroundProperty);
set => SetValue(OtherButtonHoverForegroundProperty, value);
}
public Brush NonClientAreaBackground
{
get => (Brush) GetValue(NonClientAreaBackgroundProperty);
set => SetValue(NonClientAreaBackgroundProperty, value);
}
public Brush NonClientAreaForeground
{
get => (Brush) GetValue(NonClientAreaForegroundProperty);
set => SetValue(NonClientAreaForegroundProperty, value);
}
public bool ShowNonClientArea
{
get => (bool) GetValue(ShowNonClientAreaProperty);
set => SetValue(ShowNonClientAreaProperty, value);
}
public bool ShowTitle
{
get => (bool) GetValue(ShowTitleProperty);
set => SetValue(ShowTitleProperty, value);
}
private static void OnShowNonClientAreaChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var ctl = (Window) d;
ctl.SwitchShowNonClientArea((bool) e.NewValue);
}
private void SwitchShowNonClientArea(bool showNonClientArea)
{
if (_nonClientArea == null)
{
_showNonClientArea = showNonClientArea;
return;
}
if (showNonClientArea)
{
if (IsFullScreen)
{
_nonClientArea.Show(false);
_tempNonClientAreaHeight = NonClientAreaHeight;
NonClientAreaHeight = 0;
}
else
{
_nonClientArea.Show(true);
NonClientAreaHeight = _tempNonClientAreaHeight;
}
}
else
{
_nonClientArea.Show(false);
_tempNonClientAreaHeight = NonClientAreaHeight;
NonClientAreaHeight = 0;
}
}
private static void OnIsFullScreenChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var ctl = (Window) d;
ctl.SwitchIsFullScreen((bool) e.NewValue);
}
private void SwitchIsFullScreen(bool isFullScreen)
{
if (_nonClientArea == null)
{
_isFullScreen = isFullScreen;
return;
}
if (isFullScreen)
{
_nonClientArea.Show(false);
_tempNonClientAreaHeight = NonClientAreaHeight;
NonClientAreaHeight = 0;
_tempWindowState = WindowState;
_tempWindowStyle = WindowStyle;
_tempResizeMode = ResizeMode;
WindowStyle = WindowStyle.None;
//下面三行不能改变,就是故意的
WindowState = WindowState.Maximized;
WindowState = WindowState.Minimized;
WindowState = WindowState.Maximized;
}
else
{
if (ShowNonClientArea)
{
_nonClientArea.Show(true);
NonClientAreaHeight = _tempNonClientAreaHeight;
}
else
{
_nonClientArea.Show(false);
_tempNonClientAreaHeight = NonClientAreaHeight;
NonClientAreaHeight = 0;
}
WindowState = _tempWindowState;
WindowStyle = _tempWindowStyle;
ResizeMode = _tempResizeMode;
}
}
protected override void OnStateChanged(EventArgs e)
{
base.OnStateChanged(e);
if (WindowState == WindowState.Maximized)
{
BorderThickness = new Thickness();
}
else if (WindowState == WindowState.Normal)
{
BorderThickness = _tempThickness;
}
}
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
_nonClientArea = GetTemplateChild(ElementNonClientArea) as UIElement;
if (SizeToContent != SizeToContent.WidthAndHeight)
return;
SizeToContent = SizeToContent.Height;
Dispatcher.BeginInvoke(new Action(() => { SizeToContent = SizeToContent.WidthAndHeight; }));
}
private void ShowSystemMenu(object sender, ExecutedRoutedEventArgs e)
{
var point = WindowState == WindowState.Maximized
? new Point(0, 28)
: new Point(Left, Top + 28);
SystemCommands.ShowSystemMenu(this, point);
}
protected override void OnContentRendered(EventArgs e)
{
base.OnContentRendered(e);
if (SizeToContent == SizeToContent.WidthAndHeight)
InvalidateMeasure();
}
/// <summary>
/// 获取自定义窗口
/// </summary>
/// <returns></returns>
public static Window GetCustomWindow(FrameworkElement content)
{
var window = new Window
{
Style = ResourceHelper.GetResource<Style>(ResourceToken.WindowWin10),
Content = content
};
window.Loaded += (s, e) =>
{
window.Width = window.BorderThickness.Left + window.BorderThickness.Right + content.Width;
if (!(window.Template.FindName("GridMenu", window) is Grid nemuArea))
throw new NullReferenceException("can not find GridMenu in template");
window.Height = window.BorderThickness.Top + window.BorderThickness.Bottom + content.Height +
nemuArea.ActualHeight;
};
return window;
}
}
}
@@ -0,0 +1,13 @@
using System.Windows;
namespace HandyControl.Data
{
public class CancelRoutedEventArgs : RoutedEventArgs
{
public CancelRoutedEventArgs(RoutedEvent routedEvent, object source) : base(routedEvent, source)
{
}
public bool Cancel { get; set; }
}
}
@@ -0,0 +1,19 @@
using System.Windows;
namespace HandyControl.Data
{
public class FunctionEventArgs<T> : RoutedEventArgs
{
public FunctionEventArgs(T info)
{
Info = info;
}
public FunctionEventArgs(RoutedEvent routedEvent, object source) : base(routedEvent, source)
{
}
public T Info { get; set; }
}
}
@@ -0,0 +1,75 @@
using System.Windows.Media;
namespace HandyControl.Data
{
/// <summary>
/// 颜色范围
/// </summary>
public class ColorRange
{
/// <summary>
/// 颜色1
/// </summary>
private Color _color1;
/// <summary>
/// 颜色1
/// </summary>
public Color Color1
{
get => _color1;
set
{
_color1 = value;
Update();
}
}
/// <summary>
/// 颜色2
/// </summary>
private Color _color2;
/// <summary>
/// 颜色2
/// </summary>
public Color Color2
{
get => _color2;
set
{
_color2 = value;
Update();
}
}
/// <summary>
/// 颜色差值
/// </summary>
private readonly int[] _subColorArr = new int[4];
/// <summary>
/// 更新
/// </summary>
private void Update()
{
_subColorArr[0] = Color1.A - Color2.A;
_subColorArr[1] = Color1.R - Color2.R;
_subColorArr[2] = Color1.G - Color2.G;
_subColorArr[3] = Color1.B - Color2.B;
}
/// <summary>
/// 获取指定比例处的颜色
/// </summary>
/// <param name="range">范围(0-1)</param>
/// <returns></returns>
public Color GetColor(double range)
{
if (range < 0 || range > 1) return default(Color);
return Color.FromArgb((byte)(_color1.A - _subColorArr[0] * range), (byte)(_color1.R - _subColorArr[1] * range),
(byte)(_color1.G - _subColorArr[2] * range), (byte)(_color1.B - _subColorArr[3] * range));
}
}
}
@@ -0,0 +1,13 @@
namespace HandyControl.Data
{
public enum InfoType
{
Success = 0,
Info,
Warning,
Error,
Fatal,
Ask
}
}
@@ -0,0 +1,30 @@
namespace HandyControl.Data
{
/// <summary>
/// IP类型枚举
/// </summary>
public enum IpType
{
/// <summary>
/// A类IP地址
/// </summary>
A = 0,
/// <summary>
/// B类IP地址
/// </summary>
B,
/// <summary>
/// C类IP地址
/// </summary>
C,
/// <summary>
/// D类IP地址
/// </summary>
D,
/// <summary>
/// E类IP地址
/// </summary>
E
}
}
@@ -0,0 +1,24 @@
namespace HandyControl.Data
{
/// <summary>
/// 表示一个操作的返回结果类型
/// </summary>
public enum ResultType
{
/// <summary>
/// 成功
/// </summary>
Success,
/// <summary>
/// 失败
/// </summary>
Failed,
/// <summary>
/// 无数据
/// </summary>
None
}
}
@@ -0,0 +1,10 @@
namespace HandyControl.Data
{
public enum SkinType
{
Default,
Dark,
Violet
}
}
@@ -0,0 +1,23 @@
namespace HandyControl.Data
{
/// <summary>
/// 步骤状态
/// </summary>
public enum StepStatus
{
/// <summary>
/// 完成
/// </summary>
Complete,
/// <summary>
/// 进行中
/// </summary>
UnderWay,
/// <summary>
/// 等待中
/// </summary>
Waiting
}
}
@@ -0,0 +1,27 @@
namespace HandyControl.Data
{
/// <summary>
/// 文本类型枚举
/// </summary>
public enum TextType
{
Common,
Phone,
Mail,
Url,
Chinese,
Number,
Digits,
PInt,
NInt,
Int,
NnInt,
NpInt,
PDouble,
NDouble,
Double,
NnDouble,
NpDouble,
}
}
@@ -0,0 +1,9 @@
namespace HandyControl.Data
{
public enum TipPlacement
{
TopLeft,
BottomRight,
}
}
@@ -0,0 +1,12 @@
namespace HandyControl.Data
{
/// <summary>
/// 标题对齐方式
/// </summary>
public enum TitleAlignment
{
Left,
Top
}
}
@@ -0,0 +1,68 @@
using System;
using System.Collections;
namespace HandyControl.Data
{
internal enum FrugalMapStoreState
{
Success,
ThreeObjectMap,
SixObjectMap,
Array,
SortedArray,
Hashtable
}
internal delegate void FrugalMapIterationCallback(ArrayList list, int key, object value);
abstract class FrugalMapBase
{
public abstract FrugalMapStoreState InsertEntry(int key, Object value);
public abstract void RemoveEntry(int key);
/// <summary>
/// Looks for an entry that contains the given key, null is returned if the
/// key is not found.
/// </summary>
public abstract Object Search(int key);
/// <summary>
/// A routine used by enumerators that need a sorted map
/// </summary>
public abstract void Sort();
/// <summary>
/// A routine used by enumerators to iterate through the map
/// </summary>
public abstract void GetKeyValuePair(int index, out int key, out Object value);
/// <summary>
/// A routine used to iterate through all the entries in the map
/// </summary>
public abstract void Iterate(ArrayList list, FrugalMapIterationCallback callback);
/// <summary>
/// Promotes the key/value pairs in the current collection to the next larger
/// and more complex storage model.
/// </summary>
public abstract void Promote(FrugalMapBase newMap);
/// <summary>
/// Size of this data store
/// </summary>
public abstract int Count
{
get;
}
protected const int INVALIDKEY = 0x7FFFFFFF;
internal struct Entry
{
public int Key;
public object Value;
}
}
}
@@ -0,0 +1,271 @@
using System;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Runtime.InteropServices;
using System.Security.Permissions;
using HandyControl.Tools;
namespace HandyControl.Data
{
// ReSharper disable once InconsistentNaming
internal class GPStream : ExternDllHelper.IStream
{
protected Stream DataStream;
// to support seeking ahead of the stream length...
long _virtualPosition = -1;
internal GPStream(Stream stream)
{
if (!stream.CanSeek)
{
const int readBlock = 256;
var bytes = new byte[readBlock];
int readLen;
var current = 0;
do
{
if (bytes.Length < current + readBlock)
{
var newData = new byte[bytes.Length * 2];
Array.Copy(bytes, newData, bytes.Length);
bytes = newData;
}
readLen = stream.Read(bytes, current, readBlock);
current += readLen;
} while (readLen != 0);
DataStream = new MemoryStream(bytes);
}
else
{
DataStream = stream;
}
}
private void ActualizeVirtualPosition()
{
if (_virtualPosition == -1) return;
if (_virtualPosition > DataStream.Length)
DataStream.SetLength(_virtualPosition);
DataStream.Position = _virtualPosition;
_virtualPosition = -1;
}
public virtual ExternDllHelper.IStream Clone()
{
NotImplemented();
return null;
}
public virtual void Commit(int grfCommitFlags)
{
DataStream.Flush();
// Extend the length of the file if needed.
ActualizeVirtualPosition();
}
[
UIPermission(SecurityAction.Demand, Window = UIPermissionWindow.AllWindows),
SecurityPermission(SecurityAction.Assert, Flags = SecurityPermissionFlag.UnmanagedCode)
]
public virtual long CopyTo(ExternDllHelper.IStream pstm, long cb, long[] pcbRead)
{
const int bufsize = 4096;
var buffer = Marshal.AllocHGlobal(bufsize);
if (buffer == IntPtr.Zero) throw new OutOfMemoryException();
long written = 0;
try
{
while (written < cb)
{
var toRead = bufsize;
if (written + toRead > cb) toRead = (int)(cb - written);
var read = Read(buffer, toRead);
if (read == 0) break;
if (pstm.Write(buffer, read) != read)
{
throw EFail("Wrote an incorrect number of bytes");
}
written += read;
}
}
finally
{
Marshal.FreeHGlobal(buffer);
}
if (pcbRead != null && pcbRead.Length > 0)
{
pcbRead[0] = written;
}
return written;
}
public virtual Stream GetDataStream()
{
return DataStream;
}
public virtual void LockRegion(long libOffset, long cb, int dwLockType)
{
}
protected static ExternalException EFail(string msg)
{
throw new ExternalException(msg, ExternDllHelper.E_FAIL);
}
protected static void NotImplemented()
{
throw new ExternalException("NotImplemented");
}
public virtual int Read(IntPtr buf, int length)
{
var buffer = new byte[length];
var count = Read(buffer, length);
Marshal.Copy(buffer, 0, buf, length);
return count;
}
public virtual int Read(byte[] buffer, int length)
{
ActualizeVirtualPosition();
return DataStream.Read(buffer, 0, length);
}
public virtual void Revert()
{
NotImplemented();
}
public virtual long Seek(long offset, int origin)
{
var pos = _virtualPosition;
if (_virtualPosition == -1)
{
pos = DataStream.Position;
}
var len = DataStream.Length;
switch (origin)
{
case ExternDllHelper.StreamConsts.STREAM_SEEK_SET:
if (offset <= len)
{
DataStream.Position = offset;
_virtualPosition = -1;
}
else
{
_virtualPosition = offset;
}
break;
case ExternDllHelper.StreamConsts.STREAM_SEEK_END:
if (offset <= 0)
{
DataStream.Position = len + offset;
_virtualPosition = -1;
}
else
{
_virtualPosition = len + offset;
}
break;
case ExternDllHelper.StreamConsts.STREAM_SEEK_CUR:
if (offset + pos <= len)
{
DataStream.Position = pos + offset;
_virtualPosition = -1;
}
else
{
_virtualPosition = offset + pos;
}
break;
}
return _virtualPosition != -1 ? _virtualPosition : DataStream.Position;
}
public virtual void SetSize(long value)
{
DataStream.SetLength(value);
}
public void Stat(IntPtr pstatstg, int grfStatFlag)
{
var stats = new STATSTG {cbSize = DataStream.Length};
Marshal.StructureToPtr(stats, pstatstg, true);
}
public virtual void UnlockRegion(long libOffset, long cb, int dwLockType)
{
}
public virtual int Write(IntPtr buf, int length)
{
var buffer = new byte[length];
Marshal.Copy(buf, buffer, 0, length);
return Write(buffer, length);
}
public virtual int Write(byte[] buffer, int length)
{
ActualizeVirtualPosition();
DataStream.Write(buffer, 0, length);
return length;
}
[StructLayout(LayoutKind.Sequential)]
[SuppressMessage("ReSharper", "InconsistentNaming")]
public class STATSTG
{
[SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources")]
public IntPtr pwcsName = IntPtr.Zero;
public int type;
[MarshalAs(UnmanagedType.I8)]
public long cbSize;
[MarshalAs(UnmanagedType.I8)]
public long mtime;
[MarshalAs(UnmanagedType.I8)]
public long ctime;
[MarshalAs(UnmanagedType.I8)]
public long atime;
[MarshalAs(UnmanagedType.I4)]
public int grfMode;
[MarshalAs(UnmanagedType.I4)]
public int grfLocksSupported;
public int clsid_data1;
[MarshalAs(UnmanagedType.I2)]
public short clsid_data2;
[MarshalAs(UnmanagedType.I2)]
public short clsid_data3;
[MarshalAs(UnmanagedType.U1)]
public byte clsid_b0;
[MarshalAs(UnmanagedType.U1)]
public byte clsid_b1;
[MarshalAs(UnmanagedType.U1)]
public byte clsid_b2;
[MarshalAs(UnmanagedType.U1)]
public byte clsid_b3;
[MarshalAs(UnmanagedType.U1)]
public byte clsid_b4;
[MarshalAs(UnmanagedType.U1)]
public byte clsid_b5;
[MarshalAs(UnmanagedType.U1)]
public byte clsid_b6;
[MarshalAs(UnmanagedType.U1)]
public byte clsid_b7;
[MarshalAs(UnmanagedType.I4)]
public int grfStateBits;
[MarshalAs(UnmanagedType.I4)]
public int reserved;
}
}
}
@@ -0,0 +1,29 @@
using System;
namespace HandyControl.Data
{
internal class GifFrameDimension
{
public GifFrameDimension(Guid guid) => Guid = guid;
public Guid Guid { get; }
public static GifFrameDimension Time { get; } = new GifFrameDimension(new Guid("{6aedbd6d-3fb5-418a-83a6-7f45229dc872}"));
public static GifFrameDimension Resolution { get; } = new GifFrameDimension(new Guid("{84236f7b-3bd3-428f-8dab-4ea1439ca315}"));
public static GifFrameDimension Page { get; } = new GifFrameDimension(new Guid("{7462dc86-6180-4c7e-8e3f-ee7333a7a483}"));
public override bool Equals(object o) => o is GifFrameDimension format && Guid == format.Guid;
public override int GetHashCode() => Guid.GetHashCode();
public override string ToString()
{
if (Equals(this, Time)) return "Time";
if (Equals(this, Resolution)) return "Resolution";
if (Equals(this, Page)) return "Page";
return "[FrameDimension: " + Guid + "]";
}
}
}
@@ -0,0 +1,97 @@
using System;
using HandyControl.Controls;
namespace HandyControl.Data
{
public class GifImageInfo
{
private const int PropertyTagFrameDelay = 0x5100;
private int _frame;
private readonly int[] _frameDelay;
public int FrameCount { get; }
internal GifImage Image { get; }
public bool Animated { get; }
public EventHandler FrameChangedHandler { get; set; }
internal int FrameTimer { get; set; }
public bool FrameDirty { get; private set; }
public int Frame
{
get => _frame;
set
{
if (_frame != value)
{
if (value < 0 || value >= FrameCount)
{
throw new ArgumentException("InvalidFrame");
}
if (Animated)
{
_frame = value;
FrameDirty = true;
OnFrameChanged(EventArgs.Empty);
}
}
}
}
public GifImageInfo(GifImage image)
{
Image = image;
Animated = ImageAnimator.CanAnimate(image);
if (Animated)
{
FrameCount = image.GetFrameCount(GifFrameDimension.Time);
var frameDelayItem = image.GetPropertyItem(PropertyTagFrameDelay);
// If the image does not have a frame delay, we just return 0.
//
if (frameDelayItem != null)
{
// Convert the frame delay from byte[] to int
//
var values = frameDelayItem.Value;
_frameDelay = new int[FrameCount];
for (int i = 0; i < FrameCount; ++i)
{
_frameDelay[i] = values[i * 4] + 256 * values[i * 4 + 1] + 256 * 256 * values[i * 4 + 2] + 256 * 256 * 256 * values[i * 4 + 3];
}
}
}
else
{
FrameCount = 1;
}
if (_frameDelay == null)
{
_frameDelay = new int[FrameCount];
}
}
public int FrameDelay(int frame) => _frameDelay[frame];
internal void UpdateFrame()
{
if (FrameDirty)
{
Image.SelectActiveFrame(GifFrameDimension.Time, Frame);
FrameDirty = false;
}
}
protected void OnFrameChanged(EventArgs e) => FrameChangedHandler?.Invoke(Image, e);
}
}
@@ -0,0 +1,13 @@
namespace HandyControl.Data
{
public class GifPropertyItem
{
public int Id { get; set; }
public int Len { get; set; }
public short Type { get; set; }
public byte[] Value { get; set; }
}
}
@@ -0,0 +1,114 @@
using System;
using System.Runtime.InteropServices;
using HandyControl.Tools;
namespace HandyControl.Data
{
[StructLayout(LayoutKind.Sequential)]
public class GifPropertyItemInternal : IDisposable
{
public int id;
public int len;
public short type;
public IntPtr value = IntPtr.Zero;
internal GifPropertyItemInternal()
{
}
public byte[] Value
{
get
{
if (len == 0)
return null;
var bytes = new byte[len];
Marshal.Copy(value,
bytes,
0,
len);
return bytes;
}
}
public void Dispose()
{
Dispose(true);
}
~GifPropertyItemInternal()
{
Dispose(false);
}
private void Dispose(bool disposing)
{
if (value != IntPtr.Zero)
{
Marshal.FreeHGlobal(value);
value = IntPtr.Zero;
}
if (disposing)
GC.SuppressFinalize(this);
}
internal static GifPropertyItemInternal ConvertFromPropertyItem(GifPropertyItem propItem)
{
var propItemInternal = new GifPropertyItemInternal
{
id = propItem.Id,
len = 0,
type = propItem.Type
};
var propItemValue = propItem.Value;
if (propItemValue != null)
{
var length = propItemValue.Length;
propItemInternal.len = length;
propItemInternal.value = Marshal.AllocHGlobal(length);
Marshal.Copy(propItemValue, 0, propItemInternal.value, length);
}
return propItemInternal;
}
internal static GifPropertyItem[] ConvertFromMemory(IntPtr propdata, int count)
{
var props = new GifPropertyItem[count];
for (var i = 0; i < count; i++)
{
GifPropertyItemInternal propcopy = null;
try
{
propcopy = (GifPropertyItemInternal) ExternDllHelper.PtrToStructure(propdata,
typeof(GifPropertyItemInternal));
props[i] = new GifPropertyItem
{
Id = propcopy.id,
Len = propcopy.len,
Type = propcopy.type,
Value = propcopy.Value
};
// this calls Marshal.Copy and creates a copy of the original memory into a byte array.
propcopy.value = IntPtr.Zero; // we dont actually own this memory so dont free it.
}
finally
{
propcopy?.Dispose();
}
propdata = (IntPtr) ((long) propdata + Marshal.SizeOf(typeof(GifPropertyItemInternal)));
}
return props;
}
}
}
@@ -0,0 +1,31 @@
using System;
namespace HandyControl.Data
{
public class GrowlInfo
{
public string Message { get; set; }
public bool ShowDateTime { get; set; } = true;
public int WaitTime { get; set; } = 6;
public string CancelStr { get; set; } = Properties.Langs.Lang.Cancel;
public string ConfirmStr { get; set; } = Properties.Langs.Lang.Confirm;
public Func<bool, bool> ActionBeforeClose { get; set; }
public bool StaysOpen { get; set; }
public bool IsCustom { get; set; }
public InfoType Type { get; set; }
public string IconKey { get; set; }
public string IconBrushKey { get; set; }
public bool ShowCloseButton { get; set; } = true;
}
}
@@ -0,0 +1,23 @@
namespace HandyControl.Data
{
/// <summary>
/// 表示一个操作的返回结果信息
/// </summary>
public class OperationResult<T> : OperationResult
{
/// <summary>
/// 操作结果
/// </summary>
public ResultType ResultType { get; set; }
/// <summary>
/// 返回数据
/// </summary>
public T Data { get; set; }
/// <summary>
/// 操作消息(包含错误原因等数据)
/// </summary>
public string Message { get; set; }
}
}
@@ -0,0 +1,25 @@
namespace HandyControl.Data
{
public class OperationResult
{
public static OperationResult<bool> Failed(string message = "")
{
return new OperationResult<bool>
{
ResultType = ResultType.Failed,
Message = message,
Data = false
};
}
public static OperationResult<bool> Success(string message = "")
{
return new OperationResult<bool>
{
ResultType = ResultType.Success,
Message = message,
Data = true
};
}
}
}
@@ -0,0 +1,68 @@
namespace HandyControl.Data
{
internal class ResourceToken
{
public static readonly string SuccessGeometry = nameof(SuccessGeometry);
public static readonly string InfoGeometry = nameof(InfoGeometry);
public static readonly string ErrorGeometry = nameof(ErrorGeometry);
public static readonly string WarningGeometry = nameof(WarningGeometry);
public static readonly string AskGeometry = nameof(AskGeometry);
public static readonly string FatalGeometry = nameof(FatalGeometry);
public static readonly string PrimaryBrush = nameof(PrimaryBrush);
public static readonly string SuccessBrush = nameof(SuccessBrush);
public static readonly string InfoBrush = nameof(InfoBrush);
public static readonly string DangerBrush = nameof(DangerBrush);
public static readonly string WarningBrush = nameof(WarningBrush);
public static readonly string AccentBrush = nameof(AccentBrush);
public static readonly string PrimaryTextBrush = nameof(PrimaryTextBrush);
public static readonly string ThirdlyTextBrush = nameof(ThirdlyTextBrush);
public static readonly string BlurGradientValue = nameof(BlurGradientValue);
public static readonly string ButtonCustom = nameof(ButtonCustom);
public static readonly string PaginationButtonStyle = nameof(PaginationButtonStyle);
public static readonly string WindowWin10 = nameof(WindowWin10);
public static readonly string AddTagButtonStyle = nameof(AddTagButtonStyle);
public static readonly string RadioGroupItemDefault = nameof(RadioGroupItemDefault);
public static readonly string RadioGroupItemSingle = nameof(RadioGroupItemSingle);
public static readonly string RadioGroupItemHorizontalFirst = nameof(RadioGroupItemHorizontalFirst);
public static readonly string RadioGroupItemHorizontalLast = nameof(RadioGroupItemHorizontalLast);
public static readonly string RadioGroupItemVerticalFirst = nameof(RadioGroupItemVerticalFirst);
public static readonly string RadioGroupItemVerticalLast = nameof(RadioGroupItemVerticalLast);
public static readonly string TabItemCapsuleDefault = nameof(TabItemCapsuleDefault);
public static readonly string TabItemCapsuleSingle = nameof(TabItemCapsuleSingle);
public static readonly string TabItemCapsuleHorizontalFirst = nameof(TabItemCapsuleHorizontalFirst);
public static readonly string TabItemCapsuleHorizontalLast = nameof(TabItemCapsuleHorizontalLast);
public static readonly string TabItemCapsuleVerticalFirst = nameof(TabItemCapsuleVerticalFirst);
public static readonly string TabItemCapsuleVerticalLast = nameof(TabItemCapsuleVerticalLast);
}
}
@@ -0,0 +1,63 @@
<#@ template debug="false" hostspecific="false" language="C#" #>
<#@ import namespace="System.Collections.Generic"#>
<#@ output extension=".cs" #>
<#
var geometryList = new List<string>
{
"SuccessGeometry",
"InfoGeometry",
"ErrorGeometry",
"WarningGeometry",
"AskGeometry",
"FatalGeometry",
"PrimaryBrush"
};
var brushList = new List<string>
{
"SuccessBrush",
"InfoBrush",
"DangerBrush",
"WarningBrush",
"AccentBrush",
"PrimaryTextBrush",
"ThirdlyTextBrush",
"BlurGradientValue"
};
var styleList = new List<string>
{
"ButtonCustom",
"PaginationButtonStyle",
"WindowWin10",
"AddTagButtonStyle",
"RadioGroupItemDefault",
"RadioGroupItemSingle",
"RadioGroupItemHorizontalFirst",
"RadioGroupItemHorizontalLast",
"RadioGroupItemVerticalFirst",
"RadioGroupItemVerticalLast",
"TabItemCapsuleDefault",
"TabItemCapsuleSingle",
"TabItemCapsuleHorizontalFirst",
"TabItemCapsuleHorizontalLast",
"TabItemCapsuleVerticalFirst",
"TabItemCapsuleVerticalLast"
};
#>
namespace HandyControl.Data
{
internal class ResourceToken
{
<#foreach(var item in geometryList){#>
public static readonly string <#=item#> = nameof(<#=item#>);
<#}#>
<#foreach(var item in brushList){#>
public static readonly string <#=item#> = nameof(<#=item#>);
<#}#>
<#foreach(var item in styleList){#>
public static readonly string <#=item#> = nameof(<#=item#>);
<#}#>
}
}
@@ -0,0 +1,76 @@
// reference from https://github.com/sourcechord/FluentWPF/blob/master/FluentWPF/Utility/VersionInfo.cs
// LICENSE: https://github.com/sourcechord/FluentWPF/blob/master/LICENSE
using System;
namespace HandyControl.Data
{
public struct SystemVersionInfo
{
public static SystemVersionInfo Windows10 => new SystemVersionInfo(10, 0, 10240);
public static SystemVersionInfo Windows10_1809 => new SystemVersionInfo(10, 0, 17763);
public SystemVersionInfo(int major, int minor, int build)
{
Major = major;
Minor = minor;
Build = build;
}
public int Major { get; }
public int Minor { get; }
public int Build { get; }
public bool Equals(SystemVersionInfo other) => Major == other.Major && Minor == other.Minor && Build == other.Build;
public override bool Equals(object obj) => obj is SystemVersionInfo other && Equals(other);
public override int GetHashCode() => Major.GetHashCode() ^ Minor.GetHashCode() ^ Build.GetHashCode();
public static bool operator ==(SystemVersionInfo left, SystemVersionInfo right) => left.Equals(right);
public static bool operator !=(SystemVersionInfo left, SystemVersionInfo right) => !(left == right);
public int CompareTo(SystemVersionInfo other)
{
if (Major != other.Major)
{
return Major.CompareTo(other.Major);
}
if (Minor != other.Minor)
{
return Minor.CompareTo(other.Minor);
}
if (Build != other.Build)
{
return Build.CompareTo(other.Build);
}
return 0;
}
public int CompareTo(object obj)
{
if (!(obj is SystemVersionInfo other))
{
throw new ArgumentException();
}
return CompareTo(other);
}
public static bool operator <(SystemVersionInfo left, SystemVersionInfo right) => left.CompareTo(right) < 0;
public static bool operator <=(SystemVersionInfo left, SystemVersionInfo right) => left.CompareTo(right) <= 0;
public static bool operator >(SystemVersionInfo left, SystemVersionInfo right) => left.CompareTo(right) > 0;
public static bool operator >=(SystemVersionInfo left, SystemVersionInfo right) => left.CompareTo(right) >= 0;
public override string ToString() => $"{Major}.{Minor}.{Build}";
}
}
@@ -0,0 +1,36 @@
namespace HandyControl.Data
{
/// <summary>
/// 装箱后的值类型(用于提高效率)
/// </summary>
internal static class ValueBoxes
{
internal static object TrueBox = true;
internal static object FalseBox = false;
internal static object Double0Box = .0;
internal static object Double1Box = 1.0;
internal static object Double20Box = 20.0;
internal static object Double100Box = 100.0;
internal static object Double200Box = 200.0;
internal static object Double300Box = 300.0;
internal static object DoubleNeg1Box = -1.0;
internal static object Int0Box = 0;
internal static object Int1Box = 1;
internal static object Int2Box = 2;
internal static object Int5Box = 5;
internal static object BooleanBox(bool value) => value ? TrueBox : FalseBox;
}
}
@@ -0,0 +1,357 @@
using System;
using System.Collections.Generic;
using System.Windows;
namespace HandyControl.Expression.Drawing
{
internal static class BezierCurveFlattener
{
public const double StandardFlatteningTolerance = 0.25;
private static void DoCubicForwardDifferencing(Point[] controlPoints, double leftParameter, double rightParameter, double inverseErrorTolerance, ICollection<Point> resultPolyline, ICollection<double> resultParameters)
{
double num14;
var num = controlPoints[1].X - controlPoints[0].X;
var num2 = controlPoints[1].Y - controlPoints[0].Y;
var num3 = controlPoints[2].X - controlPoints[1].X;
var num4 = controlPoints[2].Y - controlPoints[1].Y;
var num5 = controlPoints[3].X - controlPoints[2].X;
var num6 = controlPoints[3].Y - controlPoints[2].Y;
var num7 = num3 - num;
var num8 = num4 - num2;
var num9 = num5 - num3;
var num10 = num6 - num4;
var num11 = num9 - num7;
var num12 = num10 - num8;
var vector = controlPoints[3].Subtract(controlPoints[0]);
var length = vector.Length;
if (!MathHelper.IsVerySmall(length))
num14 = Math.Max(0.0,
Math.Max(Math.Abs((num7 * vector.Y - num8 * vector.X) / length),
Math.Abs((num9 * vector.Y - num10 * vector.X) / length)));
else
num14 = Math.Max(0.0,
Math.Max(GeometryHelper.Distance(controlPoints[1], controlPoints[0]),
GeometryHelper.Distance(controlPoints[2], controlPoints[0])));
uint num15 = 0;
if (num14 > 0.0)
{
var d = num14 * inverseErrorTolerance;
num15 = d < 2147483647.0 ? Log4UnsignedInt32((uint) (d + 0.5)) : Log4Double(d);
}
var exp = (int) -num15;
var num18 = exp + exp;
var num19 = num18 + exp;
var num20 = MathHelper.DoubleFromMantissaAndExponent(3.0 * num7, num18);
var num21 = MathHelper.DoubleFromMantissaAndExponent(3.0 * num8, num18);
var num22 = MathHelper.DoubleFromMantissaAndExponent(6.0 * num11, num19);
var num23 = MathHelper.DoubleFromMantissaAndExponent(6.0 * num12, num19);
var num24 = MathHelper.DoubleFromMantissaAndExponent(3.0 * num, exp) + num20 +
0.16666666666666666 * num22;
var num25 = MathHelper.DoubleFromMantissaAndExponent(3.0 * num2, exp) + num21 +
0.16666666666666666 * num23;
var num26 = 2.0 * num20 + num22;
var num27 = 2.0 * num21 + num23;
var x = controlPoints[0].X;
var y = controlPoints[0].Y;
var item = new Point(0.0, 0.0);
var num30 = 1 << (int) num15;
var num31 = num30 > 0 ? (rightParameter - leftParameter) / num30 : 0.0;
var num32 = leftParameter;
for (var i = 1; i < num30; i++)
{
x += num24;
y += num25;
item.X = x;
item.Y = y;
resultPolyline.Add(item);
num32 += num31;
resultParameters?.Add(num32);
num24 += num26;
num25 += num27;
num26 += num22;
num27 += num23;
}
}
private static void DoCubicMidpointSubdivision(Point[] controlPoints, uint depth, double leftParameter, double rightParameter, double inverseErrorTolerance, ICollection<Point> resultPolyline, ICollection<double> resultParameters)
{
Point[] pointArray = { controlPoints[0], controlPoints[1], controlPoints[2], controlPoints[3] };
var pointArray2 = new Point[4];
pointArray2[3] = pointArray[3];
pointArray[3] = GeometryHelper.Midpoint(pointArray[3], pointArray[2]);
pointArray[2] = GeometryHelper.Midpoint(pointArray[2], pointArray[1]);
pointArray[1] = GeometryHelper.Midpoint(pointArray[1], pointArray[0]);
pointArray2[2] = pointArray[3];
pointArray[3] = GeometryHelper.Midpoint(pointArray[3], pointArray[2]);
pointArray[2] = GeometryHelper.Midpoint(pointArray[2], pointArray[1]);
pointArray2[1] = pointArray[3];
pointArray[3] = GeometryHelper.Midpoint(pointArray[3], pointArray[2]);
pointArray2[0] = pointArray[3];
depth--;
var num = (leftParameter + rightParameter) * 0.5;
if (depth > 0)
{
DoCubicMidpointSubdivision(pointArray, depth, leftParameter, num, inverseErrorTolerance,
resultPolyline, resultParameters);
resultPolyline.Add(pointArray2[0]);
resultParameters?.Add(num);
DoCubicMidpointSubdivision(pointArray2, depth, num, rightParameter, inverseErrorTolerance,
resultPolyline, resultParameters);
}
else
{
DoCubicForwardDifferencing(pointArray, leftParameter, num, inverseErrorTolerance,
resultPolyline, resultParameters);
resultPolyline.Add(pointArray2[0]);
resultParameters?.Add(num);
DoCubicForwardDifferencing(pointArray2, num, rightParameter, inverseErrorTolerance,
resultPolyline, resultParameters);
}
}
private static void EnsureErrorTolerance(ref double errorTolerance)
{
if (errorTolerance <= 0.0)
{
errorTolerance = 0.25;
}
}
public static void FlattenCubic(Point[] controlPoints, double errorTolerance, ICollection<Point> resultPolyline, bool skipFirstPoint, ICollection<double> resultParameters = null)
{
if (resultPolyline == null) throw new ArgumentNullException(nameof(resultPolyline));
if (controlPoints == null) throw new ArgumentNullException(nameof(controlPoints));
if (controlPoints.Length != 4) throw new ArgumentOutOfRangeException(nameof(controlPoints));
EnsureErrorTolerance(ref errorTolerance);
if (!skipFirstPoint)
{
resultPolyline.Add(controlPoints[0]);
resultParameters?.Add(0.0);
}
if (IsCubicChordMonotone(controlPoints, errorTolerance * errorTolerance))
{
var flattener = new AdaptiveForwardDifferencingCubicFlattener(controlPoints, errorTolerance,
errorTolerance, true);
var p = new Point();
var u = 0.0;
while (flattener.Next(ref p, ref u))
{
resultPolyline.Add(p);
resultParameters?.Add(u);
}
}
else
{
var x = controlPoints[3].X - controlPoints[2].X + controlPoints[1].X - controlPoints[0].X;
var y = controlPoints[3].Y - controlPoints[2].Y + controlPoints[1].Y - controlPoints[0].Y;
var num4 = 1.0 / errorTolerance;
var depth = Log8UnsignedInt32((uint) (MathHelper.Hypotenuse(x, y) * num4 + 0.5));
if (depth > 0) depth--;
if (depth > 0)
DoCubicMidpointSubdivision(controlPoints, depth, 0.0, 1.0, 0.75 * num4, resultPolyline,
resultParameters);
else
DoCubicForwardDifferencing(controlPoints, 0.0, 1.0, 0.75 * num4, resultPolyline,
resultParameters);
}
resultPolyline.Add(controlPoints[3]);
resultParameters?.Add(1.0);
}
public static void FlattenQuadratic(Point[] controlPoints, double errorTolerance, ICollection<Point> resultPolyline, bool skipFirstPoint, ICollection<double> resultParameters = null)
{
if (resultPolyline == null)
{
throw new ArgumentNullException(nameof(resultPolyline));
}
if (controlPoints == null)
{
throw new ArgumentNullException(nameof(controlPoints));
}
if (controlPoints.Length != 3)
{
throw new ArgumentOutOfRangeException(nameof(controlPoints));
}
EnsureErrorTolerance(ref errorTolerance);
Point[] pointArray = { controlPoints[0], GeometryHelper.Lerp(controlPoints[0], controlPoints[1], 0.66666666666666663), GeometryHelper.Lerp(controlPoints[1], controlPoints[2], 0.33333333333333331), controlPoints[2] };
FlattenCubic(pointArray, errorTolerance, resultPolyline, skipFirstPoint, resultParameters);
}
private static bool IsCubicChordMonotone(Point[] controlPoints, double squaredTolerance)
{
double num = GeometryHelper.SquaredDistance(controlPoints[0], controlPoints[3]);
if (num <= squaredTolerance)
{
return false;
}
Vector lhs = controlPoints[3].Subtract(controlPoints[0]);
Vector rhs = controlPoints[1].Subtract(controlPoints[0]);
double num2 = GeometryHelper.Dot(lhs, rhs);
if ((num2 < 0.0) || (num2 > num))
{
return false;
}
Vector vector3 = controlPoints[2].Subtract(controlPoints[0]);
double num3 = GeometryHelper.Dot(lhs, vector3);
if ((num3 < 0.0) || (num3 > num))
{
return false;
}
if (num2 > num3)
{
return false;
}
return true;
}
private static uint Log4Double(double d)
{
uint num = 0;
while (d > 1.0)
{
d *= 0.25;
num++;
}
return num;
}
private static uint Log4UnsignedInt32(uint i)
{
uint num = 0;
while (i > 0)
{
i = i >> 2;
num++;
}
return num;
}
private static uint Log8UnsignedInt32(uint i)
{
uint num = 0;
while (i > 0)
{
i = i >> 3;
num++;
}
return num;
}
private class AdaptiveForwardDifferencingCubicFlattener
{
private double _aX;
private double _aY;
private double _bX;
private double _bY;
private double _cX;
private double _cY;
private readonly double _distanceTolerance;
private readonly bool _doParameters;
private double _dParameter;
private double _dX;
private double _dY;
private readonly double _flatnessTolerance;
private int _numSteps;
private double _parameter;
internal AdaptiveForwardDifferencingCubicFlattener(Point[] controlPoints,
double flatnessTolerance, double distanceTolerance, bool doParameters)
{
_numSteps = 1;
_dParameter = 1.0;
_flatnessTolerance = 3.0 * flatnessTolerance;
_distanceTolerance = distanceTolerance;
_doParameters = doParameters;
_aX = -controlPoints[0].X + 3.0 * (controlPoints[1].X - controlPoints[2].X) +
controlPoints[3].X;
_aY = -controlPoints[0].Y + 3.0 * (controlPoints[1].Y - controlPoints[2].Y) +
controlPoints[3].Y;
_bX = 3.0 * (controlPoints[0].X - 2.0 * controlPoints[1].X + controlPoints[2].X);
_bY = 3.0 * (controlPoints[0].Y - 2.0 * controlPoints[1].Y + controlPoints[2].Y);
_cX = 3.0 * (-controlPoints[0].X + controlPoints[1].X);
_cY = 3.0 * (-controlPoints[0].Y + controlPoints[1].Y);
_dX = controlPoints[0].X;
_dY = controlPoints[0].Y;
}
private void DoubleStepSize()
{
_aX *= 8.0;
_aY *= 8.0;
_bX *= 4.0;
_bY *= 4.0;
_cX += _cX;
_cY += _cY;
if (_doParameters) _dParameter *= 2.0;
_numSteps = _numSteps >> 1;
}
private void HalveStepSize()
{
_aX *= 0.125;
_aY *= 0.125;
_bX *= 0.25;
_bY *= 0.25;
_cX *= 0.5;
_cY *= 0.5;
if (_doParameters) _dParameter *= 0.5;
_numSteps = _numSteps << 1;
}
private void IncrementDifferencesAndParameter()
{
_dX = _aX + _bX + _cX + _dX;
_dY = _aY + _bY + _cY + _dY;
_cX = _aX + _aX + _aX + _bX + _bX + _cX;
_cY = _aY + _aY + _aY + _bY + _bY + _cY;
_bX = _aX + _aX + _aX + _bX;
_bY = _aY + _aY + _aY + _bY;
_numSteps--;
_parameter += _dParameter;
}
private bool MustSubdivide(double flatnessTolerance)
{
var num = -(_aY + _bY + _cY);
var num2 = _aX + _bX + _cX;
var num3 = Math.Abs(num) + Math.Abs(num2);
if (num3 <= _distanceTolerance) return false;
num3 *= flatnessTolerance;
if (Math.Abs(_cX * num + _cY * num2) <= num3 &&
Math.Abs((_bX + _cX + _cX) * num + (_bY + _cY + _cY) * num2) <= num3) return false;
return true;
}
// ReSharper disable once RedundantAssignment
internal bool Next(ref Point p, ref double u)
{
while (MustSubdivide(_flatnessTolerance)) HalveStepSize();
if ((_numSteps & 1) == 0)
while (_numSteps > 1 && !MustSubdivide(_flatnessTolerance * 0.25))
DoubleStepSize();
IncrementDifferencesAndParameter();
p.X = _dX;
p.Y = _dY;
u = _parameter;
return _numSteps != 0;
}
}
}
}
Loaded 100 of 704 files, more files were not shown because too many files have changed in this diff. Show more