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,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
}
}