Adjust project structure

This commit is contained in:
NaBian committed 2019-04-05 22:23:19 +08:00
1 parent d38912f5da
commit e3c436f939
2111 files changed
+14028 -47199

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,50 @@
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using HandyControl.Data;
using HandyControl.Tools.Converter;
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);
public static readonly DependencyProperty CircularProperty = DependencyProperty.RegisterAttached(
"Circular", typeof(bool), typeof(BorderElement), new PropertyMetadata(ValueBoxes.FalseBox, OnCircularChanged));
private static void OnCircularChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is Border border)
{
if ((bool)e.NewValue)
{
var binding = new MultiBinding
{
Converter = new BorderCircularConverter()
};
binding.Bindings.Add(new Binding(FrameworkElement.ActualWidthProperty.Name) {Source = border});
binding.Bindings.Add(new Binding(FrameworkElement.ActualHeightProperty.Name) { Source = border });
border.SetBinding(Border.CornerRadiusProperty, binding);
}
else
{
BindingOperations.ClearBinding(border, FrameworkElement.MinWidthProperty);
BindingOperations.ClearBinding(border, FrameworkElement.MinHeightProperty);
BindingOperations.ClearBinding(border, Border.CornerRadiusProperty);
}
}
}
public static void SetCircular(DependencyObject element, bool value)
=> element.SetValue(CircularProperty, value);
public static bool GetCircular(DependencyObject element)
=> (bool) element.GetValue(CircularProperty);
}
}
@@ -0,0 +1,273 @@
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using HandyControl.Data;
namespace HandyControl.Controls
{
public class DataGridAttach
{
public static readonly DependencyProperty TextColumnStyleProperty = DependencyProperty.RegisterAttached(
"TextColumnStyle", typeof(Style), typeof(DataGridAttach), new PropertyMetadata(default(Style), OnTextColumnStyleChanged));
private static void OnTextColumnStyleChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var grid = (DataGrid)d;
if (e.OldValue == null && e.NewValue != null)
{
UpdateTextColumnStyles(grid);
}
}
public static void SetTextColumnStyle(DependencyObject element, Style value) => element.SetValue(TextColumnStyleProperty, value);
public static Style GetTextColumnStyle(DependencyObject element) => (Style)element.GetValue(TextColumnStyleProperty);
public static readonly DependencyProperty EditingTextColumnStyleProperty = DependencyProperty.RegisterAttached(
"EditingTextColumnStyle", typeof(Style), typeof(DataGridAttach), new PropertyMetadata(default(Style), OnTextColumnStyleChanged));
public static void SetEditingTextColumnStyle(DependencyObject element, Style value) => element.SetValue(EditingTextColumnStyleProperty, value);
public static Style GetEditingTextColumnStyle(DependencyObject element) => (Style) element.GetValue(EditingTextColumnStyleProperty);
private static void UpdateTextColumnStyles(DataGrid grid)
{
var textColumnStyle = GetTextColumnStyle(grid);
var editingTextColumnStyle = GetEditingTextColumnStyle(grid);
if (textColumnStyle != null)
{
foreach (var column in grid.Columns.OfType<DataGridTextColumn>())
{
var elementStyle = new Style
{
BasedOn = column.ElementStyle,
TargetType = textColumnStyle.TargetType
};
foreach (var setter in textColumnStyle.Setters.OfType<Setter>())
{
elementStyle.Setters.Add(setter);
}
column.ElementStyle = elementStyle;
}
}
if (editingTextColumnStyle != null)
{
foreach (var column in grid.Columns.OfType<DataGridTextColumn>())
{
var editingElementStyle = new Style
{
BasedOn = column.EditingElementStyle,
TargetType = editingTextColumnStyle.TargetType
};
foreach (var setter in editingTextColumnStyle.Setters.OfType<Setter>())
{
editingElementStyle.Setters.Add(setter);
}
column.EditingElementStyle = editingElementStyle;
}
}
}
public static readonly DependencyProperty ComboBoxColumnStyleProperty = DependencyProperty.RegisterAttached(
"ComboBoxColumnStyle", typeof(Style), typeof(DataGridAttach), new PropertyMetadata(default(Style), OnComboBoxColumnStyleChanged));
private static void OnComboBoxColumnStyleChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var grid = (DataGrid)d;
if (e.OldValue == null && e.NewValue != null)
{
UpdateComboBoxColumnStyles(grid);
}
}
public static void SetComboBoxColumnStyle(DependencyObject element, Style value) => element.SetValue(ComboBoxColumnStyleProperty, value);
public static Style GetComboBoxColumnStyle(DependencyObject element) => (Style) element.GetValue(ComboBoxColumnStyleProperty);
public static readonly DependencyProperty EditingComboBoxColumnStyleProperty = DependencyProperty.RegisterAttached(
"EditingComboBoxColumnStyle", typeof(Style), typeof(DataGridAttach), new PropertyMetadata(default(Style), OnComboBoxColumnStyleChanged));
public static void SetEditingComboBoxColumnStyle(DependencyObject element, Style value) => element.SetValue(EditingComboBoxColumnStyleProperty, value);
public static Style GetEditingComboBoxColumnStyle(DependencyObject element) => (Style) element.GetValue(EditingComboBoxColumnStyleProperty);
private static void UpdateComboBoxColumnStyles(DataGrid grid)
{
var comboBoxColumnStyle = GetComboBoxColumnStyle(grid);
var editingComboBoxColumnStyle = GetEditingComboBoxColumnStyle(grid);
if (comboBoxColumnStyle != null)
{
foreach (var column in grid.Columns.OfType<DataGridComboBoxColumn>())
{
var elementStyle = new Style
{
BasedOn = column.ElementStyle,
TargetType = comboBoxColumnStyle.TargetType
};
foreach (var setter in comboBoxColumnStyle.Setters.OfType<Setter>())
{
elementStyle.Setters.Add(setter);
}
column.ElementStyle = elementStyle;
}
}
if (editingComboBoxColumnStyle != null)
{
foreach (var column in grid.Columns.OfType<DataGridComboBoxColumn>())
{
var editingElementStyle = new Style
{
BasedOn = column.EditingElementStyle,
TargetType = editingComboBoxColumnStyle.TargetType
};
foreach (var setter in editingComboBoxColumnStyle.Setters.OfType<Setter>())
{
editingElementStyle.Setters.Add(setter);
}
column.EditingElementStyle = editingElementStyle;
}
}
}
public static readonly DependencyProperty CheckBoxColumnStyleProperty = DependencyProperty.RegisterAttached(
"CheckBoxColumnStyle", typeof(Style), typeof(DataGridAttach), new PropertyMetadata(default(Style), OnCheckBoxColumnStyleChanged));
private static void OnCheckBoxColumnStyleChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var grid = (DataGrid)d;
if (e.OldValue == null && e.NewValue != null)
{
UpdateCheckBoxColumnStyles(grid);
}
}
public static void SetCheckBoxColumnStyle(DependencyObject element, Style value) => element.SetValue(CheckBoxColumnStyleProperty, value);
public static Style GetCheckBoxColumnStyle(DependencyObject element) => (Style)element.GetValue(CheckBoxColumnStyleProperty);
public static readonly DependencyProperty EditingCheckBoxColumnStyleProperty = DependencyProperty.RegisterAttached(
"EditingCheckBoxColumnStyle", typeof(Style), typeof(DataGridAttach), new PropertyMetadata(default(Style), OnCheckBoxColumnStyleChanged));
public static void SetEditingCheckBoxColumnStyle(DependencyObject element, Style value) => element.SetValue(EditingCheckBoxColumnStyleProperty, value);
public static Style GetEditingCheckBoxColumnStyle(DependencyObject element) => (Style) element.GetValue(EditingCheckBoxColumnStyleProperty);
private static void UpdateCheckBoxColumnStyles(DataGrid grid)
{
var checkBoxColumnStyle = GetCheckBoxColumnStyle(grid);
var editingCheckBoxColumnStyle = GetEditingCheckBoxColumnStyle(grid);
if (checkBoxColumnStyle != null)
{
foreach (var column in grid.Columns.OfType<DataGridCheckBoxColumn>())
{
var elementStyle = new Style
{
BasedOn = column.ElementStyle,
TargetType = checkBoxColumnStyle.TargetType
};
foreach (var setter in checkBoxColumnStyle.Setters.OfType<Setter>())
{
elementStyle.Setters.Add(setter);
}
column.ElementStyle = elementStyle;
}
}
if (editingCheckBoxColumnStyle != null)
{
foreach (var column in grid.Columns.OfType<DataGridCheckBoxColumn>())
{
var editingElementStyle = new Style
{
BasedOn = column.EditingElementStyle,
TargetType = editingCheckBoxColumnStyle.TargetType
};
foreach (var setter in editingCheckBoxColumnStyle.Setters.OfType<Setter>())
{
editingElementStyle.Setters.Add(setter);
}
column.EditingElementStyle = editingElementStyle;
}
}
}
public static DependencyProperty ShowRowNumberProperty = DependencyProperty.RegisterAttached("ShowRowNumber",
typeof(bool), typeof(DataGridAttach),
new FrameworkPropertyMetadata(ValueBoxes.FalseBox, FrameworkPropertyMetadataOptions.Inherits, OnShowRowNumberChanged));
public static bool GetShowRowNumber(DependencyObject target) => (bool)target.GetValue(ShowRowNumberProperty);
public static void SetShowRowNumber(DependencyObject target, bool value) => target.SetValue(ShowRowNumberProperty, value);
private static void OnShowRowNumberChanged(DependencyObject target, DependencyPropertyChangedEventArgs e)
{
if (!(target is DataGrid dataGrid)) return;
var show = (bool)e.NewValue;
if (show)
{
dataGrid.LoadingRow += DataGrid_LoadingRow;
dataGrid.ItemContainerGenerator.ItemsChanged += ItemContainerGenerator_ItemsChanged;
}
else
{
dataGrid.LoadingRow -= DataGrid_LoadingRow;
dataGrid.ItemContainerGenerator.ItemsChanged -= ItemContainerGenerator_ItemsChanged;
}
UpdateItems(dataGrid.ItemContainerGenerator, dataGrid.Items.Count, show);
}
private static void ItemContainerGenerator_ItemsChanged(object sender, ItemsChangedEventArgs e)
{
if (!(sender is ItemContainerGenerator generator)) return;
UpdateItems(generator, e.ItemCount);
}
private static void UpdateItems(ItemContainerGenerator generator, int itemsCount, bool show = true)
{
if (show)
{
for (int i = 0; i < itemsCount; i++)
{
var row = (DataGridRow)generator.ContainerFromIndex(i);
if (row != null)
{
row.Header = (i + 1).ToString();
}
}
}
else
{
for (int i = 0; i < itemsCount; i++)
{
var row = (DataGridRow)generator.ContainerFromIndex(i);
if (row != null)
{
row.Header = null;
}
}
}
}
private static void DataGrid_LoadingRow(object sender, DataGridRowEventArgs e) => e.Row.Header = (e.Row.GetIndex() + 1).ToString();
}
}
@@ -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)
{
CreateTrigger(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 CreateTrigger(UIElement element, string link)
{
element.CommandBindings.Remove(GetCommandBinding(element));
element.SetCurrentValue(CommandBindingProperty, DependencyProperty.UnsetValue);
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,44 @@
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.ItemRemoved(oldBehavior);
panel.SetCurrentValue(TempFluidMoveBehaviorProperty, DependencyProperty.UnsetValue);
if (e.NewValue is FluidMoveBehavior behavior)
{
collection.ItemAdded(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,25 @@
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);
}
public static readonly DependencyProperty HeaderTemplateProperty = DependencyProperty.Register(
"HeaderTemplate", typeof(DataTemplate), typeof(HeaderedSelectableItem), new PropertyMetadata(default(DataTemplate)));
public DataTemplate HeaderTemplate
{
get => (DataTemplate) GetValue(HeaderTemplateProperty);
set => SetValue(HeaderTemplateProperty, value);
}
}
}
@@ -0,0 +1,7 @@
namespace HandyControl.Controls
{
public interface IGravatarGenerator
{
object GetGravatar(string id);
}
}
@@ -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,111 @@
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using HandyControl.Data;
namespace HandyControl.Controls
{
public class SelectableItem : ContentControl, 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)
{
if (SelfManage)
{
if (!IsSelected)
{
IsSelected = true;
OnSelected(new RoutedEventArgs(SelectedEvent, this));
}
else if (CanDeselect)
{
IsSelected = false;
OnSelected(new RoutedEventArgs(DeselectedEvent, this));
}
}
else
{
if (CanDeselect)
{
OnSelected(IsSelected
? new RoutedEventArgs(DeselectedEvent, this)
: new RoutedEventArgs(SelectedEvent, this));
}
else
{
OnSelected(new RoutedEventArgs(SelectedEvent, this));
}
}
_isMouseLeftButtonDown = false;
}
}
protected virtual void OnSelected(RoutedEventArgs e) => RaiseEvent(e);
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 DependencyProperty SelfManageProperty = DependencyProperty.Register(
"SelfManage", typeof(bool), typeof(SelectableItem), new PropertyMetadata(ValueBoxes.FalseBox));
public bool SelfManage
{
get => (bool) GetValue(SelfManageProperty);
set => SetValue(SelfManageProperty, value);
}
public static readonly DependencyProperty CanDeselectProperty = DependencyProperty.Register(
"CanDeselect", typeof(bool), typeof(SelectableItem), new PropertyMetadata(ValueBoxes.FalseBox));
public bool CanDeselect
{
get => (bool) GetValue(CanDeselectProperty);
set => SetValue(CanDeselectProperty, 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 RoutedEvent DeselectedEvent =
EventManager.RegisterRoutedEvent("Deselected", RoutingStrategy.Bubble,
typeof(RoutedEventHandler), typeof(SelectableItem));
public event RoutedEventHandler Deselected
{
add => AddHandler(DeselectedEvent, value);
remove => RemoveHandler(DeselectedEvent, value);
}
}
}
@@ -0,0 +1,151 @@
using System.Collections;
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 static readonly DependencyProperty ItemsSourceProperty = DependencyProperty.Register(
"ItemsSource", typeof(IEnumerable), typeof(SimpleItemsControl),
new PropertyMetadata(default(IEnumerable), OnItemsSourceChanged));
public SimpleItemsControl()
{
var items = new ObservableCollection<object>();
items.CollectionChanged += OnItemsChanged;
Items = items;
}
public IEnumerable ItemsSource
{
get => (IEnumerable) GetValue(ItemsSourceProperty);
set => SetValue(ItemsSourceProperty, value);
}
[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; }
private static void OnItemsSourceChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
((SimpleItemsControl) d).OnItemsSourceChanged((IEnumerable) e.OldValue, (IEnumerable) e.NewValue);
}
protected virtual void OnItemsSourceChanged(IEnumerable oldValue, IEnumerable newValue)
{
}
public override void OnApplyTemplate()
{
ItemsHost?.Children.Clear();
base.OnApplyTemplate();
ItemsHost = GetTemplateChild(ElementPanel) as Panel;
Refresh();
}
protected virtual void OnItemsChanged(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)
=> (d as SimpleItemsControl)?.OnItemTemplateChanged(e);
protected virtual void OnItemTemplateChanged(DependencyPropertyChangedEventArgs e) => Refresh();
private static void OnItemContainerStyleChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
=> (d as SimpleItemsControl)?.OnItemContainerStyleChanged(e);
protected virtual void OnItemContainerStyleChanged(DependencyPropertyChangedEventArgs e) => 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,27 @@
using System.Windows;
using System.Windows.Controls.Primitives;
using HandyControl.Data;
namespace HandyControl.Controls
{
public class ProgressButton : ToggleButton
{
public static readonly DependencyProperty ProgressStyleProperty = DependencyProperty.Register(
"ProgressStyle", typeof(Style), typeof(ProgressButton), new PropertyMetadata(default(Style)));
public Style ProgressStyle
{
get => (Style) GetValue(ProgressStyleProperty);
set => SetValue(ProgressStyleProperty, value);
}
public static readonly DependencyProperty ProgressProperty = DependencyProperty.Register(
"Progress", typeof(double), typeof(ProgressButton), new PropertyMetadata(ValueBoxes.Double0Box));
public double Progress
{
get => (double) GetValue(ProgressProperty);
set => SetValue(ProgressProperty, value);
}
}
}
@@ -0,0 +1,305 @@
using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Media.Media3D;
using HandyControl.Data;
namespace HandyControl.Controls
{
/// <summary>
/// 封面流
/// </summary>
[TemplatePart(Name = ElementViewport3D, Type = typeof(Viewport3D))]
[TemplatePart(Name = ElementCamera, Type = typeof(ProjectionCamera))]
[TemplatePart(Name = ElementVisualParent, Type = typeof(ModelVisual3D))]
public class CoverFlow : Control
{
private const string ElementViewport3D = "PART_Viewport3D";
private const string ElementCamera = "PART_Camera";
private const string ElementVisualParent = "PART_VisualParent";
/// <summary>
/// 最大显示数量的一半
/// </summary>
private const int MaxShowCountHalf = 7;
/// <summary>
/// 页码
/// </summary>
public static readonly DependencyProperty PageIndexProperty = DependencyProperty.Register(
"PageIndex", typeof(int), typeof(CoverFlow),
new PropertyMetadata(ValueBoxes.Int0Box, OnPageIndexChanged, CoercePageIndex));
private static object CoercePageIndex(DependencyObject d, object baseValue)
{
var ctl = (CoverFlow)d;
var v = (int)baseValue;
if (v < 0)
{
return 0;
}
if (v >= ctl._uriList.Count)
{
return ctl._uriList.Count - 1;
}
return v;
}
private static void OnPageIndexChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var ctl = (CoverFlow)d;
ctl.UpdateIndex((int)e.NewValue, (int)e.OldValue);
}
/// <summary>
/// 是否循环
/// </summary>
public static readonly DependencyProperty LoopProperty = DependencyProperty.Register(
"Loop", typeof(bool), typeof(CoverFlow), new PropertyMetadata(ValueBoxes.FalseBox));
/// <summary>
/// 存储所有的资源标识符
/// </summary>
private readonly Dictionary<int, Uri> _uriList = new Dictionary<int, Uri>();
/// <summary>
/// 当前在显示范围内的项
/// </summary>
private readonly Dictionary<int, CoverFlowItem> _itemShowList = new Dictionary<int, CoverFlowItem>();
/// <summary>
/// 相机
/// </summary>
private ProjectionCamera _camera;
/// <summary>
/// 3d画布
/// </summary>
private Viewport3D _viewport3D;
/// <summary>
/// 项容器
/// </summary>
private ModelVisual3D _visualParent;
/// <summary>
/// 显示范围内第一个项的编号
/// </summary>
private int _firstShowIndex;
/// <summary>
/// 显示范围内最后一个项的编号
/// </summary>
private int _lastShowIndex;
/// <summary>
/// 跳转编号
/// </summary>
private int _jumpToIndex = -1;
/// <summary>
/// 页码
/// </summary>
public int PageIndex
{
get => (int)GetValue(PageIndexProperty);
internal set => SetValue(PageIndexProperty, value);
}
/// <summary>
/// 是否循环
/// </summary>
public bool Loop
{
get => (bool)GetValue(LoopProperty);
set => SetValue(LoopProperty, value);
}
public override void OnApplyTemplate()
{
if (_viewport3D != null)
{
_viewport3D.Children.Clear();
_itemShowList.Clear();
_viewport3D.MouseLeftButtonDown -= Viewport3D_MouseLeftButtonDown;
}
base.OnApplyTemplate();
_viewport3D = GetTemplateChild(ElementViewport3D) as Viewport3D;
if (_viewport3D != null)
{
_viewport3D.MouseLeftButtonDown += Viewport3D_MouseLeftButtonDown;
}
_camera = GetTemplateChild(ElementCamera) as ProjectionCamera;
_visualParent = GetTemplateChild(ElementVisualParent) as ModelVisual3D;
UpdateShowRange();
if (_jumpToIndex > 0)
{
PageIndex = _jumpToIndex;
_jumpToIndex = -1;
}
_camera.Position = new Point3D(CoverFlowItem.Interval * PageIndex, _camera.Position.Y, _camera.Position.Z);
}
/// <summary>
/// 批量添加资源
/// </summary>
/// <param name="uriList"></param>
public void AddRange(List<Uri> uriList)
{
foreach (var uri in uriList)
{
_uriList.Add(_uriList.Count, uri);
}
}
/// <summary>
/// 添加一项资源
/// </summary>
/// <param name="uriString"></param>
public void Add(string uriString) => _uriList.Add(_uriList.Count, new Uri(uriString));
/// <summary>
/// 添加一项资源
/// </summary>
/// <param name="uri"></param>
public void Add(Uri uri) => _uriList.Add(_uriList.Count, uri);
/// <summary>
/// 跳转
/// </summary>
public void JumpTo(int index) => _jumpToIndex = index;
protected override void OnMouseWheel(MouseWheelEventArgs e)
{
base.OnMouseWheel(e);
if (e.Delta < 0)
{
var index = PageIndex + 1;
PageIndex = index >= _uriList.Count ? (Loop ? 0 : _uriList.Count - 1) : index;
}
else
{
var index = PageIndex - 1;
PageIndex = index < 0 ? (Loop ? _uriList.Count - 1 : 0) : index;
}
}
/// <summary>
/// 删除指定位置的项
/// </summary>
/// <param name="pos"></param>
private void Remove(int pos)
{
var item = _itemShowList[pos];
_visualParent.Children.Remove(item);
_itemShowList.Remove(pos);
}
/// <summary>
/// 移动项到指定的位置
/// </summary>
/// <param name="index"></param>
/// <param name="animated"></param>
private void Move(int index, bool animated) => _itemShowList[index].Move(PageIndex, animated);
private void Viewport3D_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
var result = (RayMeshGeometry3DHitTestResult)VisualTreeHelper.HitTest(_viewport3D, e.GetPosition(_viewport3D));
if (result != null)
{
foreach (var item in _itemShowList.Values)
{
if (item.HitTest(result.MeshHit))
{
PageIndex = item.Index;
break;
}
}
}
}
/// <summary>
/// 更新项的位置
/// </summary>
/// <param name="newIndex"></param>
/// <param name="oldIndex"></param>
private void UpdateIndex(int newIndex, int oldIndex)
{
var animate = Math.Abs(newIndex - oldIndex) < MaxShowCountHalf;
UpdateShowRange();
if (newIndex > oldIndex)
{
if (oldIndex < _firstShowIndex)
{
oldIndex = _firstShowIndex;
}
for (var i = oldIndex; i <= newIndex; i++)
{
Move(i, animate);
}
}
else
{
if (oldIndex > _lastShowIndex)
{
oldIndex = _lastShowIndex;
}
for (var i = oldIndex; i >= newIndex; i--)
{
Move(i, animate);
}
}
_camera.Position = new Point3D(CoverFlowItem.Interval * newIndex, _camera.Position.Y,
_camera.Position.Z);
}
/// <summary>
/// 更新显示范围
/// </summary>
private void UpdateShowRange()
{
var newFirstShowIndex = Math.Max(PageIndex - MaxShowCountHalf, 0);
var newLastShowIndex = Math.Min(PageIndex + MaxShowCountHalf, _uriList.Count - 1);
if (_firstShowIndex < newFirstShowIndex)
{
for (var i = _firstShowIndex; i < newFirstShowIndex; i++)
{
Remove(i);
}
}
else if (newLastShowIndex < _lastShowIndex)
{
for (var i = newLastShowIndex; i < _lastShowIndex; i++)
{
Remove(i);
}
}
for (var i = newFirstShowIndex; i <= newLastShowIndex; i++)
{
if (!_itemShowList.ContainsKey(i))
{
var cover = new CoverFlowItem(i, PageIndex, BitmapFrame.Create(_uriList[i]));
_itemShowList[i] = cover;
_visualParent.Children.Add(cover);
}
}
_firstShowIndex = newFirstShowIndex;
_lastShowIndex = newLastShowIndex;
}
}
}
@@ -0,0 +1,165 @@
using System;
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Media3D;
using HandyControl.Tools;
namespace HandyControl.Controls
{
public class CoverFlowItem : ModelVisual3D
{
internal static readonly double Interval = .2;
internal static readonly double AnimationSpeed = 400;
private readonly AxisAngleRotation3D _rotation3D;
private readonly TranslateTransform3D _transform3D;
private readonly Model3DGroup _model3DGroup;
private readonly ImageSource _source;
internal int Index { get; set; }
public CoverFlowItem(int itemIndex, int currentIndex, ImageSource source)
{
Index = itemIndex;
_source = source;
_rotation3D = new AxisAngleRotation3D(new Vector3D(0, 1, 0), GetAngleByPos(currentIndex));
_transform3D = new TranslateTransform3D(GetXByPos(currentIndex), 0, GetZByPos(currentIndex));
var transformGroup = new Transform3DGroup();
transformGroup.Children.Add(new RotateTransform3D(_rotation3D));
transformGroup.Children.Add(_transform3D);
_model3DGroup = new Model3DGroup();
_model3DGroup.Children.Add(new GeometryModel3D(CreateItemGeometry(), new DiffuseMaterial(new ImageBrush(_source))));
_model3DGroup.Transform = transformGroup;
Content = _model3DGroup;
}
/// <summary>
/// 创建网格形状
/// </summary>
/// <param name="p0"></param>
/// <param name="p1"></param>
/// <param name="p2"></param>
/// <param name="p3"></param>
/// <returns></returns>
private static MeshGeometry3D CreateMeshGeometry(Point3D p0, Point3D p1, Point3D p2, Point3D p3)
{
var positions = new Point3DCollection
{
p0,
p1,
p2,
p3
};
var triangleIndices = new Int32Collection
{
0,
1,
2,
2,
3,
0
};
var textureCoordinates = new PointCollection
{
new Point(0, 1),
new Point(1, 1),
new Point(1, 0),
new Point(0, 0),
new Point(1, 0),
new Point(1, 1)
};
var normals = new Vector3DCollection
{
ArithmeticHelper.CalNormal(p0, p1, p2),
ArithmeticHelper.CalNormal(p2, p3, p0)
};
var geometry3D = new MeshGeometry3D
{
Positions = positions,
TriangleIndices = triangleIndices,
TextureCoordinates = textureCoordinates,
Normals = normals
};
geometry3D.Freeze();
return geometry3D;
}
/// <summary>
/// 创建内容形状
/// </summary>
/// <returns></returns>
private Geometry3D CreateItemGeometry()
{
var sx = _source.Width > _source.Height ? 0 : 1 - _source.Width / _source.Height;
var sy = _source.Width < _source.Height ? 0 : 1 - _source.Height / _source.Width;
var p0 = new Point3D(-1 + sx, -1 + sy, 0);
var p1 = new Point3D(1 - sx, -1 + sy, 0);
var p2 = new Point3D(1 - sx, 1 - sy, 0);
var p3 = new Point3D(-1 + sx, 1 - sy, 0);
return CreateMeshGeometry(p0, p1, p2, p3);
}
private double GetAngleByPos(int index) => Math.Sign(Index - index) * -90;
private double GetXByPos(int index) => Index * Interval + Math.Sign(Index - index) * 1.5;
private double GetZByPos(int index) => Index == index ? 1 : 0;
/// <summary>
/// 移动
/// </summary>
internal void Move(int currentIndex, bool animated)
{
if (animated || _rotation3D.HasAnimatedProperties)
{
_rotation3D.BeginAnimation(AxisAngleRotation3D.AngleProperty,
AnimationHelper.CreateAnimation(GetAngleByPos(currentIndex), AnimationSpeed));
_transform3D.BeginAnimation(TranslateTransform3D.OffsetXProperty,
AnimationHelper.CreateAnimation(GetXByPos(currentIndex), AnimationSpeed));
_transform3D.BeginAnimation(TranslateTransform3D.OffsetZProperty,
AnimationHelper.CreateAnimation(GetZByPos(currentIndex), AnimationSpeed));
}
else
{
_rotation3D.Angle = GetAngleByPos(currentIndex);
_transform3D.OffsetX = GetXByPos(currentIndex);
_transform3D.OffsetZ = GetZByPos(currentIndex);
}
}
/// <summary>
/// 命中测试
/// </summary>
/// <param name="mesh"></param>
/// <returns></returns>
internal bool HitTest(MeshGeometry3D mesh)
{
foreach (var item in _model3DGroup.Children)
{
if (item is GeometryModel3D geometryModel3D)
{
if (Equals(geometryModel3D.Geometry, mesh))
{
return true;
}
}
}
return false;
}
}
}
@@ -0,0 +1,426 @@
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Windows;
using System.Windows.Data;
using HandyControl.Data;
using HandyControl.Tools;
namespace HandyControl.Controls
{
public class CoverView : RegularItemsControl
{
private readonly CoverViewContent _viewContent;
private CoverViewItem _selectedItem;
private IEnumerable _itemsSourceInternal;
private readonly Dictionary<object, CoverViewItem> _entryDic = new Dictionary<object, CoverViewItem>();
private bool _isRefresh;
public CoverView()
{
_viewContent = new CoverViewContent();
AddHandler(SelectableItem.SelectedEvent, new RoutedEventHandler(CoverViewItem_OnSelected));
_viewContent.SetBinding(CoverViewContent.ContentHeightProperty, new Binding(ItemContentHeightProperty.Name) { Source = this });
_viewContent.SetBinding(WidthProperty, new Binding(ActualWidthProperty.Name) { Source = this });
}
private void CoverViewItem_OnSelected(object sender, RoutedEventArgs e)
{
if (e.OriginalSource is CoverViewItem item)
{
if (_selectedItem == null)
{
item.IsSelected = true;
_selectedItem = item;
if (_viewContent != null)
{
_viewContent.Content = item.Content;
_viewContent.ContentTemplate = ItemTemplate;
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;
_viewContent.ContentTemplate = 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();
private void SetBindingForItem(FrameworkElement item)
{
item.SetBinding(MarginProperty, new Binding(ItemMarginProperty.Name) { Source = this });
item.SetBinding(WidthProperty, new Binding(ItemWidthProperty.Name) { Source = this });
item.SetBinding(HeightProperty, new Binding(ItemHeightProperty.Name) { Source = this });
item.SetBinding(HeaderedSelectableItem.HeaderTemplateProperty, new Binding(ItemHeaderTemplateProperty.Name) { Source = this });
}
protected override bool IsItemItsOwnContainerOverride(object item) => item is CoverViewItem;
public static readonly DependencyProperty CoverViewContentStyleProperty = DependencyProperty.Register(
"CoverViewContentStyle", typeof(Style), typeof(CoverView), new PropertyMetadata(default(Style)));
public Style CoverViewContentStyle
{
get => (Style)GetValue(CoverViewContentStyleProperty);
set => SetValue(CoverViewContentStyleProperty, value);
}
internal static readonly DependencyProperty GroupsProperty = DependencyProperty.Register(
"Groups", typeof(int), typeof(CoverView),
new FrameworkPropertyMetadata(ValueBoxes.Int5Box, FrameworkPropertyMetadataOptions.AffectsMeasure,
OnGroupsChanged), IsGroupsValid);
internal static void OnGroupsChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is CoverView ctl)
{
ctl.UpdateCoverViewContent(ctl._viewContent.IsOpen);
}
}
private static bool IsGroupsValid(object value)
{
var v = (int)value;
return v >= 1;
}
public int Groups
{
get => (int)GetValue(GroupsProperty);
set => SetValue(GroupsProperty, value);
}
public static readonly DependencyProperty ItemContentHeightProperty = DependencyProperty.Register(
"ItemContentHeight", typeof(double), typeof(CoverView), new PropertyMetadata(ValueBoxes.Double300Box),
value => ValidateHelper.IsInRangeOfPosDouble(value, true));
public double ItemContentHeight
{
get => (double)GetValue(ItemContentHeightProperty);
set => SetValue(ItemContentHeightProperty, value);
}
public static readonly DependencyProperty ItemContentHeightFixedProperty = DependencyProperty.Register(
"ItemContentHeightFixed", typeof(bool), typeof(CoverView), new PropertyMetadata(ValueBoxes.TrueBox));
public bool ItemContentHeightFixed
{
get => (bool)GetValue(ItemContentHeightFixedProperty);
set => SetValue(ItemContentHeightFixedProperty, value);
}
public static readonly DependencyProperty ItemHeaderTemplateProperty = DependencyProperty.Register(
"ItemHeaderTemplate", typeof(DataTemplate), typeof(CoverView), new PropertyMetadata(default(DataTemplate)));
public DataTemplate ItemHeaderTemplate
{
get => (DataTemplate) GetValue(ItemHeaderTemplateProperty);
set => SetValue(ItemHeaderTemplateProperty, 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;
_entryDic.Clear();
_isRefresh = true;
foreach (var item in Items)
{
AddItem(item);
}
_isRefresh = false;
GenerateIndex();
UpdateCoverViewContent(_viewContent.IsOpen);
}
/// <summary>
/// 更新内容视图
/// </summary>
private void UpdateCoverViewContent(bool isOpen)
{
if (_selectedItem == null) return;
ItemsHost.Children.Remove(_viewContent);
if (!ItemContentHeightFixed)
{
_viewContent.ManualHeight = 0;
if (_viewContent.Content is FrameworkElement element)
{
element.VerticalAlignment = VerticalAlignment.Top;
element.Arrange(new Rect(new Size(double.MaxValue, double.MaxValue)));
_viewContent.ManualHeight = element.ActualHeight;
}
}
UpdateCoverViewContentPosition();
if (_viewContent.CanSwitch)
{
_viewContent.IsOpen = isOpen;
}
}
/// <summary>
/// 更新内容视图位置
/// </summary>
private void UpdateCoverViewContentPosition()
{
if (_viewContent.Parent != null)
{
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);
}
protected override void OnItemsSourceChanged(IEnumerable oldValue, IEnumerable newValue)
{
if (_itemsSourceInternal != null)
{
if (_itemsSourceInternal is INotifyCollectionChanged s)
{
s.CollectionChanged -= InternalCollectionChanged;
}
Items.Clear();
ClearItems();
}
_itemsSourceInternal = newValue;
if (_itemsSourceInternal != null)
{
if (_itemsSourceInternal is INotifyCollectionChanged s)
{
s.CollectionChanged += InternalCollectionChanged;
}
foreach (var item in _itemsSourceInternal)
{
AddItem(item);
}
}
if (ItemsHost != null)
{
GenerateIndex();
}
}
private void ClearItems()
{
_selectedItem = null;
_viewContent.Content = null;
_viewContent.ContentTemplate = null;
if (ItemsHost != null)
{
ItemsHost.Children.Remove(_viewContent);
ItemsHost.Children.Clear();
}
_entryDic.Clear();
}
private void RemoveItem(object item)
{
if (_entryDic.TryGetValue(item, out var entry))
{
if (ReferenceEquals(entry, _selectedItem))
{
_selectedItem = null;
if (_viewContent != null)
{
_viewContent.Content = null;
_viewContent.IsOpen = false;
ItemsHost.Children.Remove(_viewContent);
}
}
ItemsHost.Children.Remove(entry);
Items.Remove(item);
_entryDic.Remove(item);
}
}
private void AddItem(object item) => InsertItem(_entryDic.Count, item);
private void InsertItem(int index, object item)
{
if (ItemsHost == null)
{
Items.Insert(index, item);
_entryDic.Add(item, null);
}
else
{
DependencyObject container;
if (IsItemItsOwnContainerOverride(item))
{
container = item as DependencyObject;
}
else
{
container = GetContainerForItemOverride();
PrepareContainerForItemOverride(container, item);
}
if (container is CoverViewItem element)
{
SetBindingForItem(element);
element.Style = ItemContainerStyle;
if (element.Header == null)
{
element.Header = item;
}
_entryDic[item] = element;
ItemsHost.Children.Insert(index, element);
if (IsLoaded && !_isRefresh && _itemsSourceInternal != null)
{
Items.Insert(index, item);
}
}
}
}
/// <summary>
/// 生成序号
/// </summary>
private void GenerateIndex()
{
var index = 0;
foreach (var item in Items)
{
_entryDic[item].Index = index++;
}
}
private void InternalCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
if (ItemsHost == null) return;
if (e.Action == NotifyCollectionChangedAction.Reset)
{
if (_entryDic.Count == 0) return;
ClearItems();
Items.Clear();
return;
}
if (e.OldItems != null)
{
foreach (var item in e.OldItems)
{
RemoveItem(item);
}
}
if (e.NewItems != null)
{
if (_viewContent.IsOpen)
{
ItemsHost.Children.Remove(_viewContent);
}
var count = 0;
foreach (var item in e.NewItems)
{
var index = e.NewStartingIndex + count++;
InsertItem(index, item);
}
}
GenerateIndex();
if (_viewContent.IsOpen)
{
UpdateCoverViewContentPosition();
}
}
protected override void OnItemsChanged(object sender, NotifyCollectionChangedEventArgs e)
{
if (_itemsSourceInternal != null) return;
InternalCollectionChanged(sender, e);
}
protected override void OnItemTemplateChanged(DependencyPropertyChangedEventArgs e)
{
}
protected override void OnItemContainerStyleChanged(DependencyPropertyChangedEventArgs e)
{
}
}
}
@@ -0,0 +1,122 @@
using System.Windows;
using System.Windows.Controls;
using HandyControl.Data;
using HandyControl.Expression.Drawing;
using HandyControl.Tools;
using HandyControl.Tools.Extension;
namespace HandyControl.Controls
{
[TemplatePart(Name = ElementTriangle, Type = typeof(FrameworkElement))]
[TemplatePart(Name = ElementContent, Type = typeof(FrameworkElement))]
internal class CoverViewContent : ContentControl
{
private const string ElementTriangle = "PART_Triangle";
private const string ElementContent = "PART_Content";
private FrameworkElement _triangle;
private FrameworkElement _content;
internal bool WaitForUpdate { get; set; }
private int _index;
private int _groups;
private double _itemWidth;
internal bool CanSwitch { get; set; } = true;
private bool _isOpen;
internal bool IsOpen
{
get => _isOpen;
set
{
if (_isOpen == value) return;
_isOpen = value;
OpenSwitch(value);
}
}
internal static readonly DependencyProperty ManualHeightProperty = DependencyProperty.Register(
"ManualHeight", typeof(double), typeof(CoverViewContent), new PropertyMetadata(default(double)), value => ValidateHelper.IsInRangeOfPosDouble(value, true));
internal double ManualHeight
{
get => (double)GetValue(ManualHeightProperty);
set => SetValue(ManualHeightProperty, value);
}
public static readonly DependencyProperty ContentHeightProperty = DependencyProperty.Register(
"ContentHeight", typeof(double), typeof(CoverViewContent), new PropertyMetadata(ValueBoxes.Double300Box),
value => ValidateHelper.IsInRangeOfPosDouble(value, true));
public double ContentHeight
{
get => (double)GetValue(ContentHeightProperty);
set => SetValue(ContentHeightProperty, value);
}
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
_triangle = GetTemplateChild(ElementTriangle) as FrameworkElement;
_content = GetTemplateChild(ElementContent) as FrameworkElement;
if (WaitForUpdate)
{
_triangle.BeginAnimation(MarginProperty, AnimationHelper.CreateAnimation(new Thickness((_index % _groups + .5) * _itemWidth - _triangle.Width / 2, 0, 0, 0)));
OpenSwitch(_isOpen);
WaitForUpdate = false;
}
}
internal void UpdatePosition(int index, int groups, double itemWidth)
{
if (_triangle == null)
{
_index = index;
_groups = groups;
_itemWidth = itemWidth;
WaitForUpdate = true;
return;
}
_triangle.BeginAnimation(MarginProperty, AnimationHelper.CreateAnimation(new Thickness((index % groups + .5) * itemWidth - _triangle.Width / 2, 0, 0, 0)));
if (IsOpen)
{
if (ManualHeight > 0 && !MathHelper.AreClose(ManualHeight, ContentHeight))
{
_content.BeginAnimation(HeightProperty, AnimationHelper.CreateAnimation(ManualHeight));
}
else
{
_content.BeginAnimation(HeightProperty, AnimationHelper.CreateAnimation(ContentHeight));
}
}
}
private void OpenSwitch(bool isOpen)
{
if (_content == null) return;
var animation = AnimationHelper.CreateAnimation(isOpen ? ManualHeight > 0 ? ManualHeight : ContentHeight : 0);
_triangle.Show(false);
this.Show(true);
animation.Completed += (s, e) =>
{
CanSwitch = true;
this.Show(IsOpen);
if (IsOpen)
{
_triangle.Show(true);
}
};
CanSwitch = false;
_content.BeginAnimation(HeightProperty, animation);
}
}
}
@@ -0,0 +1,7 @@
namespace HandyControl.Controls
{
public class CoverViewItem : HeaderedSelectableItem
{
internal int Index { get; set; }
}
}
@@ -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 = ElementPanelMain, Type = typeof(Panel))]
[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 ElementPanelMain = "PART_PanelMain";
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 Panel _panelMain;
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();
_panelMain = GetTemplateChild(ElementPanelMain) as Panel;
_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(_panelMain);
_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(_panelMain);
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(_panelMain);
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,113 @@
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Input;
using HandyControl.Data;
using HandyControl.Interactivity;
namespace HandyControl.Controls
{
/// <inheritdoc cref="IDataInput" />
[TemplatePart(Name = ElementTextBox, Type = typeof(DatePickerTextBox))]
public class DatePicker : System.Windows.Controls.DatePicker, IDataInput
{
private const string ElementTextBox = "PART_TextBox";
private System.Windows.Controls.TextBox _textBox;
public DatePicker()
{
CommandBindings.Add(new CommandBinding(ControlCommands.Clear, (s, e) =>
{
ClearValue(SelectedDateProperty);
ClearValue(TextProperty);
}));
}
public override void OnApplyTemplate()
{
if (_textBox != null)
{
_textBox.TextChanged -= TextBox_TextChanged;
}
base.OnApplyTemplate();
_textBox = GetTemplateChild(ElementTextBox) as System.Windows.Controls.TextBox;
if (_textBox != null)
{
_textBox.TextChanged += TextBox_TextChanged;
}
}
private void TextBox_TextChanged(object sender, TextChangedEventArgs e) => VerifyData();
public Func<string, OperationResult<bool>> VerifyFunc { get; set; }
public static readonly DependencyProperty IsErrorProperty = DependencyProperty.Register(
"IsError", typeof(bool), typeof(DatePicker), new PropertyMetadata(ValueBoxes.FalseBox));
public bool IsError
{
get => (bool) GetValue(IsErrorProperty);
set => SetValue(IsErrorProperty, value);
}
public static readonly DependencyProperty ErrorStrProperty = DependencyProperty.Register(
"ErrorStr", typeof(string), typeof(DatePicker), new PropertyMetadata(default(string)));
public string ErrorStr
{
get => (string) GetValue(ErrorStrProperty);
set => SetValue(ErrorStrProperty, value);
}
public static readonly DependencyProperty TextTypeProperty = DependencyProperty.Register(
"TextType", typeof(TextType), typeof(DatePicker), new PropertyMetadata(default(TextType)));
public TextType TextType
{
get => (TextType) GetValue(TextTypeProperty);
set => SetValue(TextTypeProperty, value);
}
public static readonly DependencyProperty ShowClearButtonProperty = DependencyProperty.Register(
"ShowClearButton", typeof(bool), typeof(DatePicker), 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,690 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Input;
using System.Windows.Threading;
using HandyControl.Data;
using HandyControl.Interactivity;
namespace HandyControl.Controls
{
/// <summary>
/// 时间日期选择器
/// </summary>
[TemplatePart(Name = ElementRoot, Type = typeof(Grid))]
[TemplatePart(Name = ElementTextBox, Type = typeof(WatermarkTextBox))]
[TemplatePart(Name = ElementButton, Type = typeof(Button))]
[TemplatePart(Name = ElementPopup, Type = typeof(Popup))]
public class DateTimePicker : Control, IDataInput
{
#region Constants
private const string ElementRoot = "PART_Root";
private const string ElementTextBox = "PART_TextBox";
private const string ElementButton = "PART_Button";
private const string ElementPopup = "PART_Popup";
#endregion Constants
#region Data
private CalendarWithClock _calendarWithClock;
private string _defaultText;
private ButtonBase _dropDownButton;
private Popup _popUp;
private bool _disablePopupReopen;
private WatermarkTextBox _textBox;
private IDictionary<DependencyProperty, bool> _isHandlerSuspended;
private DateTime? _originalSelectedDateTime;
#endregion Data
#region Public Events
public static readonly RoutedEvent SelectedDateTimeChangedEvent =
EventManager.RegisterRoutedEvent("SelectedDateTimeChanged", RoutingStrategy.Direct,
typeof(EventHandler<FunctionEventArgs<DateTime?>>), typeof(DateTimePicker));
public event EventHandler<FunctionEventArgs<DateTime?>> SelectedDateTimeChanged
{
add => AddHandler(SelectedDateTimeChangedEvent, value);
remove => RemoveHandler(SelectedDateTimeChangedEvent, value);
}
public event RoutedEventHandler PickerClosed;
public event RoutedEventHandler PickerOpened;
#endregion Public Events
static DateTimePicker()
{
EventManager.RegisterClassHandler(typeof(DateTimePicker), GotFocusEvent, new RoutedEventHandler(OnGotFocus));
KeyboardNavigation.TabNavigationProperty.OverrideMetadata(typeof(DateTimePicker), new FrameworkPropertyMetadata(KeyboardNavigationMode.Once));
KeyboardNavigation.IsTabStopProperty.OverrideMetadata(typeof(DateTimePicker), new FrameworkPropertyMetadata(false));
}
public DateTimePicker()
{
InitCalendarWithClock();
CommandBindings.Add(new CommandBinding(ControlCommands.Clear, (s, e) =>
{
ClearValue(SelectedDateTimeProperty);
ClearValue(TextProperty);
_textBox.Text = string.Empty;
}));
}
#region Public Properties
public static readonly DependencyProperty DateTimeFormatProperty = DependencyProperty.Register(
"DateTimeFormat", typeof(string), typeof(DateTimePicker), new PropertyMetadata("yyyy-MM-dd HH:mm:ss"));
public string DateTimeFormat
{
get => (string) GetValue(DateTimeFormatProperty);
set => SetValue(DateTimeFormatProperty, value);
}
public static readonly DependencyProperty ClockStyleProperty = DependencyProperty.Register(
"ClockStyle", typeof(Style), typeof(DateTimePicker), new PropertyMetadata(default(Style)));
public Style ClockStyle
{
get => (Style) GetValue(ClockStyleProperty);
set => SetValue(ClockStyleProperty, value);
}
public static readonly DependencyProperty CalendarStyleProperty = DependencyProperty.Register(
"CalendarStyle", typeof(Style), typeof(DateTimePicker), new PropertyMetadata(default(Style)));
public Style CalendarStyle
{
get => (Style) GetValue(CalendarStyleProperty);
set => SetValue(CalendarStyleProperty, value);
}
public static readonly DependencyProperty DisplayDateTimeProperty = DependencyProperty.Register(
"DisplayDateTime", typeof(DateTime), typeof(DateTimePicker), new FrameworkPropertyMetadata(DateTime.Now, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, null, CoerceDisplayDateTime));
private static object CoerceDisplayDateTime(DependencyObject d, object value)
{
var dp = (DateTimePicker)d;
dp._calendarWithClock.DisplayDateTime = (DateTime)value;
return dp._calendarWithClock.DisplayDateTime;
}
public DateTime DisplayDateTime
{
get => (DateTime) GetValue(DisplayDateTimeProperty);
set => SetValue(DisplayDateTimeProperty, value);
}
public static readonly DependencyProperty IsDropDownOpenProperty = DependencyProperty.Register(
"IsDropDownOpen", typeof(bool), typeof(DateTimePicker), new FrameworkPropertyMetadata(ValueBoxes.FalseBox, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnIsDropDownOpenChanged, OnCoerceIsDropDownOpen));
private static object OnCoerceIsDropDownOpen(DependencyObject d, object baseValue) => d is DateTimePicker dp && !dp.IsEnabled ? false : baseValue;
private static void OnIsDropDownOpenChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var dp = d as DateTimePicker;
var newValue = (bool)e.NewValue;
if (dp?._popUp != null && dp._popUp.IsOpen != newValue)
{
dp._popUp.IsOpen = newValue;
if (newValue)
{
dp._originalSelectedDateTime = dp.SelectedDateTime;
dp.Dispatcher.BeginInvoke(DispatcherPriority.Input, (Action)delegate
{
dp._calendarWithClock.Focus();
});
}
}
}
public bool IsDropDownOpen
{
get => (bool) GetValue(IsDropDownOpenProperty);
set => SetValue(IsDropDownOpenProperty, value);
}
public static readonly DependencyProperty SelectedDateTimeProperty = DependencyProperty.Register(
"SelectedDateTime", typeof(DateTime?), typeof(DateTimePicker), new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnSelectedDateTimeChanged, CoerceSelectedDateTime));
private static object CoerceSelectedDateTime(DependencyObject d, object value)
{
var dp = (DateTimePicker)d;
dp._calendarWithClock.SelectedDateTime = (DateTime?)value;
return dp._calendarWithClock.SelectedDateTime;
}
private static void OnSelectedDateTimeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (!(d is DateTimePicker dp)) return;
if (dp.SelectedDateTime.HasValue)
{
var time = dp.SelectedDateTime.Value;
dp.SetTextInternal(dp.DateTimeToString(time));
}
dp.RaiseEvent(new FunctionEventArgs<DateTime?>(SelectedDateTimeChangedEvent, dp)
{
Info = dp.SelectedDateTime
});
}
public DateTime? SelectedDateTime
{
get => (DateTime?) GetValue(SelectedDateTimeProperty);
set => SetValue(SelectedDateTimeProperty, value);
}
public static readonly DependencyProperty TextProperty = DependencyProperty.Register(
"Text", typeof(string), typeof(DateTimePicker), new FrameworkPropertyMetadata(string.Empty, OnTextChanged));
private static void OnTextChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is DateTimePicker dp && !dp.IsHandlerSuspended(TextProperty))
{
if (e.NewValue is string newValue)
{
if (dp._textBox != null)
{
dp._textBox.Text = newValue;
}
else
{
dp._defaultText = newValue;
}
dp.SetSelectedDateTime();
}
else
{
dp.SetValueNoCallback(SelectedDateTimeProperty, null);
}
}
}
public string Text
{
get => (string) GetValue(TextProperty);
set => SetValue(TextProperty, value);
}
/// <summary>
/// Sets the local Text property without breaking bindings
/// </summary>
/// <param name="value"></param>
private void SetTextInternal(string value)
{
SetCurrentValue(TextProperty, value);
}
public Func<string, OperationResult<bool>> VerifyFunc { get; set; }
public static readonly DependencyProperty IsErrorProperty = DependencyProperty.Register(
"IsError", typeof(bool), typeof(DateTimePicker), new PropertyMetadata(ValueBoxes.FalseBox));
public bool IsError
{
get => (bool) GetValue(IsErrorProperty);
set => SetValue(IsErrorProperty, value);
}
public static readonly DependencyProperty ErrorStrProperty = DependencyProperty.Register(
"ErrorStr", typeof(string), typeof(DateTimePicker), new PropertyMetadata(default(string)));
public string ErrorStr
{
get => (string) GetValue(ErrorStrProperty);
set => SetValue(ErrorStrProperty, value);
}
public static readonly DependencyProperty TextTypeProperty = DependencyProperty.Register(
"TextType", typeof(TextType), typeof(DateTimePicker), new PropertyMetadata(default(TextType)));
public TextType TextType
{
get => (TextType) GetValue(TextTypeProperty);
set => SetValue(TextTypeProperty, value);
}
public static readonly DependencyProperty ShowClearButtonProperty = DependencyProperty.Register(
"ShowClearButton", typeof(bool), typeof(DateTimePicker), new PropertyMetadata(ValueBoxes.FalseBox));
public bool ShowClearButton
{
get => (bool) GetValue(ShowClearButtonProperty);
set => SetValue(ShowClearButtonProperty, value);
}
#endregion
#region Public Methods
public override void OnApplyTemplate()
{
if (DesignerProperties.GetIsInDesignMode(this)) return;
if (_popUp != null)
{
_popUp.PreviewMouseLeftButtonDown -= PopUp_PreviewMouseLeftButtonDown;
_popUp.Opened -= PopUp_Opened;
_popUp.Closed -= PopUp_Closed;
_popUp.Child = null;
}
if (_dropDownButton != null)
{
_dropDownButton.Click -= DropDownButton_Click;
_dropDownButton.MouseLeave -= DropDownButton_MouseLeave;
}
if (_textBox != null)
{
_textBox.KeyDown -= TextBox_KeyDown;
_textBox.TextChanged -= TextBox_TextChanged;
_textBox.LostFocus -= TextBox_LostFocus;
}
base.OnApplyTemplate();
_popUp = GetTemplateChild(ElementPopup) as Popup;
_dropDownButton = GetTemplateChild(ElementButton) as Button;
_textBox = GetTemplateChild(ElementTextBox) as WatermarkTextBox;
CheckNull();
_popUp.PreviewMouseLeftButtonDown += PopUp_PreviewMouseLeftButtonDown;
_popUp.Opened += PopUp_Opened;
_popUp.Closed += PopUp_Closed;
_popUp.Child = _calendarWithClock;
if (IsDropDownOpen)
{
_popUp.IsOpen = true;
}
_dropDownButton.Click += DropDownButton_Click;
_dropDownButton.MouseLeave += DropDownButton_MouseLeave;
if (SelectedDateTime == null)
{
_textBox.Text = DateTime.Now.ToString(DateTimeFormat);
}
_textBox.KeyDown += TextBox_KeyDown;
_textBox.TextChanged += TextBox_TextChanged;
_textBox.LostFocus += TextBox_LostFocus;
if (SelectedDateTime == null)
{
if (!string.IsNullOrEmpty(_defaultText))
{
_textBox.Text = _defaultText;
SetSelectedDateTime();
}
}
else
{
_textBox.Text = DateTimeToString(SelectedDateTime.Value);
}
if (_originalSelectedDateTime == null)
{
_originalSelectedDateTime = DateTime.Now;
}
SetCurrentValue(DisplayDateTimeProperty, _originalSelectedDateTime);
}
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;
}
public override string ToString() => SelectedDateTime?.ToString(DateTimeFormat) ?? string.Empty;
#endregion
#region Protected Methods
protected virtual void OnPickerClosed(RoutedEventArgs e)
{
var handler = PickerClosed;
handler?.Invoke(this, e);
}
protected virtual void OnPickerOpened(RoutedEventArgs e)
{
var handler = PickerOpened;
handler?.Invoke(this, e);
}
#endregion Protected Methods
#region Private Methods
private void CheckNull()
{
if (_dropDownButton == null || _popUp == null || _textBox == null)
throw new Exception();
}
private void InitCalendarWithClock()
{
_calendarWithClock = new CalendarWithClock
{
ShowConfirmButton = true
};
_calendarWithClock.SelectedDateTimeChanged += CalendarWithClock_SelectedDateTimeChanged;
_calendarWithClock.Confirmed += CalendarWithClock_Confirmed;
}
private void CalendarWithClock_Confirmed() => TogglePopUp();
private void CalendarWithClock_SelectedDateTimeChanged(object sender, FunctionEventArgs<DateTime?> e) => SelectedDateTime = e.Info;
private void TextBox_LostFocus(object sender, RoutedEventArgs e)
{
SetSelectedDateTime();
}
private void SetIsHandlerSuspended(DependencyProperty property, bool value)
{
if (value)
{
if (_isHandlerSuspended == null)
{
_isHandlerSuspended = new Dictionary<DependencyProperty, bool>(2);
}
_isHandlerSuspended[property] = true;
}
else
{
_isHandlerSuspended?.Remove(property);
}
}
private void SetValueNoCallback(DependencyProperty property, object value)
{
SetIsHandlerSuspended(property, true);
try
{
SetCurrentValue(property, value);
}
finally
{
SetIsHandlerSuspended(property, false);
}
}
private void TextBox_TextChanged(object sender, TextChangedEventArgs e)
{
SetValueNoCallback(TextProperty, _textBox.Text);
VerifyData();
}
private bool ProcessDateTimePickerKey(KeyEventArgs e)
{
switch (e.Key)
{
case Key.System:
{
switch (e.SystemKey)
{
case Key.Down:
{
if ((Keyboard.Modifiers & ModifierKeys.Alt) == ModifierKeys.Alt)
{
TogglePopUp();
return true;
}
break;
}
}
break;
}
case Key.Enter:
{
SetSelectedDateTime();
return true;
}
}
return false;
}
private void TextBox_KeyDown(object sender, KeyEventArgs e)
{
e.Handled = ProcessDateTimePickerKey(e) || e.Handled;
}
private void DropDownButton_MouseLeave(object sender, MouseEventArgs e)
{
_disablePopupReopen = false;
}
private bool IsHandlerSuspended(DependencyProperty property)
{
return _isHandlerSuspended != null && _isHandlerSuspended.ContainsKey(property);
}
private void PopUp_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
if (sender is Popup popup && !popup.StaysOpen)
{
if (_dropDownButton?.InputHitTest(e.GetPosition(_dropDownButton)) != null)
{
_disablePopupReopen = true;
}
}
}
private void PopUp_Opened(object sender, EventArgs e)
{
if (!IsDropDownOpen)
{
SetCurrentValue(IsDropDownOpenProperty, ValueBoxes.TrueBox);
}
_calendarWithClock?.MoveFocus(new TraversalRequest(FocusNavigationDirection.First));
OnPickerOpened(new RoutedEventArgs());
}
private void PopUp_Closed(object sender, EventArgs e)
{
if (IsDropDownOpen)
{
SetCurrentValue(IsDropDownOpenProperty, ValueBoxes.FalseBox);
}
if (_calendarWithClock.IsKeyboardFocusWithin)
{
MoveFocus(new TraversalRequest(FocusNavigationDirection.First));
}
OnPickerClosed(new RoutedEventArgs());
}
private void DropDownButton_Click(object sender, RoutedEventArgs e) => TogglePopUp();
private void TogglePopUp()
{
if (IsDropDownOpen)
{
SetCurrentValue(IsDropDownOpenProperty, ValueBoxes.FalseBox);
}
else
{
if (_disablePopupReopen)
{
_disablePopupReopen = false;
}
else
{
SetSelectedDateTime();
SetCurrentValue(IsDropDownOpenProperty, ValueBoxes.TrueBox);
}
}
}
private void SafeSetText(string s)
{
if (string.Compare(Text, s, StringComparison.Ordinal) != 0)
{
SetCurrentValue(TextProperty, s);
}
}
private DateTime? ParseText(string text)
{
try
{
return DateTime.Parse(text);
}
catch
{
// ignored
}
return null;
}
private DateTime? SetTextBoxValue(string s)
{
if (string.IsNullOrEmpty(s))
{
SafeSetText(s);
return SelectedDateTime;
}
var d = ParseText(s);
if (d != null)
{
SafeSetText(DateTimeToString((DateTime)d));
return d;
}
if (SelectedDateTime != null)
{
var newtext = DateTimeToString(SelectedDateTime.Value);
SafeSetText(newtext);
return SelectedDateTime;
}
SafeSetText(DateTimeToString(DisplayDateTime));
return DisplayDateTime;
}
private void SetSelectedDateTime()
{
if (_textBox != null)
{
if (!string.IsNullOrEmpty(_textBox.Text))
{
var s = _textBox.Text;
if (SelectedDateTime != null)
{
var selectedTime = DateTimeToString(SelectedDateTime.Value);
if (string.Compare(selectedTime, s, StringComparison.Ordinal) == 0)
{
return;
}
}
var d = SetTextBoxValue(s);
if (!SelectedDateTime.Equals(d))
{
SetCurrentValue(SelectedDateTimeProperty, d);
SetCurrentValue(DisplayDateTimeProperty, d);
}
}
else
{
if (SelectedDateTime.HasValue)
{
SetCurrentValue(SelectedDateTimeProperty, null);
}
}
}
else
{
var d = SetTextBoxValue(_defaultText);
if (!SelectedDateTime.Equals(d))
{
SetCurrentValue(SelectedDateTimeProperty, d);
}
}
}
private string DateTimeToString(DateTime d) => d.ToString(DateTimeFormat);
private static void OnGotFocus(object sender, RoutedEventArgs e)
{
var picker = (DateTimePicker)sender;
if (!e.Handled && picker._textBox != null)
{
if (Equals(e.OriginalSource, picker))
{
picker._textBox.Focus();
e.Handled = true;
}
else if (Equals(e.OriginalSource, picker._textBox))
{
picker._textBox.SelectAll();
e.Handled = true;
}
}
}
#endregion
}
}
@@ -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,170 @@
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,130 @@
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using HandyControl.Data;
using HandyControl.Interactivity;
namespace HandyControl.Controls
{
public class SearchBar : TextBox, ICommandSource
{
public SearchBar()
{
CommandBindings.Add(new CommandBinding(ControlCommands.Search, (s, e) => OnSearchStarted()));
}
public static readonly RoutedEvent SearchStartedEvent =
EventManager.RegisterRoutedEvent("SearchStarted", RoutingStrategy.Bubble,
typeof(EventHandler<FunctionEventArgs<string>>), typeof(SearchBar));
public event EventHandler<FunctionEventArgs<string>> SearchStarted
{
add => AddHandler(SearchStartedEvent, value);
remove => RemoveHandler(SearchStartedEvent, value);
}
protected override void OnKeyDown(KeyEventArgs e)
{
base.OnKeyDown(e);
if (e.Key == Key.Enter)
{
OnSearchStarted();
}
}
protected override void OnTextChanged(TextChangedEventArgs e)
{
base.OnTextChanged(e);
if (IsRealTime)
{
OnSearchStarted();
}
VerifyData();
}
private void OnSearchStarted()
{
RaiseEvent(new FunctionEventArgs<string>(SearchStartedEvent, this)
{
Info = Text
});
if (Command == null) return;
if (Command is RoutedCommand command)
{
command.Execute(CommandParameter, CommandTarget);
}
else
{
Command.Execute(CommandParameter);
}
}
/// <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 static readonly DependencyProperty CommandProperty = DependencyProperty.Register(
"Command", typeof(ICommand), typeof(SearchBar), new PropertyMetadata(default(ICommand), OnCommandChanged));
private static void OnCommandChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var ctl = (SearchBar)d;
if (e.OldValue is ICommand oldCommand)
{
oldCommand.CanExecuteChanged -= ctl.CanExecuteChanged;
}
if (e.NewValue is ICommand newCommand)
{
newCommand.CanExecuteChanged += ctl.CanExecuteChanged;
}
}
public ICommand Command
{
get => (ICommand) GetValue(CommandProperty);
set => SetValue(CommandProperty, value);
}
public static readonly DependencyProperty CommandParameterProperty = DependencyProperty.Register(
"CommandParameter", typeof(object), typeof(SearchBar), new PropertyMetadata(default(object)));
public object CommandParameter
{
get => GetValue(CommandParameterProperty);
set => SetValue(CommandParameterProperty, value);
}
public static readonly DependencyProperty CommandTargetProperty = DependencyProperty.Register(
"CommandTarget", typeof(IInputElement), typeof(SearchBar), new PropertyMetadata(default(IInputElement)));
public IInputElement CommandTarget
{
get => (IInputElement) GetValue(CommandTargetProperty);
set => SetValue(CommandTargetProperty, value);
}
private void CanExecuteChanged(object sender, EventArgs e)
{
if (Command == null) return;
IsEnabled = Command is RoutedCommand command
? command.CanExecute(CommandParameter, CommandTarget)
: Command.CanExecute(CommandParameter);
}
}
}
@@ -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,718 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Input;
using System.Windows.Threading;
using HandyControl.Data;
using HandyControl.Interactivity;
namespace HandyControl.Controls
{
/// <summary>
/// 时间选择器
/// </summary>
[TemplatePart(Name = ElementRoot, Type = typeof(Grid))]
[TemplatePart(Name = ElementTextBox, Type = typeof(WatermarkTextBox))]
[TemplatePart(Name = ElementButton, Type = typeof(Button))]
[TemplatePart(Name = ElementPopup, Type = typeof(Popup))]
public class TimePicker : Control, IDataInput
{
#region Constants
private const string ElementRoot = "PART_Root";
private const string ElementTextBox = "PART_TextBox";
private const string ElementButton = "PART_Button";
private const string ElementPopup = "PART_Popup";
#endregion Constants
#region Data
private Clock _clock;
private string _defaultText;
private ButtonBase _dropDownButton;
private Popup _popUp;
private bool _disablePopupReopen;
private WatermarkTextBox _textBox;
private IDictionary<DependencyProperty, bool> _isHandlerSuspended;
private DateTime? _originalSelectedTime;
#endregion Data
#region Public Events
public static readonly RoutedEvent SelectedTimeChangedEvent =
EventManager.RegisterRoutedEvent("SelectedTimeChanged", RoutingStrategy.Direct,
typeof(EventHandler<FunctionEventArgs<DateTime?>>), typeof(TimePicker));
public event EventHandler<FunctionEventArgs<DateTime?>> SelectedTimeChanged
{
add => AddHandler(SelectedTimeChangedEvent, value);
remove => RemoveHandler(SelectedTimeChangedEvent, value);
}
public event RoutedEventHandler ClockClosed;
public event RoutedEventHandler ClockOpened;
#endregion Public Events
static TimePicker()
{
EventManager.RegisterClassHandler(typeof(TimePicker), GotFocusEvent, new RoutedEventHandler(OnGotFocus));
KeyboardNavigation.TabNavigationProperty.OverrideMetadata(typeof(TimePicker), new FrameworkPropertyMetadata(KeyboardNavigationMode.Once));
KeyboardNavigation.IsTabStopProperty.OverrideMetadata(typeof(TimePicker), new FrameworkPropertyMetadata(false));
}
public TimePicker()
{
InitClock();
CommandBindings.Add(new CommandBinding(ControlCommands.Clear, (s, e) =>
{
ClearValue(SelectedTimeProperty);
ClearValue(TextProperty);
_textBox.Text = string.Empty;
}));
}
#region Public Properties
#region TimeFormat
public static readonly DependencyProperty TimeFormatProperty = DependencyProperty.Register(
"TimeFormat", typeof(string), typeof(TimePicker), new PropertyMetadata("HH:mm:ss"));
public string TimeFormat
{
get => (string)GetValue(TimeFormatProperty);
set => SetValue(TimeFormatProperty, value);
}
#endregion TimeFormat
#region ClockStyle
public Style ClockStyle
{
get => (Style)GetValue(ClockStyleProperty);
set => SetValue(ClockStyleProperty, value);
}
public static readonly DependencyProperty ClockStyleProperty = DependencyProperty.Register("ClockStyle", typeof(Style), typeof(TimePicker));
#endregion CalendarStyle
#region DisplayTime
public DateTime DisplayTime
{
get => (DateTime)GetValue(DisplayTimeProperty);
set => SetValue(DisplayTimeProperty, value);
}
public static readonly DependencyProperty DisplayTimeProperty =
DependencyProperty.Register(
"DisplayTime",
typeof(DateTime),
typeof(TimePicker),
new FrameworkPropertyMetadata(DateTime.Now, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, null, CoerceDisplayTime));
private static object CoerceDisplayTime(DependencyObject d, object value)
{
var dp = (TimePicker)d;
dp._clock.DisplayTime = (DateTime)value;
return dp._clock.DisplayTime;
}
#endregion DisplayTime
#region IsDropDownOpen
public bool IsDropDownOpen
{
get => (bool)GetValue(IsDropDownOpenProperty);
set => SetValue(IsDropDownOpenProperty, value);
}
public static readonly DependencyProperty IsDropDownOpenProperty =
DependencyProperty.Register(
"IsDropDownOpen",
typeof(bool),
typeof(TimePicker),
new FrameworkPropertyMetadata(ValueBoxes.FalseBox, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnIsDropDownOpenChanged, OnCoerceIsDropDownOpen));
private static object OnCoerceIsDropDownOpen(DependencyObject d, object baseValue) => d is TimePicker dp && !dp.IsEnabled ? false : baseValue;
private static void OnIsDropDownOpenChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var dp = d as TimePicker;
var newValue = (bool)e.NewValue;
if (dp?._popUp != null && dp._popUp.IsOpen != newValue)
{
dp._popUp.IsOpen = newValue;
if (newValue)
{
dp._originalSelectedTime = dp.SelectedTime;
dp.Dispatcher.BeginInvoke(DispatcherPriority.Input, (Action)delegate
{
dp._clock.Focus();
});
}
}
}
#endregion IsDropDownOpen
#region SelectedTime
public DateTime? SelectedTime
{
get => (DateTime?)GetValue(SelectedTimeProperty);
set => SetValue(SelectedTimeProperty, value);
}
public static readonly DependencyProperty SelectedTimeProperty =
DependencyProperty.Register(
"SelectedTime",
typeof(DateTime?),
typeof(TimePicker),
new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnSelectedTimeChanged, CoerceSelectedTime));
private static void OnSelectedTimeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (!(d is TimePicker dp)) return;
if (dp.SelectedTime.HasValue)
{
var time = dp.SelectedTime.Value;
dp.SetTextInternal(dp.DateTimeToString(time));
}
dp.RaiseEvent(new FunctionEventArgs<DateTime?>(SelectedTimeChangedEvent, dp)
{
Info = dp.SelectedTime
});
}
private static object CoerceSelectedTime(DependencyObject d, object value)
{
var dp = (TimePicker)d;
dp._clock.SelectedTime = (DateTime?)value;
return dp._clock.SelectedTime;
}
#endregion SelectedDate
#region Text
public string Text
{
get => (string)GetValue(TextProperty);
set => SetValue(TextProperty, value);
}
public static readonly DependencyProperty TextProperty =
DependencyProperty.Register(
"Text",
typeof(string),
typeof(TimePicker),
new FrameworkPropertyMetadata(string.Empty, OnTextChanged));
private static void OnTextChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is TimePicker dp && !dp.IsHandlerSuspended(TextProperty))
{
if (e.NewValue is string newValue)
{
if (dp._textBox != null)
{
dp._textBox.Text = newValue;
}
else
{
dp._defaultText = newValue;
}
dp.SetSelectedTime();
}
else
{
dp.SetValueNoCallback(SelectedTimeProperty, null);
}
}
}
/// <summary>
/// Sets the local Text property without breaking bindings
/// </summary>
/// <param name="value"></param>
private void SetTextInternal(string value)
{
SetCurrentValue(TextProperty, value);
}
#endregion Text
public Func<string, OperationResult<bool>> VerifyFunc { get; set; }
public static readonly DependencyProperty IsErrorProperty = DependencyProperty.Register(
"IsError", typeof(bool), typeof(TimePicker), new PropertyMetadata(ValueBoxes.FalseBox));
public bool IsError
{
get => (bool) GetValue(IsErrorProperty);
set => SetValue(IsErrorProperty, value);
}
public static readonly DependencyProperty ErrorStrProperty = DependencyProperty.Register(
"ErrorStr", typeof(string), typeof(TimePicker), new PropertyMetadata(default(string)));
public string ErrorStr
{
get => (string) GetValue(ErrorStrProperty);
set => SetValue(ErrorStrProperty, value);
}
public static readonly DependencyProperty TextTypeProperty = DependencyProperty.Register(
"TextType", typeof(TextType), typeof(TimePicker), new PropertyMetadata(default(TextType)));
public TextType TextType
{
get => (TextType) GetValue(TextTypeProperty);
set => SetValue(TextTypeProperty, value);
}
public static readonly DependencyProperty ShowClearButtonProperty = DependencyProperty.Register(
"ShowClearButton", typeof(bool), typeof(TimePicker), new PropertyMetadata(ValueBoxes.FalseBox));
public bool ShowClearButton
{
get => (bool) GetValue(ShowClearButtonProperty);
set => SetValue(ShowClearButtonProperty, value);
}
#endregion Public Properties
#region Public Methods
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;
}
public override void OnApplyTemplate()
{
if (DesignerProperties.GetIsInDesignMode(this)) return;
if (_popUp != null)
{
_popUp.PreviewMouseLeftButtonDown -= PopUp_PreviewMouseLeftButtonDown;
_popUp.Opened -= PopUp_Opened;
_popUp.Closed -= PopUp_Closed;
_popUp.Child = null;
}
if (_dropDownButton != null)
{
_dropDownButton.Click -= DropDownButton_Click;
_dropDownButton.MouseLeave -= DropDownButton_MouseLeave;
}
if (_textBox != null)
{
_textBox.KeyDown -= TextBox_KeyDown;
_textBox.TextChanged -= TextBox_TextChanged;
_textBox.LostFocus -= TextBox_LostFocus;
}
base.OnApplyTemplate();
_popUp = GetTemplateChild(ElementPopup) as Popup;
_dropDownButton = GetTemplateChild(ElementButton) as Button;
_textBox = GetTemplateChild(ElementTextBox) as WatermarkTextBox;
CheckNull();
_popUp.PreviewMouseLeftButtonDown += PopUp_PreviewMouseLeftButtonDown;
_popUp.Opened += PopUp_Opened;
_popUp.Closed += PopUp_Closed;
_popUp.Child = _clock;
if (IsDropDownOpen)
{
_popUp.IsOpen = true;
}
_dropDownButton.Click += DropDownButton_Click;
_dropDownButton.MouseLeave += DropDownButton_MouseLeave;
if (SelectedTime == null)
{
_textBox.Text = DateTime.Now.ToString(TimeFormat);
}
_textBox.KeyDown += TextBox_KeyDown;
_textBox.TextChanged += TextBox_TextChanged;
_textBox.LostFocus += TextBox_LostFocus;
if (SelectedTime == null)
{
if (!string.IsNullOrEmpty(_defaultText))
{
_textBox.Text = _defaultText;
SetSelectedTime();
}
}
else
{
_textBox.Text = DateTimeToString((DateTime)SelectedTime);
}
if (_originalSelectedTime == null)
{
_originalSelectedTime = DateTime.Now;
}
SetCurrentValue(DisplayTimeProperty, _originalSelectedTime);
}
public override string ToString() => SelectedTime?.ToString(TimeFormat) ?? string.Empty;
#endregion
#region Protected Methods
protected virtual void OnClockClosed(RoutedEventArgs e)
{
var handler = ClockClosed;
handler?.Invoke(this, e);
}
protected virtual void OnClockOpened(RoutedEventArgs e)
{
var handler = ClockOpened;
handler?.Invoke(this, e);
}
#endregion Protected Methods
#region Private Methods
private void CheckNull()
{
if (_dropDownButton == null || _popUp == null || _textBox == null)
throw new Exception();
}
private void InitClock()
{
_clock = new Clock
{
ShowConfirmButton = true
};
_clock.SelectedTimeChanged += Clock_SelectedTimeChanged;
_clock.Confirmed += Clock_Confirmed;
}
private void TextBox_LostFocus(object sender, RoutedEventArgs e)
{
SetSelectedTime();
}
private void SetIsHandlerSuspended(DependencyProperty property, bool value)
{
if (value)
{
if (_isHandlerSuspended == null)
{
_isHandlerSuspended = new Dictionary<DependencyProperty, bool>(2);
}
_isHandlerSuspended[property] = true;
}
else
{
_isHandlerSuspended?.Remove(property);
}
}
private void SetValueNoCallback(DependencyProperty property, object value)
{
SetIsHandlerSuspended(property, true);
try
{
SetCurrentValue(property, value);
}
finally
{
SetIsHandlerSuspended(property, false);
}
}
private void TextBox_TextChanged(object sender, TextChangedEventArgs e)
{
SetValueNoCallback(TextProperty, _textBox.Text);
VerifyData();
}
private bool ProcessTimePickerKey(KeyEventArgs e)
{
switch (e.Key)
{
case Key.System:
{
switch (e.SystemKey)
{
case Key.Down:
{
if ((Keyboard.Modifiers & ModifierKeys.Alt) == ModifierKeys.Alt)
{
TogglePopUp();
return true;
}
break;
}
}
break;
}
case Key.Enter:
{
SetSelectedTime();
return true;
}
}
return false;
}
private void TextBox_KeyDown(object sender, KeyEventArgs e)
{
e.Handled = ProcessTimePickerKey(e) || e.Handled;
}
private void DropDownButton_MouseLeave(object sender, MouseEventArgs e)
{
_disablePopupReopen = false;
}
private bool IsHandlerSuspended(DependencyProperty property)
{
return _isHandlerSuspended != null && _isHandlerSuspended.ContainsKey(property);
}
private void PopUp_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
if (sender is Popup popup && !popup.StaysOpen)
{
if (_dropDownButton?.InputHitTest(e.GetPosition(_dropDownButton)) != null)
{
_disablePopupReopen = true;
}
}
}
private void Clock_SelectedTimeChanged(object sender, FunctionEventArgs<DateTime?> e) => SelectedTime = e.Info;
private void Clock_Confirmed() => TogglePopUp();
private void PopUp_Opened(object sender, EventArgs e)
{
if (!IsDropDownOpen)
{
SetCurrentValue(IsDropDownOpenProperty, ValueBoxes.TrueBox);
}
_clock?.MoveFocus(new TraversalRequest(FocusNavigationDirection.First));
OnClockOpened(new RoutedEventArgs());
}
private void PopUp_Closed(object sender, EventArgs e)
{
if (IsDropDownOpen)
{
SetCurrentValue(IsDropDownOpenProperty, ValueBoxes.FalseBox);
}
if (_clock.IsKeyboardFocusWithin)
{
MoveFocus(new TraversalRequest(FocusNavigationDirection.First));
}
OnClockClosed(new RoutedEventArgs());
}
private void DropDownButton_Click(object sender, RoutedEventArgs e) => TogglePopUp();
private void TogglePopUp()
{
if (IsDropDownOpen)
{
SetCurrentValue(IsDropDownOpenProperty, ValueBoxes.FalseBox);
}
else
{
if (_disablePopupReopen)
{
_disablePopupReopen = false;
}
else
{
SetSelectedTime();
SetCurrentValue(IsDropDownOpenProperty, ValueBoxes.TrueBox);
}
}
}
private void SafeSetText(string s)
{
if (string.Compare(Text, s, StringComparison.Ordinal) != 0)
{
SetCurrentValue(TextProperty, s);
}
}
private DateTime? ParseText(string text)
{
try
{
return DateTime.Parse(text);
}
catch
{
// ignored
}
return null;
}
private DateTime? SetTextBoxValue(string s)
{
if (string.IsNullOrEmpty(s))
{
SafeSetText(s);
return SelectedTime;
}
var d = ParseText(s);
if (d != null)
{
SafeSetText(DateTimeToString((DateTime)d));
return d;
}
if (SelectedTime != null)
{
var newtext = DateTimeToString((DateTime) SelectedTime);
SafeSetText(newtext);
return SelectedTime;
}
SafeSetText(DateTimeToString(DisplayTime));
return DisplayTime;
}
private void SetSelectedTime()
{
if (_textBox != null)
{
if (!string.IsNullOrEmpty(_textBox.Text))
{
var s = _textBox.Text;
if (SelectedTime != null)
{
var selectedTime = DateTimeToString(SelectedTime.Value);
if (string.Compare(selectedTime, s, StringComparison.Ordinal) == 0)
{
return;
}
}
var d = SetTextBoxValue(s);
if (!SelectedTime.Equals(d))
{
SetCurrentValue(SelectedTimeProperty, d);
SetCurrentValue(DisplayTimeProperty, d);
}
}
else
{
if (SelectedTime.HasValue)
{
SetCurrentValue(SelectedTimeProperty, null);
}
}
}
else
{
var d = SetTextBoxValue(_defaultText);
if (!SelectedTime.Equals(d))
{
SetCurrentValue(SelectedTimeProperty, d);
}
}
}
private string DateTimeToString(DateTime d) => d.ToString(TimeFormat);
private static void OnGotFocus(object sender, RoutedEventArgs e)
{
var picker = (TimePicker)sender;
if (!e.Handled && picker._textBox != null)
{
if (Equals(e.OriginalSource, picker))
{
picker._textBox.Focus();
e.Handled = true;
}
else if (Equals(e.OriginalSource, picker._textBox))
{
picker._textBox.SelectAll();
e.Handled = true;
}
}
}
#endregion Private Methods
}
}
@@ -0,0 +1,36 @@
using System.Windows;
namespace HandyControl.Controls
{
internal class WatermarkTextBox : TextBox
{
#region Public Properties
#region Watermark
public static readonly DependencyProperty WatermarkProperty = DependencyProperty.Register(
"Watermark", typeof(object), typeof(WatermarkTextBox), new PropertyMetadata(default(object)));
public object Watermark
{
get => GetValue(WatermarkProperty);
set => SetValue(WatermarkProperty, value);
}
#endregion Watermark
#endregion Public Properties
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,202 @@
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 EasingDoubleKeyFrame
{
EasingFunction = new PowerEase
{
EasingMode = EasingMode.EaseOut
},
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 EasingDoubleKeyFrame
{
EasingFunction = new PowerEase
{
EasingMode = EasingMode.EaseIn
},
Value = 360 + subAngle,
KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromSeconds(DotSpeed * (3.5 / 7)))
};
//匀加速结束到匀减速结束
var frame4 = new EasingDoubleKeyFrame
{
EasingFunction = new PowerEase
{
EasingMode = EasingMode.EaseOut
},
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 EasingDoubleKeyFrame
{
EasingFunction = new PowerEase
{
EasingMode = EasingMode.EaseIn
},
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,106 @@
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 EasingThicknessKeyFrame
{
EasingFunction = new PowerEase
{
EasingMode = EasingMode.EaseOut
},
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 EasingThicknessKeyFrame
{
EasingFunction = new PowerEase
{
EasingMode = EasingMode.EaseIn
},
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,210 @@
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;
using HandyControl.Expression.Drawing;
using HandyControl.Tools.Extension;
namespace HandyControl.Controls
{
public class AnimationPath : Shape
{
private bool _isLoaded;
/// <summary>
/// 故事版
/// </summary>
private Storyboard _storyboard;
/// <summary>
/// 路径长度
/// </summary>
private double _pathLength;
/// <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);
}
/// <summary>
/// 更新路径
/// </summary>
private void UpdatePath()
{
if (!Duration.HasTimeSpan || !IsPlaying) return;
_pathLength = PathLength > 0 ? PathLength : Data.GetTotalLength(new Size(ActualWidth, ActualHeight), StrokeThickness);
if (MathHelper.IsVerySmall(_pathLength)) return;
StrokeDashOffset = _pathLength;
StrokeDashArray = new DoubleCollection(new List<double>
{
_pathLength,
_pathLength
});
//定义动画
if (_storyboard != null)
{
_storyboard.Stop();
_storyboard.Completed -= Storyboard_Completed;
}
_storyboard = new Storyboard
{
RepeatBehavior = RepeatBehavior
};
_storyboard.Completed += Storyboard_Completed;
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();
}
private void Storyboard_Completed(object sender, EventArgs e) => RaiseEvent(new RoutedEventArgs(CompletedEvent));
}
}
@@ -0,0 +1,82 @@
using System;
using System.Windows;
using System.Windows.Controls;
using HandyControl.Data;
namespace HandyControl.Controls
{
/// <summary>
/// 标记
/// </summary>
public class Badge : ContentControl
{
public static readonly RoutedEvent ValueChangedEvent =
EventManager.RegisterRoutedEvent("ValueChanged", RoutingStrategy.Bubble,
typeof(EventHandler<FunctionEventArgs<int>>), typeof(Badge));
public event EventHandler<FunctionEventArgs<int>> ValueChanged
{
add => AddHandler(ValueChangedEvent, value);
remove => RemoveHandler(ValueChangedEvent, value);
}
public static readonly DependencyProperty TextProperty = DependencyProperty.Register(
"Text", typeof(string), typeof(Badge), new PropertyMetadata(default(string)));
public string Text
{
get => (string) GetValue(TextProperty);
set => SetValue(TextProperty, value);
}
public static readonly DependencyProperty ValueProperty = DependencyProperty.Register(
"Value", typeof(int), typeof(Badge), new PropertyMetadata(ValueBoxes.Int0Box, OnValueChanged));
private static void OnValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var ctl = (Badge)d;
var v = (int)e.NewValue;
ctl.Text = v <= ctl.Maximum ? v.ToString() : $"{ctl.Maximum}+";
if (ctl.IsInitialized)
{
ctl.RaiseEvent(new FunctionEventArgs<int>(ValueChangedEvent, ctl)
{
Info = v
});
}
}
public int Value
{
get => (int)GetValue(ValueProperty);
set => SetValue(ValueProperty, value);
}
public static readonly DependencyProperty StatusProperty = DependencyProperty.Register(
"Status", typeof(BadgeStatus), typeof(Badge), new PropertyMetadata(default(BadgeStatus)));
public BadgeStatus Status
{
get => (BadgeStatus)GetValue(StatusProperty);
set => SetValue(StatusProperty, value);
}
public static readonly DependencyProperty MaximumProperty = DependencyProperty.Register(
"Maximum", typeof(int), typeof(Badge), new PropertyMetadata(ValueBoxes.Int99Box, OnValueChanged));
public int Maximum
{
get => (int)GetValue(MaximumProperty);
set => SetValue(MaximumProperty, value);
}
public static readonly DependencyProperty BadgeMarginProperty = DependencyProperty.Register(
"BadgeMargin", typeof(Thickness), typeof(Badge), new PropertyMetadata(default(Thickness)));
public Thickness BadgeMargin
{
get => (Thickness)GetValue(BadgeMarginProperty);
set => SetValue(BadgeMarginProperty, value);
}
}
}
@@ -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,52 @@
using System;
using System.Windows;
using HandyControl.Data;
namespace HandyControl.Controls
{
public class ChatBubble : SelectableItem
{
public static readonly DependencyProperty RoleProperty = DependencyProperty.Register(
"Role", typeof(ChatRoleType), typeof(ChatBubble), new PropertyMetadata(default(ChatRoleType)));
public ChatRoleType Role
{
get => (ChatRoleType) GetValue(RoleProperty);
set => SetValue(RoleProperty, value);
}
public static readonly DependencyProperty TypeProperty = DependencyProperty.Register(
"Type", typeof(ChatMessageType), typeof(ChatBubble), new PropertyMetadata(default(ChatMessageType)));
public ChatMessageType Type
{
get => (ChatMessageType) GetValue(TypeProperty);
set => SetValue(TypeProperty, value);
}
public static readonly DependencyProperty IsReadProperty = DependencyProperty.Register(
"IsRead", typeof(bool), typeof(ChatBubble), new PropertyMetadata(ValueBoxes.FalseBox));
public bool IsRead
{
get => (bool) GetValue(IsReadProperty);
set => SetValue(IsReadProperty, value);
}
public static void SetMaxWidth(DependencyObject element, double value)
=> element.SetValue(MaxWidthProperty, value);
public static double GetMaxWidth(DependencyObject element)
=> (double) element.GetValue(MaxWidthProperty);
public Action<object> ReadAction { get; set; }
protected override void OnSelected(RoutedEventArgs e)
{
base.OnSelected(e);
IsRead = true;
ReadAction?.Invoke(Content);
}
}
}
@@ -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,84 @@
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using HandyControl.Data;
using HandyControl.Interactivity;
using HandyControl.Tools;
namespace HandyControl.Controls
{
public class Dialog : ContentControl
{
private Adorner _container;
public static readonly DependencyProperty IsClosedProperty = DependencyProperty.Register(
"IsClosed", typeof(bool), typeof(Dialog), new PropertyMetadata(ValueBoxes.FalseBox));
public bool IsClosed
{
get => (bool) GetValue(IsClosedProperty);
internal set => SetValue(IsClosedProperty, value);
}
public Dialog()
{
CommandBindings.Add(new CommandBinding(ControlCommands.Close, (s, e) => Close()));
}
public static Dialog Show<T>() where T : new() => Show(new T());
public static Dialog Show(object content)
{
var dialog = new Dialog
{
Content = content
};
var window = VisualHelper.GetActiveWindow();
if (window != null)
{
var decorator = VisualHelper.GetChild<AdornerDecorator>(window);
if (decorator != null)
{
if (decorator.Child != null)
{
decorator.Child.IsEnabled = false;
}
var layer = decorator.AdornerLayer;
if (layer != null)
{
var container = new AdornerContainer(layer)
{
Child = dialog
};
dialog._container = container;
dialog.IsClosed = false;
layer.Add(container);
}
}
}
return dialog;
}
public void Close()
{
var window = VisualHelper.GetActiveWindow();
if (window != null)
{
var decorator = VisualHelper.GetChild<AdornerDecorator>(window);
if (decorator != null)
{
if (decorator.Child != null)
{
decorator.Child.IsEnabled = true;
}
var layer = decorator.AdornerLayer;
layer?.Remove(_container);
IsClosed = true;
}
}
}
}
}
@@ -0,0 +1,100 @@
using System;
using System.Windows;
using System.Windows.Controls;
using HandyControl.Data;
using HandyControl.Tools;
using HandyControl.Tools.Extension;
namespace HandyControl.Controls
{
public class GotoTop : Button
{
private Action _gotoTopAction;
private System.Windows.Controls.ScrollViewer _scrollViewer;
public static readonly DependencyProperty TargetProperty = DependencyProperty.Register(
"Target", typeof(DependencyObject), typeof(GotoTop), new PropertyMetadata(default(DependencyObject)));
public DependencyObject Target
{
get => (DependencyObject)GetValue(TargetProperty);
set => SetValue(TargetProperty, value);
}
public GotoTop() => Loaded += (s, e) => CreateGotoAction(Target);
public virtual void CreateGotoAction(DependencyObject obj)
{
if (_scrollViewer != null)
{
_scrollViewer.ScrollChanged -= ScrollViewer_ScrollChanged;
}
_scrollViewer = VisualHelper.GetChild<System.Windows.Controls.ScrollViewer>(obj);
if (_scrollViewer != null)
{
_scrollViewer.ScrollChanged += ScrollViewer_ScrollChanged;
if (_scrollViewer is ScrollViewer scrollViewerHandy && Animated && scrollViewerHandy.IsEnableInertia)
{
_gotoTopAction = () => scrollViewerHandy.ScrollToTopInternal(AnimationTime);
}
else
{
_gotoTopAction = () => _scrollViewer.ScrollToTop();
}
}
}
private void ScrollViewer_ScrollChanged(object sender, ScrollChangedEventArgs e)
{
if (AutoHiding)
{
this.Show(e.VerticalOffset >= HidingHeight);
}
}
public static readonly DependencyProperty AnimatedProperty = DependencyProperty.Register(
"Animated", typeof(bool), typeof(GotoTop), new PropertyMetadata(ValueBoxes.TrueBox));
public bool Animated
{
get => (bool) GetValue(AnimatedProperty);
set => SetValue(AnimatedProperty, value);
}
public static readonly DependencyProperty AnimationTimeProperty = DependencyProperty.Register(
"AnimationTime", typeof(double), typeof(GotoTop), new PropertyMetadata(ValueBoxes.Double200Box));
public double AnimationTime
{
get => (double) GetValue(AnimationTimeProperty);
set => SetValue(AnimationTimeProperty, value);
}
public static readonly DependencyProperty HidingHeightProperty = DependencyProperty.Register(
"HidingHeight", typeof(double), typeof(GotoTop), new PropertyMetadata(default(double)));
public double HidingHeight
{
get => (double) GetValue(HidingHeightProperty);
set => SetValue(HidingHeightProperty, value);
}
public static readonly DependencyProperty AutoHidingProperty = DependencyProperty.Register(
"AutoHiding", typeof(bool), typeof(GotoTop), new PropertyMetadata(ValueBoxes.TrueBox));
public bool AutoHiding
{
get => (bool) GetValue(AutoHidingProperty);
set => SetValue(AutoHidingProperty, value);
}
protected override void OnClick()
{
base.OnClick();
_gotoTopAction?.Invoke();
}
}
}
@@ -0,0 +1,51 @@
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using HandyControl.Tools;
namespace HandyControl.Controls
{
public class Gravatar : ContentControl
{
public static readonly DependencyProperty GeneratorProperty = DependencyProperty.Register(
"Generator", typeof(IGravatarGenerator), typeof(Gravatar), new PropertyMetadata(new GithubGravatarGenerator()));
public IGravatarGenerator Generator
{
get => (IGravatarGenerator) GetValue(GeneratorProperty);
set => SetValue(GeneratorProperty, value);
}
public static readonly DependencyProperty IdProperty = DependencyProperty.Register(
"Id", typeof(string), typeof(Gravatar), new PropertyMetadata(default(string), OnIdChanged));
private static void OnIdChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var ctl = (Gravatar) d;
if (ctl.Source != null) return;
ctl.Content = ctl.Generator.GetGravatar((string)e.NewValue);
}
public string Id
{
get => (string) GetValue(IdProperty);
set => SetValue(IdProperty, value);
}
public static readonly DependencyProperty SourceProperty = DependencyProperty.Register(
"Source", typeof(ImageSource), typeof(Gravatar), new PropertyMetadata(default(ImageSource), OnSourceChanged));
private static void OnSourceChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var ctl = (Gravatar)d;
ctl.Background = new ImageBrush((ImageSource)e.NewValue);
}
public ImageSource Source
{
get => (ImageSource) GetValue(SourceProperty);
set => SetValue(SourceProperty, value);
}
}
}
@@ -0,0 +1,601 @@
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 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>
private int _tickCount;
/// <summary>
/// 关闭计时器
/// </summary>
private DispatcherTimer _timerClose;
private static readonly Dictionary<string, Panel> PanelDic = new Dictionary<string, Panel>();
#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));
}
public static void Register(string token, Panel panel)
{
if (string.IsNullOrEmpty(token) || panel == null) return;
PanelDic[token] = panel;
InitGrowlPanel(panel);
}
public static void Unregister(string token, Panel panel)
{
if (string.IsNullOrEmpty(token) || panel == null) return;
if (PanelDic.ContainsKey(token))
{
if (ReferenceEquals(PanelDic[token], panel))
{
PanelDic.Remove(token);
panel.ContextMenu = null;
panel.SetCurrentValue(PanelElement.FluidMoveBehaviorProperty, DependencyProperty.UnsetValue);
}
}
}
public static void Unregister(Panel panel)
{
if (panel == null) return;
var first = PanelDic.FirstOrDefault(item => ReferenceEquals(panel, item.Value));
if (!string.IsNullOrEmpty(first.Key))
{
PanelDic.Remove(first.Key);
panel.ContextMenu = null;
panel.SetCurrentValue(PanelElement.FluidMoveBehaviorProperty, DependencyProperty.UnsetValue);
}
}
protected override void OnMouseEnter(MouseEventArgs e)
{
base.OnMouseEnter(e);
_buttonClose.Show(_showCloseButton);
}
protected override void OnMouseLeave(MouseEventArgs e)
{
base.OnMouseLeave(e);
_buttonClose.Collapse();
}
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; }
/// <summary>
/// 消息容器
/// </summary>
public static Panel GrowlPanel { 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 ((bool)args.NewValue && o is Panel panel)
{
SetGrowlPanel(panel);
}
}));
public static readonly DependencyProperty TokenProperty = DependencyProperty.RegisterAttached(
"Token", typeof(string), typeof(Growl), new PropertyMetadata(default(string), OnTokenChanged));
private static void OnTokenChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is Panel panel)
{
if (e.NewValue == null)
{
Unregister(panel);
}
else
{
Register(e.NewValue.ToString(), panel);
}
}
}
public static void SetToken(DependencyObject element, string value)
=> element.SetValue(TokenProperty, value);
public static string GetToken(DependencyObject element)
=> (string) element.GetValue(TokenProperty);
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;
InitGrowlPanel(panel);
}
private static void InitGrowlPanel(Panel panel)
{
var menuItem = new MenuItem
{
Header = Properties.Langs.Lang.Clear
};
menuItem.Click += (s, e) =>
{
foreach (var item in panel.Children.OfType<Growl>())
{
item.Close();
}
};
panel.ContextMenu = new ContextMenu
{
Items =
{
menuItem
}
};
PanelElement.SetFluidMoveBehavior(panel, ResourceHelper.GetResource<FluidMoveBehavior>(ResourceToken.BehaviorXY400));
}
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)
{
Application.Current.Dispatcher.Invoke(() =>
{
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)
};
if (!string.IsNullOrEmpty(growlInfo.Token))
{
if (PanelDic.TryGetValue(growlInfo.Token, out var panel))
{
panel?.Children.Insert(0, ctl);
}
}
else
{
GrowlPanel.Children.Insert(0, ctl);
}
});
}
/// <summary>
/// 成功
/// </summary>
/// <param name="message"></param>
/// <param name="token"></param>
public static void Success(string message, string token = "") => Success(new GrowlInfo
{
Message = message,
Token = token
});
/// <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>
/// <param name="token"></param>
public static void Info(string message, string token = "") => Info(new GrowlInfo
{
Message = message,
Token = token
});
/// <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>
/// <param name="token"></param>
public static void Warning(string message, string token = "") => Warning(new GrowlInfo
{
Message = message,
Token = token
});
/// <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>
/// <param name="token"></param>
public static void Error(string message, string token = "") => Error(new GrowlInfo
{
Message = message,
Token = token
});
/// <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>
/// <param name="token"></param>
public static void Fatal(string message, string token = "") => Fatal(new GrowlInfo
{
Message = message,
Token = token
});
/// <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>
/// <param name="token"></param>
public static void Ask(string message, Func<bool, bool> actionBeforeClose, string token = "") => Ask(new GrowlInfo
{
Message = message,
ActionBeforeClose = actionBeforeClose,
Token = token
});
/// <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 += (s, e) =>
{
if (Parent is Panel panel)
{
panel.Children.Remove(this);
}
};
transform.BeginAnimation(TranslateTransform.XProperty, animation);
}
/// <summary>
/// 清除
/// </summary>
/// <param name="token"></param>
public static void Clear(string token = "")
{
if (!string.IsNullOrEmpty(token))
{
if (PanelDic.TryGetValue(token, out var panel))
{
Clear(panel);
}
}
else
{
Clear(GrowlPanel);
}
}
/// <summary>
/// 清除
/// </summary>
/// <param name="panel"></param>
private static void Clear(Panel panel)
{
panel.Children.Clear();
if (panel.ContextMenu != null)
{
panel.ContextMenu.IsOpen = false;
panel.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,425 @@
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Input;
using System.Windows.Interop;
using System.Windows.Media;
using System.Windows.Threading;
using HandyControl.Data;
using HandyControl.Tools;
using HandyControl.Tools.Interop;
namespace HandyControl.Controls
{
public class NotifyIcon : FrameworkElement, IDisposable
{
private bool _added;
private readonly object _syncObj = new object();
private readonly int _id;
private static int NextId;
private ImageSource _icon;
private IntPtr _iconCurrentHandle;
private IntPtr _iconDefaultHandle;
private IconHandle _iconHandle;
private const int WmTrayMouseMessage = NativeMethods.WM_USER + 1024;
private string _windowClassName;
private int _wmTaskbarCreated;
private IntPtr _messageWindowHandle;
private readonly WndProc _callback;
private Popup _contextContent;
private bool _doubleClick;
private DispatcherTimer _dispatcherTimer;
private bool _isTransparent;
private bool _isDisposed;
static NotifyIcon()
{
VisibilityProperty.OverrideMetadata(typeof(NotifyIcon), new PropertyMetadata(Visibility.Visible, OnVisibilityChanged));
}
private static void OnVisibilityChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var ctl = (NotifyIcon) d;
var v = (Visibility)e.NewValue;
if (v == Visibility.Visible)
{
if (ctl._iconCurrentHandle == IntPtr.Zero)
{
ctl.OnIconChanged();
}
ctl.UpdateIcon(true);
}
else if(ctl._iconCurrentHandle != IntPtr.Zero)
{
ctl.UpdateIcon(false);
}
}
public NotifyIcon()
{
_id = ++NextId;
_callback = Callback;
Loaded += (s, e) =>
{
RegisterClass();
if (Visibility == Visibility.Visible)
{
OnIconChanged();
UpdateIcon(true);
}
};
if (Application.Current != null) Application.Current.Exit += (s, e) => Dispose();
}
~NotifyIcon()
{
Dispose(false);
}
public static readonly DependencyProperty TextProperty = DependencyProperty.Register(
"Text", typeof(string), typeof(NotifyIcon), new PropertyMetadata(default(string)));
public string Text
{
get => (string) GetValue(TextProperty);
set => SetValue(TextProperty, value);
}
public static readonly DependencyProperty IconProperty = DependencyProperty.Register(
"Icon", typeof(ImageSource), typeof(NotifyIcon), new PropertyMetadata(default(ImageSource), OnIconChanged));
private static void OnIconChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var ctl = (NotifyIcon)d;
ctl._icon = (ImageSource)e.NewValue;
ctl.OnIconChanged();
}
public ImageSource Icon
{
get => (ImageSource)GetValue(IconProperty);
set => SetValue(IconProperty, value);
}
public static readonly DependencyProperty ContextContentProperty = DependencyProperty.Register(
"ContextContent", typeof(object), typeof(NotifyIcon), new PropertyMetadata(default(object)));
public object ContextContent
{
get => GetValue(ContextContentProperty);
set => SetValue(ContextContentProperty, value);
}
public static readonly DependencyProperty BlinkIntervalProperty = DependencyProperty.Register(
"BlinkInterval", typeof(TimeSpan), typeof(NotifyIcon), new PropertyMetadata(TimeSpan.FromMilliseconds(500), OnBlinkIntervalChanged));
private static void OnBlinkIntervalChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var ctl = (NotifyIcon)d;
if (ctl._dispatcherTimer != null)
{
ctl._dispatcherTimer.Interval = (TimeSpan) e.NewValue;
}
}
public TimeSpan BlinkInterval
{
get => (TimeSpan) GetValue(BlinkIntervalProperty);
set => SetValue(BlinkIntervalProperty, value);
}
public static readonly DependencyProperty IsBlinkProperty = DependencyProperty.Register(
"IsBlink", typeof(bool), typeof(NotifyIcon), new PropertyMetadata(ValueBoxes.FalseBox, OnIsBlinkChanged));
private static void OnIsBlinkChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var ctl = (NotifyIcon)d;
if (ctl.Visibility != Visibility.Visible) return;
if ((bool) e.NewValue)
{
if (ctl._dispatcherTimer == null)
{
ctl._dispatcherTimer = new DispatcherTimer
{
Interval = ctl.BlinkInterval
};
ctl._dispatcherTimer.Tick += ctl.DispatcherTimer_Tick;
}
ctl._dispatcherTimer.Start();
}
else
{
ctl._dispatcherTimer?.Stop();
ctl._dispatcherTimer = null;
ctl.UpdateIcon(true);
}
}
private void DispatcherTimer_Tick(object sender, EventArgs e)
{
if (Visibility != Visibility.Visible || _iconCurrentHandle == IntPtr.Zero) return;
UpdateIcon(true, !_isTransparent);
}
public bool IsBlink
{
get => (bool) GetValue(IsBlinkProperty);
set => SetValue(IsBlinkProperty, value);
}
private void OnIconChanged()
{
if (_icon != null)
{
IconHelper.GetIconHandlesFromImageSource(_icon, out _, out _iconHandle);
_iconCurrentHandle = _iconHandle.CriticalGetHandle();
}
else
{
if (_iconDefaultHandle == IntPtr.Zero)
{
IconHelper.GetDefaultIconHandles(out _, out _iconHandle);
_iconDefaultHandle = _iconHandle.CriticalGetHandle();
}
_iconCurrentHandle = _iconDefaultHandle;
}
}
private void UpdateIcon(bool showIconInTray, bool isTransparent = false)
{
lock (_syncObj)
{
if (DesignerHelper.IsInDesignMode) return;
_isTransparent = isTransparent;
var data = new NOTIFYICONDATA
{
uCallbackMessage = WmTrayMouseMessage,
uFlags = NativeMethods.NIF_MESSAGE | NativeMethods.NIF_ICON | NativeMethods.NIF_TIP,
hWnd = _messageWindowHandle,
uID = _id,
dwInfoFlags = NativeMethods.NIF_TIP,
hIcon = isTransparent ? IntPtr.Zero : _iconCurrentHandle,
szTip = Text
};
if (showIconInTray)
{
if (!_added)
{
UnsafeNativeMethods.Shell_NotifyIcon(NativeMethods.NIM_ADD, data);
_added = true;
}
else
{
UnsafeNativeMethods.Shell_NotifyIcon(NativeMethods.NIM_MODIFY, data);
}
}
else if (_added)
{
UnsafeNativeMethods.Shell_NotifyIcon(NativeMethods.NIM_DELETE, data);
_added = false;
}
}
}
private void RegisterClass()
{
_windowClassName = $"HandyControl.Controls.NotifyIcon{Guid.NewGuid()}";
var wndclass = new WNDCLASS
{
style = 0,
lpfnWndProc = _callback,
cbClsExtra = 0,
cbWndExtra = 0,
hInstance = IntPtr.Zero,
hIcon = IntPtr.Zero,
hCursor = IntPtr.Zero,
hbrBackground = IntPtr.Zero,
lpszMenuName = "",
lpszClassName = _windowClassName
};
UnsafeNativeMethods.RegisterClass(wndclass);
_wmTaskbarCreated = NativeMethods.RegisterWindowMessage("TaskbarCreated");
_messageWindowHandle = UnsafeNativeMethods.CreateWindowEx(0, _windowClassName, "", 0, 0, 0, 1, 1,
IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero);
}
private IntPtr Callback(IntPtr hWnd, int msg, IntPtr wparam, IntPtr lparam)
{
if (IsLoaded)
{
if (msg == _wmTaskbarCreated)
{
UpdateIcon(true);
}
else
{
switch (lparam.ToInt64())
{
case NativeMethods.WM_LBUTTONDBLCLK:
WmMouseDown(MouseButton.Left, 2);
break;
case NativeMethods.WM_LBUTTONUP:
WmMouseUp(MouseButton.Left);
break;
case NativeMethods.WM_RBUTTONUP:
ShowContextMenu();
WmMouseUp(MouseButton.Right);
break;
}
}
}
return UnsafeNativeMethods.DefWindowProc(hWnd, msg, wparam, lparam);
}
private void WmMouseDown(MouseButton button, int clicks)
{
if (clicks == 2)
{
RaiseEvent(new MouseButtonEventArgs(Mouse.PrimaryDevice, Environment.TickCount, button)
{
RoutedEvent = MouseDoubleClickEvent
});
_doubleClick = true;
}
}
private void WmMouseUp(MouseButton button)
{
if (!_doubleClick && button == MouseButton.Left)
{
RaiseEvent(new MouseButtonEventArgs(Mouse.PrimaryDevice, Environment.TickCount, button)
{
RoutedEvent = ClickEvent
});
}
_doubleClick = false;
}
private void ShowContextMenu()
{
var point = new POINT();
UnsafeNativeMethods.GetCursorPos(point);
if (ContextContent != null)
{
if (_contextContent == null)
{
_contextContent = new Popup
{
Placement = PlacementMode.AbsolutePoint,
AllowsTransparency = true,
StaysOpen = false
};
}
_contextContent.HorizontalOffset = point.x;
_contextContent.VerticalOffset = point.y;
_contextContent.Child = new ContentControl
{
Content = ContextContent
};
_contextContent.IsOpen = true;
var handle = IntPtr.Zero;
var hwndSource = (HwndSource)PresentationSource.FromVisual(_contextContent.Child);
if (hwndSource != null)
{
handle = hwndSource.Handle;
}
UnsafeNativeMethods.SetForegroundWindow(handle);
}
else if (ContextMenu != null)
{
ContextMenu.Placement = PlacementMode.AbsolutePoint;
ContextMenu.HorizontalOffset = point.x;
ContextMenu.VerticalOffset = point.y;
ContextMenu.IsOpen = true;
var handle = IntPtr.Zero;
var hwndSource = (HwndSource)PresentationSource.FromVisual(ContextMenu);
if (hwndSource != null)
{
handle = hwndSource.Handle;
}
UnsafeNativeMethods.SetForegroundWindow(handle);
}
}
public static readonly RoutedEvent ClickEvent =
EventManager.RegisterRoutedEvent("Click", RoutingStrategy.Bubble,
typeof(RoutedEventHandler), typeof(NotifyIcon));
public event RoutedEventHandler Click
{
add => AddHandler(ClickEvent, value);
remove => RemoveHandler(ClickEvent, value);
}
public static readonly RoutedEvent MouseDoubleClickEvent =
EventManager.RegisterRoutedEvent("MouseDoubleClick", RoutingStrategy.Bubble,
typeof(RoutedEventHandler), typeof(NotifyIcon));
public event RoutedEventHandler MouseDoubleClick
{
add => AddHandler(MouseDoubleClickEvent, value);
remove => RemoveHandler(MouseDoubleClickEvent, value);
}
private void Dispose(bool disposing)
{
if (_isDisposed) return;
if (disposing)
{
if (_dispatcherTimer != null && IsBlink)
{
_dispatcherTimer.Stop();
}
UpdateIcon(false);
}
_isDisposed = true;
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
public void CloseContextControl()
{
if (_contextContent != null)
{
_contextContent.IsOpen = false;
}
else if (ContextMenu != null)
{
ContextMenu.IsOpen = false;
}
}
}
}
@@ -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,237 @@
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 ScrollToTopInternal(double milliseconds = 500)
{
if (!_isRunning)
{
_totalVerticalOffset = VerticalOffset;
CurrentVerticalOffset = VerticalOffset;
}
ScrollToVerticalOffsetInternal(0, milliseconds);
}
internal void ScrollToVerticalOffsetInternal(double offset, double milliseconds = 500)
{
var animation = AnimationHelper.CreateAnimation(offset, milliseconds);
animation.EasingFunction = new CubicEase
{
EasingMode = EasingMode.EaseOut
};
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.EasingFunction = new CubicEase
{
EasingMode = EasingMode.EaseOut
};
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,266 @@
using System;
using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media.Animation;
using HandyControl.Data;
using HandyControl.Tools;
namespace HandyControl.Controls
{
[TemplateVisualState(GroupName = PresentationGroup, Name = NormalState)]
[TemplateVisualState(GroupName = PresentationGroup, Name = DefaultTransitionState)]
[TemplatePart(Name = PreviousContentPresentationSitePartName, Type = typeof(ContentControl))]
[TemplatePart(Name = CurrentContentPresentationSitePartName, Type = typeof(ContentControl))]
public class TransitioningContentControl : ContentControl
{
private Storyboard _currentTransition;
public TransitioningContentControl()
{
if (!DesignerProperties.GetIsInDesignMode(this))
{
DefaultStyleKey = typeof(TransitioningContentControl);
}
}
private Storyboard CurrentTransition
{
set
{
if (_currentTransition != null)
_currentTransition.Completed -= OnTransitionCompleted;
_currentTransition = value;
if (_currentTransition != null)
_currentTransition.Completed += OnTransitionCompleted;
}
}
#region Events
public event RoutedEventHandler TransitionCompleted;
#endregion Events
public override void OnApplyTemplate()
{
if (DesignerProperties.GetIsInDesignMode(this)) return;
if (IsTransitioning)
AbortTransition();
base.OnApplyTemplate();
PreviousContentPresentationSite =
GetTemplateChild(PreviousContentPresentationSitePartName) as ContentPresenter;
CurrentContentPresentationSite =
GetTemplateChild(CurrentContentPresentationSitePartName) as ContentPresenter;
if (CurrentContentPresentationSite != null)
CurrentContentPresentationSite.Content = Content;
var transition = GetStoryboard(Transition);
CurrentTransition = transition;
if (transition == null)
{
Transition = DefaultTransitionState;
throw new ArgumentException("TransitioningContentControl_TransitionNotFound");
}
VisualStateManager.GoToState(this, NormalState, false);
VisualStateManager.GoToState(this, Transition, true);
}
protected override void OnContentChanged(object oldContent, object newContent)
{
base.OnContentChanged(oldContent, newContent);
StartTransition(oldContent, newContent);
}
[SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "newContent", Justification = "Should be used in the future.")]
private void StartTransition(object oldContent, object newContent)
{
if (CurrentContentPresentationSite != null && PreviousContentPresentationSite != null)
{
CurrentContentPresentationSite.Content = newContent;
PreviousContentPresentationSite.Content = oldContent;
if (!IsTransitioning || RestartTransitionOnContentChange)
{
IsTransitioning = true;
VisualStateManager.GoToState(this, NormalState, false);
VisualStateManager.GoToState(this, Transition, true);
}
}
}
private void OnTransitionCompleted(object sender, EventArgs e)
{
AbortTransition();
var handler = TransitionCompleted;
handler?.Invoke(this, new RoutedEventArgs());
}
public void AbortTransition()
{
VisualStateManager.GoToState(this, NormalState, false);
IsTransitioning = false;
if (PreviousContentPresentationSite != null)
PreviousContentPresentationSite.Content = null;
}
private Storyboard GetStoryboard(string newTransition)
{
var presentationGroup = VisualHelper.TryGetVisualStateGroup(this, PresentationGroup);
Storyboard newStoryboard = null;
if (presentationGroup != null)
newStoryboard = presentationGroup.States
.OfType<VisualState>()
.Where(state => state.Name == newTransition)
.Select(state => state.Storyboard)
.FirstOrDefault();
return newStoryboard;
}
#region Visual state names
private const string PresentationGroup = "PresentationStates";
private const string NormalState = "Normal";
public const string DefaultTransitionState = "DefaultTransition";
#endregion Visual state names
#region Template part names
internal const string PreviousContentPresentationSitePartName = "PreviousContentPresentationSite";
internal const string CurrentContentPresentationSitePartName = "CurrentContentPresentationSite";
#endregion Template part names
#region TemplateParts
private ContentPresenter CurrentContentPresentationSite { get; set; }
private ContentPresenter PreviousContentPresentationSite { get; set; }
#endregion TemplateParts
#region public bool IsTransitioning
private bool _allowIsTransitioningWrite;
public bool IsTransitioning
{
get => (bool)GetValue(IsTransitioningProperty);
private set
{
_allowIsTransitioningWrite = true;
SetValue(IsTransitioningProperty, value);
_allowIsTransitioningWrite = false;
}
}
public static readonly DependencyProperty IsTransitioningProperty =
DependencyProperty.Register(
"IsTransitioning",
typeof(bool),
typeof(TransitioningContentControl),
new PropertyMetadata(OnIsTransitioningPropertyChanged));
private static void OnIsTransitioningPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var source = (TransitioningContentControl)d;
if (!source._allowIsTransitioningWrite)
{
source.IsTransitioning = (bool)e.OldValue;
throw new InvalidOperationException("TransitiotioningContentControl_IsTransitioningReadOnly");
}
}
#endregion public bool IsTransitioning
#region public string Transition
public string Transition
{
get => GetValue(TransitionProperty) as string;
set => SetValue(TransitionProperty, value);
}
public static readonly DependencyProperty TransitionProperty =
DependencyProperty.Register(
"Transition",
typeof(string),
typeof(TransitioningContentControl),
new PropertyMetadata(DefaultTransitionState, OnTransitionPropertyChanged));
private static void OnTransitionPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var source = (TransitioningContentControl)d;
var oldTransition = e.OldValue as string;
var newTransition = e.NewValue as string;
if (source.IsTransitioning)
source.AbortTransition();
var newStoryboard = source.GetStoryboard(newTransition);
if (newStoryboard == null)
if (VisualHelper.TryGetVisualStateGroup(source, PresentationGroup) == null)
{
source.CurrentTransition = null;
}
else
{
source.SetValue(TransitionProperty, oldTransition);
throw new ArgumentException(
"TransitioningContentControl_TransitionNotFound");
}
else
source.CurrentTransition = newStoryboard;
}
#endregion public string Transition
#region public bool RestartTransitionOnContentChange
public bool RestartTransitionOnContentChange
{
get => (bool)GetValue(RestartTransitionOnContentChangeProperty);
set => SetValue(RestartTransitionOnContentChangeProperty, value);
}
public static readonly DependencyProperty RestartTransitionOnContentChangeProperty =
DependencyProperty.Register(
"RestartTransitionOnContentChange",
typeof(bool),
typeof(TransitioningContentControl),
new PropertyMetadata(ValueBoxes.FalseBox, OnRestartTransitionOnContentChangePropertyChanged));
private static void OnRestartTransitionOnContentChangePropertyChanged(DependencyObject d,
DependencyPropertyChangedEventArgs e)
{
((TransitioningContentControl)d).OnRestartTransitionOnContentChangeChanged((bool)e.OldValue,
(bool)e.NewValue);
}
protected virtual void OnRestartTransitionOnContentChangeChanged(bool oldValue, bool newValue)
{
}
#endregion public bool RestartTransitionOnContentChange
}
}
@@ -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,44 @@
using System;
using System.Windows;
using System.Windows.Controls;
namespace HandyControl.Controls
{
/// <summary>
/// 用以代替Grid
/// </summary>
/// <remarks>
/// 当不需要Grid的行、列分隔等功能时建议用此轻量级类代替
/// </remarks>
public class SimplePanel : Panel
{
protected override Size MeasureOverride(Size availableSize)
{
var maxSize = new Size();
var children = InternalChildren;
for (int i = 0, count = children.Count; i < count; ++i)
{
var child = children[i];
if (child != null)
{
child.Measure(availableSize);
maxSize.Width = Math.Max(maxSize.Width, child.DesiredSize.Width);
maxSize.Height = Math.Max(maxSize.Height, child.DesiredSize.Height);
}
}
return maxSize;
}
protected override Size ArrangeOverride(Size arrangeSize)
{
foreach (UIElement child in InternalChildren)
{
child?.Arrange(new Rect(arrangeSize));
}
return arrangeSize;
}
}
}
@@ -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,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,119 @@
using System.Windows;
using System.Windows.Controls.Primitives;
using System.Windows.Media;
using HandyControl.Data;
using HandyControl.Expression.Drawing;
namespace HandyControl.Controls
{
/// <summary>
/// 波浪进度条
/// </summary>
[TemplatePart(Name = ElementWave, Type = typeof(FrameworkElement))]
[TemplatePart(Name = ElementClip, Type = typeof(FrameworkElement))]
public class WaveProgressBar : RangeBase
{
private const string ElementWave = "PART_Wave";
private const string ElementClip = "PART_Clip";
private FrameworkElement _waveElement;
private const double TranslateTransformMinY = -20;
private double _translateTransformYRange;
private TranslateTransform _translateTransform;
public WaveProgressBar() => Loaded += (s, e) => UpdateWave(Value);
static WaveProgressBar()
{
FocusableProperty.OverrideMetadata(typeof(WaveProgressBar),
new FrameworkPropertyMetadata(ValueBoxes.FalseBox));
MaximumProperty.OverrideMetadata(typeof(WaveProgressBar),
new FrameworkPropertyMetadata(ValueBoxes.Double100Box));
}
protected override void OnValueChanged(double oldValue, double newValue)
{
base.OnValueChanged(oldValue, newValue);
UpdateWave(newValue);
}
private void UpdateWave(double value)
{
if (_translateTransform == null || MathHelper.IsVerySmall(Maximum)) return;
var scale = 1 - value / Maximum;
var y = _translateTransformYRange * scale + TranslateTransformMinY;
_translateTransform.Y = y;
}
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
_waveElement = GetTemplateChild(ElementWave) as FrameworkElement;
var clipElement = GetTemplateChild(ElementClip) as FrameworkElement;
if (_waveElement != null && clipElement != null)
{
_translateTransform = new TranslateTransform
{
Y = clipElement.Height
};
_translateTransformYRange = clipElement.Height - TranslateTransformMinY;
_waveElement.RenderTransform = new TransformGroup
{
Children = {_translateTransform}
};
}
}
public static readonly DependencyProperty TextProperty = DependencyProperty.Register(
"Text", typeof(string), typeof(WaveProgressBar), new PropertyMetadata(default(string)));
public string Text
{
get => (string)GetValue(TextProperty);
set => SetValue(TextProperty, value);
}
public static readonly DependencyProperty ShowTextProperty = DependencyProperty.Register(
"ShowText", typeof(bool), typeof(WaveProgressBar), new PropertyMetadata(ValueBoxes.TrueBox));
public bool ShowText
{
get => (bool)GetValue(ShowTextProperty);
set => SetValue(ShowTextProperty, value);
}
public static readonly DependencyProperty WaveFillProperty = DependencyProperty.Register(
"WaveFill", typeof(Brush), typeof(WaveProgressBar), new PropertyMetadata(default(Brush)));
public Brush WaveFill
{
get => (Brush) GetValue(WaveFillProperty);
set => SetValue(WaveFillProperty, value);
}
public static readonly DependencyProperty WaveThicknessProperty = DependencyProperty.Register(
"WaveThickness", typeof(double), typeof(WaveProgressBar), new PropertyMetadata(ValueBoxes.Double0Box));
public double WaveThickness
{
get => (double) GetValue(WaveThicknessProperty);
set => SetValue(WaveThicknessProperty, value);
}
public static readonly DependencyProperty WaveStrokeProperty = DependencyProperty.Register(
"WaveStroke", typeof(Brush), typeof(WaveProgressBar), new PropertyMetadata(default(Brush)));
public Brush WaveStroke
{
get => (Brush) GetValue(WaveStrokeProperty);
set => SetValue(WaveStrokeProperty, value);
}
}
}
@@ -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,27 @@
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);
}
}
}
@@ -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,294 @@
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 => new Lazy<TabControl>(() => ItemsControl.ItemsControlFromItemContainer(this) as TabControl).Value;
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,244 @@
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));
UseLayoutRoundingProperty.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,91 @@
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media.Animation;
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)),
EaseY = new PowerEase(),
EaseX = new PowerEase()
};
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,260 @@
using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using HandyControl.Data;
namespace HandyControl.Controls
{
[TemplatePart(Name = ElementButtonConfirm, Type = typeof(Button))]
[TemplatePart(Name = ElementClockPresenter, Type = typeof(ContentPresenter))]
[TemplatePart(Name = ElementCalendarPresenter, Type = typeof(ContentPresenter))]
public class CalendarWithClock : Control
{
#region Constants
private const string ElementButtonConfirm = "PART_ButtonConfirm";
private const string ElementClockPresenter = "PART_ClockPresenter";
private const string ElementCalendarPresenter = "PART_CalendarPresenter";
#endregion Constants
#region Data
private ContentPresenter _clockPresenter;
private ContentPresenter _calendarPresenter;
private Clock _clock;
private Calendar _calendar;
private Button _buttonConfirm;
private bool _isLoaded;
private IDictionary<DependencyProperty, bool> _isHandlerSuspended;
#endregion Data
#region Public Events
public static readonly RoutedEvent SelectedDateTimeChangedEvent =
EventManager.RegisterRoutedEvent("SelectedDateTimeChanged", RoutingStrategy.Direct,
typeof(EventHandler<FunctionEventArgs<DateTime?>>), typeof(CalendarWithClock));
public event EventHandler<FunctionEventArgs<DateTime?>> SelectedDateTimeChanged
{
add => AddHandler(SelectedDateTimeChangedEvent, value);
remove => RemoveHandler(SelectedDateTimeChangedEvent, value);
}
public event EventHandler<FunctionEventArgs<DateTime>> DisplayDateTimeChanged;
public event Action Confirmed;
#endregion Public Events
public CalendarWithClock()
{
InitCalendarAndClock();
Loaded += (s, e) =>
{
if (_isLoaded) return;
_isLoaded = true;
DisplayDateTime = SelectedDateTime ?? DateTime.Now;
};
}
#region Public Properties
public static readonly DependencyProperty DateTimeFormatProperty = DependencyProperty.Register(
"DateTimeFormat", typeof(string), typeof(CalendarWithClock), new PropertyMetadata("yyyy-MM-dd HH:mm:ss"));
public string DateTimeFormat
{
get => (string)GetValue(DateTimeFormatProperty);
set => SetValue(DateTimeFormatProperty, value);
}
public static readonly DependencyProperty ShowConfirmButtonProperty = DependencyProperty.Register(
"ShowConfirmButton", typeof(bool), typeof(CalendarWithClock), new PropertyMetadata(ValueBoxes.FalseBox));
public bool ShowConfirmButton
{
get => (bool)GetValue(ShowConfirmButtonProperty);
set => SetValue(ShowConfirmButtonProperty, value);
}
public static readonly DependencyProperty SelectedDateTimeProperty = DependencyProperty.Register(
"SelectedDateTime", typeof(DateTime?), typeof(CalendarWithClock), new PropertyMetadata(default(DateTime?), OnSelectedDateTimeChanged));
private static void OnSelectedDateTimeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var ctl = (CalendarWithClock)d;
var v = (DateTime?)e.NewValue;
ctl.OnSelectedDateTimeChanged(new FunctionEventArgs<DateTime?>(SelectedDateTimeChangedEvent, ctl)
{
Info = v
});
}
public DateTime? SelectedDateTime
{
get => (DateTime?)GetValue(SelectedDateTimeProperty);
set => SetValue(SelectedDateTimeProperty, value);
}
public static readonly DependencyProperty DisplayDateTimeProperty = DependencyProperty.Register(
"DisplayDateTime", typeof(DateTime), typeof(CalendarWithClock), new FrameworkPropertyMetadata(DateTime.MinValue, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnDisplayDateTimeChanged));
private static void OnDisplayDateTimeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var ctl = (CalendarWithClock)d;
if (ctl.IsHandlerSuspended(DisplayDateTimeProperty)) return;
var v = (DateTime)e.NewValue;
ctl._clock.SelectedTime = v;
ctl._calendar.SelectedDate = v;
ctl.OnDisplayDateTimeChanged(new FunctionEventArgs<DateTime>(v));
}
public DateTime DisplayDateTime
{
get => (DateTime)GetValue(DisplayDateTimeProperty);
set => SetValue(DisplayDateTimeProperty, value);
}
#endregion
#region Public Methods
public override void OnApplyTemplate()
{
if (_buttonConfirm != null)
{
_buttonConfirm.Click -= ButtonConfirm_OnClick;
}
base.OnApplyTemplate();
_buttonConfirm = GetTemplateChild(ElementButtonConfirm) as Button;
_clockPresenter = GetTemplateChild(ElementClockPresenter) as ContentPresenter;
_calendarPresenter = GetTemplateChild(ElementCalendarPresenter) as ContentPresenter;
CheckNull();
_clockPresenter.Content = _clock;
_calendarPresenter.Content = _calendar;
_buttonConfirm.Click += ButtonConfirm_OnClick;
}
#endregion
#region Protected Methods
protected virtual void OnSelectedDateTimeChanged(FunctionEventArgs<DateTime?> e) => RaiseEvent(e);
protected virtual void OnDisplayDateTimeChanged(FunctionEventArgs<DateTime> e)
{
var handler = DisplayDateTimeChanged;
handler?.Invoke(this, e);
}
#endregion Protected Methods
#region Private Methods
private void SetIsHandlerSuspended(DependencyProperty property, bool value)
{
if (value)
{
if (_isHandlerSuspended == null)
{
_isHandlerSuspended = new Dictionary<DependencyProperty, bool>(2);
}
_isHandlerSuspended[property] = true;
}
else
{
_isHandlerSuspended?.Remove(property);
}
}
private void SetValueNoCallback(DependencyProperty property, object value)
{
SetIsHandlerSuspended(property, true);
try
{
SetCurrentValue(property, value);
}
finally
{
SetIsHandlerSuspended(property, false);
}
}
private bool IsHandlerSuspended(DependencyProperty property)
{
return _isHandlerSuspended != null && _isHandlerSuspended.ContainsKey(property);
}
private void CheckNull()
{
if (_buttonConfirm == null || _clockPresenter == null || _calendarPresenter == null) throw new Exception();
}
private void ButtonConfirm_OnClick(object sender, RoutedEventArgs e)
{
SelectedDateTime = DisplayDateTime;
Confirmed?.Invoke();
}
private void InitCalendarAndClock()
{
_clock = new Clock
{
BorderThickness = new Thickness(),
Background = Brushes.Transparent
};
TitleElement.SetBackground(_clock, Brushes.Transparent);
_clock.DisplayTimeChanged += Clock_DisplayTimeChanged;
_calendar = new Calendar
{
BorderThickness = new Thickness(),
Background = Brushes.Transparent,
Focusable = false
};
TitleElement.SetBackground(_calendar, Brushes.Transparent);
_calendar.SelectedDatesChanged += Calendar_SelectedDatesChanged;
}
private void Calendar_SelectedDatesChanged(object sender, SelectionChangedEventArgs e)
{
Mouse.Capture(null);
UpdateDisplayTime();
}
private void Clock_DisplayTimeChanged(object sender, FunctionEventArgs<DateTime> e) => UpdateDisplayTime();
private void UpdateDisplayTime()
{
if (_calendar.SelectedDate != null)
{
var date = _calendar.SelectedDate.Value;
var time = _clock.DisplayTime;
var result = new DateTime(date.Year, date.Month, date.Day, time.Hour, time.Minute, time.Second);
SetValueNoCallback(DisplayDateTimeProperty, result);
}
}
#endregion
}
}
@@ -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,223 @@
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using HandyControl.Interactivity;
using HandyControl.Tools.Extension;
namespace HandyControl.Controls
{
/// <summary>
/// 穿梭框
/// </summary>
[TemplatePart(Name = ElementItemsOrigin, Type = typeof(SimpleItemsControl))]
[TemplatePart(Name = ElementItemsSelected, Type = typeof(SimpleItemsControl))]
public class Transfer : SimpleItemsControl
{
private const string ElementItemsOrigin = "PART_ItemsOrigin";
private const string ElementItemsSelected = "PART_ItemsSelected";
private readonly Dictionary<object, TransferEntry> _entryDic = new Dictionary<object, TransferEntry>();
private SimpleItemsControl _itemsOrigin;
private SimpleItemsControl _itemsSelected;
private IEnumerable _itemsSourceInternal;
public static readonly RoutedEvent SelectionChangedEvent =
EventManager.RegisterRoutedEvent("SelectionChanged", RoutingStrategy.Bubble,
typeof(SelectionChangedEventHandler), typeof(Transfer));
public event SelectionChangedEventHandler SelectionChanged
{
add => AddHandler(SelectionChangedEvent, value);
remove => RemoveHandler(SelectionChangedEvent, value);
}
private static readonly DependencyPropertyKey SelectedItemsPropertyKey =
DependencyProperty.RegisterReadOnly(
"SelectedItems",
typeof(IList),
typeof(Transfer),
new FrameworkPropertyMetadata(default(IList)));
public static readonly DependencyProperty SelectedItemsProperty = SelectedItemsPropertyKey.DependencyProperty;
public IList SelectedItems
{
get => (IList) GetValue(SelectedItemsProperty);
set => SetValue(SelectedItemsProperty, value);
}
public Transfer()
{
CommandBindings.Add(new CommandBinding(ControlCommands.Selected, SelectItems));
CommandBindings.Add(new CommandBinding(ControlCommands.Cancel, DeselectItems));
SetValue(SelectedItemsPropertyKey, new ObservableCollection<object>());
}
public override void OnApplyTemplate()
{
_itemsOrigin?.ItemsHost.Children.Clear();
_itemsSelected?.ItemsHost.Children.Clear();
_itemsOrigin = GetTemplateChild(ElementItemsOrigin) as SimpleItemsControl;
_itemsSelected = GetTemplateChild(ElementItemsSelected) as SimpleItemsControl;
Refresh();
}
private void AddItem(object item)
{
var origin = new TransferItem
{
Content = item
};
var selected = new TransferItem
{
Content = item,
IsOrigin = false
};
var entry = new TransferEntry
{
OriginItem = origin,
SelectedItem = selected
};
_entryDic.Add(item, entry);
_itemsOrigin.Items.Add(origin);
_itemsSelected.Items.Add(selected);
}
private void RemoveItem(object item)
{
if (_entryDic.TryGetValue(item, out var entry))
{
_itemsOrigin.Items.Remove(entry.OriginItem);
_itemsSelected.Items.Remove(entry.SelectedItem);
_entryDic.Remove(item);
}
}
private void ClearItems()
{
_entryDic.Clear();
_itemsOrigin.Items.Clear();
_itemsSelected.Items.Clear();
}
protected override void OnItemsSourceChanged(IEnumerable oldValue, IEnumerable newValue)
{
if (_itemsSourceInternal != null)
{
if (_itemsSourceInternal is INotifyCollectionChanged s)
{
s.CollectionChanged -= InternalCollectionChanged;
}
ClearItems();
}
_itemsSourceInternal = newValue;
if (_itemsSourceInternal != null)
{
if (_itemsSourceInternal is INotifyCollectionChanged s)
{
s.CollectionChanged += InternalCollectionChanged;
}
foreach (var item in _itemsSourceInternal)
{
AddItem(item);
}
}
}
private void InternalCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
if (e.OldItems != null)
{
foreach (var item in e.OldItems)
{
RemoveItem(item);
}
}
if (e.NewItems != null)
{
foreach (var item in e.NewItems)
{
AddItem(item);
}
}
}
protected override DependencyObject GetContainerForItemOverride() => new TransferItem();
protected override bool IsItemItsOwnContainerOverride(object item) => item is TransferItem;
private void SelectItems(object sender, ExecutedRoutedEventArgs e)
{
var list = _entryDic.Where(item => item.Value.OriginItem.IsSelected).ToList();
list.Do(item => item.Value.StatusSwitch(true)).Do(item => SelectedItems.Add(item.Key));
RaiseEvent(new SelectionChangedEventArgs(SelectionChangedEvent, new List<object>(), list));
}
private void DeselectItems(object sender, ExecutedRoutedEventArgs e)
{
var list = _entryDic.Where(item => item.Value.SelectedItem.IsSelected).ToList();
list.Do(item => item.Value.StatusSwitch(false)).Do(item => SelectedItems.Remove(item.Key));
RaiseEvent(new SelectionChangedEventArgs(SelectionChangedEvent, list, new List<object>()));
}
protected override void OnItemsChanged(object sender, NotifyCollectionChangedEventArgs e)
{
}
protected override void OnItemTemplateChanged(DependencyPropertyChangedEventArgs e)
{
}
protected override void OnItemContainerStyleChanged(DependencyPropertyChangedEventArgs e)
{
}
protected override void Refresh()
{
if (_entryDic.Count > 0)
{
_entryDic.Select(item => item.Value.OriginItem).Do(item => _itemsOrigin.Items.Add(item));
_entryDic.Select(item => item.Value.SelectedItem).Do(item => _itemsSelected.Items.Add(item));
}
}
private struct TransferEntry
{
public TransferItem OriginItem { get; set; }
public TransferItem SelectedItem { get; set; }
public void StatusSwitch(bool isTransferred)
{
if (isTransferred)
{
OriginItem.IsTransferred = true;
SelectedItem.IsTransferred = true;
OriginItem.IsSelected = false;
}
else
{
OriginItem.IsTransferred = false;
SelectedItem.IsTransferred = false;
SelectedItem.IsSelected = false;
}
}
}
}
}
@@ -0,0 +1,54 @@
using System.Collections.Specialized;
using System.Windows;
namespace HandyControl.Controls
{
public class TransferGroup : SimpleItemsControl
{
protected override void Refresh()
{
if (Items.Count > 0)
{
foreach (FrameworkElement item in Items)
{
item.Style = ItemContainerStyle;
ItemsHost.Children.Add(item);
}
}
}
protected override void OnItemTemplateChanged(DependencyPropertyChangedEventArgs e)
{
}
protected override void OnItemContainerStyleChanged(DependencyPropertyChangedEventArgs e)
{
}
protected override DependencyObject GetContainerForItemOverride() => new TransferItem();
protected override bool IsItemItsOwnContainerOverride(object item) => item is TransferItem;
protected override void OnItemsChanged(object sender, NotifyCollectionChangedEventArgs e)
{
if (ItemsHost == null) return;
if (e.OldItems != null)
{
foreach (FrameworkElement item in e.OldItems)
{
ItemsHost.Children.Remove(item);
}
}
if (e.NewItems != null)
{
foreach (FrameworkElement item in e.NewItems)
{
item.Style = ItemContainerStyle;
ItemsHost.Children.Add(item);
}
}
}
}
}
@@ -0,0 +1,26 @@
using System.Windows;
using HandyControl.Data;
namespace HandyControl.Controls
{
public class TransferItem : SelectableItem
{
public static readonly DependencyProperty IsTransferredProperty = DependencyProperty.Register(
"IsTransferred", typeof(bool), typeof(TransferItem), new PropertyMetadata(ValueBoxes.FalseBox));
public bool IsTransferred
{
get => (bool) GetValue(IsTransferredProperty);
set => SetValue(IsTransferredProperty, value);
}
public static readonly DependencyProperty IsOriginProperty = DependencyProperty.Register(
"IsOrigin", typeof(bool), typeof(TransferItem), new PropertyMetadata(ValueBoxes.TrueBox));
public bool IsOrigin
{
get => (bool) GetValue(IsOriginProperty);
internal set => SetValue(IsOriginProperty, value);
}
}
}
@@ -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 = ElementPanelTop, Type = typeof(Panel))]
[TemplatePart(Name = ElementImageViewer, Type = typeof(ImageViewer))]
public class ImageBrowser : Window
{
#region Constants
private const string ElementPanelTop = "PART_PanelTop";
private const string ElementImageViewer = "PART_ImageViewer";
#endregion Constants
#region Data
private Panel _panelTop;
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 (_panelTop != null)
{
_panelTop.MouseLeftButtonDown -= PanelTopOnMouseLeftButtonDown;
}
if (_imageViewer != null)
{
_imageViewer.MouseLeftButtonDown -= ImageViewer_MouseLeftButtonDown;
}
base.OnApplyTemplate();
_panelTop = GetTemplateChild(ElementPanelTop) as Panel;
_imageViewer = GetTemplateChild(ElementImageViewer) as ImageViewer;
if (_panelTop != null)
{
_panelTop.MouseLeftButtonDown += PanelTopOnMouseLeftButtonDown;
}
if (_imageViewer != null)
{
_imageViewer.MouseLeftButtonDown += ImageViewer_MouseLeftButtonDown;
}
}
private void ButtonClose_OnClick(object sender, RoutedEventArgs e) => Close();
private void PanelTopOnMouseLeftButtonDown(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,436 @@
using System;
using System.ComponentModel;
using System.Text;
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));
}
protected override void OnPreviewKeyDown(KeyEventArgs e)
{
base.OnPreviewKeyDown(e);
if (e.KeyboardDevice.Modifiers == ModifierKeys.Control && e.Key == Key.C)
{
var builder = new StringBuilder();
var line = new string('-', 27);
builder.Append(line);
builder.Append(Environment.NewLine);
builder.Append(Title);
builder.Append(Environment.NewLine);
builder.Append(line);
builder.Append(Environment.NewLine);
builder.Append(Message);
builder.Append(Environment.NewLine);
builder.Append(line);
builder.Append(Environment.NewLine);
if (_showOk)
{
builder.Append(Properties.Langs.Lang.Confirm);
builder.Append(" ");
}
if (_showYes)
{
builder.Append(Properties.Langs.Lang.Yes);
builder.Append(" ");
}
if (_showNo)
{
builder.Append(Properties.Langs.Lang.No);
builder.Append(" ");
}
if (_showCancel)
{
builder.Append(Properties.Langs.Lang.Cancel);
builder.Append(" ");
}
builder.Append(Environment.NewLine);
builder.Append(line);
builder.Append(Environment.NewLine);
Clipboard.SetText(builder.ToString());
}
}
/// <summary>
/// 成功
/// </summary>
/// <param name="messageBoxText"></param>
/// <param name="caption"></param>
public static MessageBoxResult Success(string messageBoxText, string caption = null)
{
MessageBox messageBox = null;
Application.Current.Dispatcher.Invoke(() =>
{
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)
{
MessageBox messageBox = null;
Application.Current.Dispatcher.Invoke(() =>
{
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)
{
MessageBox messageBox = null;
Application.Current.Dispatcher.Invoke(() =>
{
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)
{
MessageBox messageBox = null;
Application.Current.Dispatcher.Invoke(() =>
{
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)
{
MessageBox messageBox = null;
Application.Current.Dispatcher.Invoke(() =>
{
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)
{
MessageBox messageBox = null;
Application.Current.Dispatcher.Invoke(() =>
{
messageBox = CreateMessageBox(null, messageBoxText, caption, MessageBoxButton.OKCancel, MessageBoxImage.Question, MessageBoxResult.Cancel);
SetButtonStatus(messageBox, MessageBoxButton.OKCancel);
SetImage(messageBox, MessageBoxImage.Question);
messageBox.ShowDialog();
});
return messageBox._messageBoxResult;
}
/// <summary>
/// 自定义信息展示
/// </summary>
/// <param name="info"></param>
/// <returns></returns>
public static MessageBoxResult Show(MessageBoxInfo info)
{
MessageBox messageBox = null;
Application.Current.Dispatcher.Invoke(() =>
{
messageBox = CreateMessageBox(null, info.MessageBoxText, info.Caption, info.Button, MessageBoxImage.None, info.DefaultResult);
SetButtonStatus(messageBox, info.Button);
if (!string.IsNullOrEmpty(info.IconKey))
{
messageBox.ShowImage = true;
messageBox.Image = ResourceHelper.GetResource<Geometry>(info.IconKey);
messageBox.ImageBrush = ResourceHelper.GetResource<Brush>(info.IconBrushKey);
}
messageBox.Style = info.Style;
messageBox.ShowDialog();
});
return messageBox._messageBoxResult;
}
/// <summary>
/// 信息展示
/// </summary>
/// <param name="messageBoxText"></param>
/// <param name="caption"></param>
/// <param name="button"></param>
/// <param name="icon"></param>
/// <param name="defaultResult"></param>
/// <returns></returns>
public static MessageBoxResult Show(string messageBoxText, string caption = null,
MessageBoxButton button = MessageBoxButton.OK, MessageBoxImage icon = MessageBoxImage.None,
MessageBoxResult defaultResult = MessageBoxResult.None) =>
Show(null, messageBoxText, caption, button, icon, defaultResult);
/// <summary>
/// 信息展示
/// </summary>
/// <param name="owner"></param>
/// <param name="messageBoxText"></param>
/// <param name="caption"></param>
/// <param name="button"></param>
/// <param name="icon"></param>
/// <param name="defaultResult"></param>
/// <returns></returns>
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)
{
MessageBox messageBox = null;
Application.Current.Dispatcher.Invoke(() =>
{
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 ?? VisualHelper.GetActiveWindow();
var ownerIsNull = ownerWindow is null;
return new MessageBox
{
Message = messageBoxText,
Owner = ownerWindow,
WindowStartupLocation = WindowStartupLocation.CenterScreen,
ShowTitle = true,
Title = caption ?? string.Empty,
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,352 @@
using System;
using System.Windows;
using System.Windows.Data;
using System.Windows.Input;
using System.Windows.Media;
using HandyControl.Data;
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 Thickness _actualBorderThickness;
private UIElement _nonClientArea;
private bool _showNonClientArea = true;
private double _tempNonClientAreaHeight;
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 += (s, e) => OnLoaded(e);
}
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 = _actualBorderThickness;
}
}
protected void OnLoaded(RoutedEventArgs args)
{
_actualBorderThickness = 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);
if (SizeToContent != SizeToContent.WidthAndHeight)
return;
SizeToContent = SizeToContent.Height;
Dispatcher.BeginInvoke(new Action(() => { SizeToContent = SizeToContent.WidthAndHeight; }));
}
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
_nonClientArea = GetTemplateChild(ElementNonClientArea) as UIElement;
}
private void ShowSystemMenu(object sender, ExecutedRoutedEventArgs e)
{
var point = WindowState == WindowState.Maximized
? new Point(0, NonClientAreaHeight)
: new Point(Left, Top + NonClientAreaHeight);
SystemCommands.ShowSystemMenu(this, point);
}
protected override void OnContentRendered(EventArgs e)
{
base.OnContentRendered(e);
if (SizeToContent == SizeToContent.WidthAndHeight)
InvalidateMeasure();
}
}
}
@@ -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,9 @@
namespace HandyControl.Data
{
public enum BadgeStatus
{
Text,
Dot,
Processing
}
}
@@ -0,0 +1,10 @@
namespace HandyControl.Data
{
public enum ChatMessageType
{
String,
Image,
Audio,
Custom
}
}
@@ -0,0 +1,8 @@
namespace HandyControl.Data
{
public enum ChatRoleType
{
Sender,
Receiver
}
}
Loaded 100 of 2111 files, more files were not shown because too many files have changed in this diff. Show more