Adjust project structure
This commit is contained in:
1 parent
d38912f5da
commit
e3c436f939
2111 files changed
+14028
-47199
No files matched your search
@@ -0,0 +1,27 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Windows.Data;
|
||||
|
||||
namespace HandyControl.Tools.Converter
|
||||
{
|
||||
public class Boolean2BooleanReConverter : IValueConverter
|
||||
{
|
||||
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
if (value is bool boolValue)
|
||||
{
|
||||
return !boolValue;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
if (value is bool boolValue)
|
||||
{
|
||||
return !boolValue;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Windows.Data;
|
||||
|
||||
namespace HandyControl.Tools.Converter
|
||||
{
|
||||
public class Boolean2StrConverter : IValueConverter
|
||||
{
|
||||
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
if (value is bool boolValue)
|
||||
{
|
||||
if (parameter is string str)
|
||||
{
|
||||
var arr = str.Split(';');
|
||||
if (arr.Length > 1)
|
||||
{
|
||||
return boolValue ? arr[1] : arr[0];
|
||||
}
|
||||
return "";
|
||||
}
|
||||
return "";
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Windows;
|
||||
using System.Windows.Data;
|
||||
|
||||
namespace HandyControl.Tools.Converter
|
||||
{
|
||||
public class Boolean2VisibilityReConverter : IValueConverter
|
||||
{
|
||||
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
return value != null && (bool)value ? Visibility.Collapsed : Visibility.Visible;
|
||||
}
|
||||
|
||||
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Windows;
|
||||
using System.Windows.Data;
|
||||
|
||||
namespace HandyControl.Tools.Converter
|
||||
{
|
||||
public class BooleanArr2VisibilityConverter : IMultiValueConverter
|
||||
{
|
||||
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
if (values == null)
|
||||
{
|
||||
return Visibility.Collapsed;
|
||||
}
|
||||
|
||||
var arr = new List<bool>();
|
||||
foreach (var item in values)
|
||||
{
|
||||
if (item is bool boolValue)
|
||||
{
|
||||
arr.Add(boolValue);
|
||||
}
|
||||
else
|
||||
{
|
||||
return Visibility.Collapsed;
|
||||
}
|
||||
}
|
||||
return arr.All(item => item) ? Visibility.Visible : Visibility.Collapsed;
|
||||
}
|
||||
|
||||
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Windows;
|
||||
using System.Windows.Data;
|
||||
|
||||
namespace HandyControl.Tools.Converter
|
||||
{
|
||||
public class BorderCircularConverter : IMultiValueConverter
|
||||
{
|
||||
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
if (values.Length == 2 && values[0] is double width && values[1] is double height)
|
||||
{
|
||||
if (width < double.Epsilon || height < double.Epsilon)
|
||||
{
|
||||
return new CornerRadius();
|
||||
}
|
||||
|
||||
var min = Math.Min(width, height);
|
||||
return new CornerRadius(min/2);
|
||||
}
|
||||
|
||||
return DependencyProperty.UnsetValue;
|
||||
}
|
||||
|
||||
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
//referenced from https://stackoverflow.com/a/5650367/9639378
|
||||
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Windows;
|
||||
using System.Windows.Data;
|
||||
using System.Windows.Media;
|
||||
|
||||
namespace HandyControl.Tools.Converter
|
||||
{
|
||||
public class BorderClipConverter : IMultiValueConverter
|
||||
{
|
||||
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
if (values.Length == 3 && values[0] is double width && values[1] is double height && values[2] is CornerRadius radius)
|
||||
{
|
||||
if (width < double.Epsilon || height < double.Epsilon)
|
||||
{
|
||||
return Geometry.Empty;
|
||||
}
|
||||
|
||||
var clip = new RectangleGeometry(new Rect(0, 0, width, height), radius.TopLeft, radius.TopLeft);
|
||||
clip.Freeze();
|
||||
|
||||
return clip;
|
||||
}
|
||||
|
||||
return DependencyProperty.UnsetValue;
|
||||
}
|
||||
|
||||
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Windows.Data;
|
||||
using System.Windows.Media;
|
||||
|
||||
namespace HandyControl.Tools.Converter
|
||||
{
|
||||
public class Color2ChannelAConverter : IValueConverter
|
||||
{
|
||||
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
if (value is SolidColorBrush brush)
|
||||
{
|
||||
return brush.Color.A;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
if (value is string str)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (ColorConverter.ConvertFromString(str) is Color color)
|
||||
{
|
||||
return new SolidColorBrush(color);
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
return parameter;
|
||||
}
|
||||
return parameter;
|
||||
}
|
||||
return parameter;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Windows.Data;
|
||||
using System.Windows.Media;
|
||||
|
||||
namespace HandyControl.Tools.Converter
|
||||
{
|
||||
public class Color2HexStrConverter : IValueConverter
|
||||
{
|
||||
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
if (value is SolidColorBrush brush)
|
||||
{
|
||||
var color = brush.Color;
|
||||
return color.A < 255 ? $"#{color.A:X2}{color.R:X2}{color.G:X2}{color.B:X2}" : $"#{color.R:X2}{color.G:X2}{color.B:X2}";
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
if (value is string str)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (ColorConverter.ConvertFromString(str) is Color color)
|
||||
{
|
||||
return new SolidColorBrush(color);
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
return new SolidColorBrush(default(Color));
|
||||
}
|
||||
return new SolidColorBrush(default(Color));
|
||||
}
|
||||
return new SolidColorBrush(default(Color));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Windows.Data;
|
||||
|
||||
namespace HandyControl.Tools.Converter
|
||||
{
|
||||
public class Int2StrConverter : IValueConverter
|
||||
{
|
||||
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
if (value is int intValue)
|
||||
{
|
||||
if (parameter is string str)
|
||||
{
|
||||
var arr = str.Split(';');
|
||||
if (arr.Length > intValue)
|
||||
{
|
||||
return arr[intValue];
|
||||
}
|
||||
|
||||
return intValue;
|
||||
}
|
||||
|
||||
return intValue;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Windows.Data;
|
||||
|
||||
namespace HandyControl.Tools.Converter
|
||||
{
|
||||
public class Long2FileSizeConverter : IValueConverter
|
||||
{
|
||||
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
if (value == null) return Properties.Langs.Lang.UnknownSize;
|
||||
if (value is long intValue)
|
||||
{
|
||||
if (intValue < 0)
|
||||
{
|
||||
return Properties.Langs.Lang.UnknownSize;
|
||||
}
|
||||
if (intValue < 1024)
|
||||
{
|
||||
return $"{intValue} B";
|
||||
}
|
||||
if (intValue < 1048576)
|
||||
{
|
||||
return $"{intValue / 1024.0:0.00} KB";
|
||||
}
|
||||
if (intValue < 1073741824)
|
||||
{
|
||||
return $"{intValue / 1048576.0:0.00} MB";
|
||||
}
|
||||
if (intValue < 1099511627776)
|
||||
{
|
||||
return $"{intValue / 1073741824.0:0.00} GB";
|
||||
}
|
||||
if (intValue < 1125899906842624)
|
||||
{
|
||||
return $"{intValue / 1099511627776.0:0.00} TB";
|
||||
}
|
||||
if (intValue < 1152921504606847000)
|
||||
{
|
||||
return $"{intValue / 1125899906842624.0:0.00} PB";
|
||||
}
|
||||
return Properties.Langs.Lang.TooLarge;
|
||||
}
|
||||
return Properties.Langs.Lang.Unknown;
|
||||
}
|
||||
|
||||
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Windows.Data;
|
||||
|
||||
namespace HandyControl.Tools.Converter
|
||||
{
|
||||
public class Object2BooleanConverter : IValueConverter
|
||||
{
|
||||
public object Convert(object value, Type targetType, object parameter, CultureInfo culture) => !(value is null);
|
||||
|
||||
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Windows;
|
||||
using System.Windows.Data;
|
||||
|
||||
namespace HandyControl.Tools.Converter
|
||||
{
|
||||
public class Source2ImageConverter : IValueConverter
|
||||
{
|
||||
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
return value != null ? new Uri(value.ToString(), UriKind.Relative) : null;
|
||||
}
|
||||
|
||||
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
return value != null && (Visibility)value == Visibility.Visible;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Windows;
|
||||
using System.Windows.Data;
|
||||
|
||||
namespace HandyControl.Tools.Converter
|
||||
{
|
||||
public class String2VisibilityConverter : IValueConverter
|
||||
{
|
||||
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
return string.IsNullOrEmpty((string)value) ? Visibility.Collapsed : Visibility.Visible;
|
||||
}
|
||||
|
||||
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
return value != null && (Visibility)value == Visibility.Collapsed;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Windows;
|
||||
using System.Windows.Data;
|
||||
|
||||
namespace HandyControl.Tools.Converter
|
||||
{
|
||||
public class String2VisibilityReConverter : IValueConverter
|
||||
{
|
||||
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
return string.IsNullOrEmpty((string)value) ? Visibility.Visible : Visibility.Collapsed;
|
||||
}
|
||||
|
||||
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
return value != null && (Visibility)value == Visibility.Collapsed;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
using System;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Data;
|
||||
using System.Windows.Media;
|
||||
|
||||
namespace HandyControl.Tools.Converter
|
||||
{
|
||||
public class TreeViewItemMarginConverter: IValueConverter
|
||||
{
|
||||
|
||||
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
|
||||
{
|
||||
var left = 0.0;
|
||||
UIElement element = value as TreeViewItem;
|
||||
while (element != null && element.GetType() != typeof(TreeView))
|
||||
{
|
||||
element = (UIElement)VisualTreeHelper.GetParent(element);
|
||||
if (element is TreeViewItem)
|
||||
left += 19.0;
|
||||
}
|
||||
return new Thickness(left, 0, 0, 0);
|
||||
}
|
||||
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Windows.Media;
|
||||
|
||||
|
||||
namespace HandyControl.Tools.Extension
|
||||
{
|
||||
/// <summary>
|
||||
/// 颜色扩展类
|
||||
/// </summary>
|
||||
public static class ColorExtension
|
||||
{
|
||||
/// <summary>
|
||||
/// 将颜色转换为10进制表示(rgb顺序颠倒)
|
||||
/// </summary>
|
||||
/// <param name="color"></param>
|
||||
/// <returns></returns>
|
||||
public static int ToInt32(this Color color) => color.R << 16 | color.G << 8 | color.B;
|
||||
|
||||
/// <summary>
|
||||
/// 将颜色转换为10进制表示(rgb顺序颠倒)
|
||||
/// </summary>
|
||||
/// <param name="color"></param>
|
||||
/// <returns></returns>
|
||||
public static int ToInt32Reverse(this Color color) => color.R | color.G << 8 | color.B << 18;
|
||||
|
||||
internal static List<byte> ToList(this Color color) => new List<byte>
|
||||
{
|
||||
color.R,
|
||||
color.G,
|
||||
color.B
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using HandyControl.Controls;
|
||||
|
||||
namespace HandyControl.Tools.Extension
|
||||
{
|
||||
public interface IDialogResultable<T>
|
||||
{
|
||||
T Result { get; set; }
|
||||
|
||||
Action CloseAction { get; set; }
|
||||
}
|
||||
|
||||
public static class DialogExtension
|
||||
{
|
||||
public static Task<TResult> GetResultAsync<TResult>(this Dialog dialog)
|
||||
{
|
||||
var tcs = new TaskCompletionSource<TResult>();
|
||||
|
||||
try
|
||||
{
|
||||
if (dialog.IsClosed)
|
||||
{
|
||||
SetResult();
|
||||
}
|
||||
else
|
||||
{
|
||||
dialog.Unloaded += OnUnloaded;
|
||||
dialog.GetViewModel<IDialogResultable<TResult>>().CloseAction = dialog.Close;
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
tcs.TrySetException(e);
|
||||
}
|
||||
|
||||
return tcs.Task;
|
||||
|
||||
// ReSharper disable once ImplicitlyCapturedClosure
|
||||
void OnUnloaded(object sender, RoutedEventArgs args)
|
||||
{
|
||||
dialog.Unloaded -= OnUnloaded;
|
||||
SetResult();
|
||||
}
|
||||
|
||||
void SetResult()
|
||||
{
|
||||
try
|
||||
{
|
||||
tcs.TrySetResult(dialog.GetViewModel<IDialogResultable<TResult>>().Result);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
tcs.TrySetException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static Dialog Initialize<TViewModel>(this Dialog dialog, Action<TViewModel> configure)
|
||||
{
|
||||
configure?.Invoke(dialog.GetViewModel<TViewModel>());
|
||||
|
||||
return dialog;
|
||||
}
|
||||
|
||||
public static TViewModel GetViewModel<TViewModel>(this Dialog dialog)
|
||||
{
|
||||
if (!(dialog.Content is FrameworkElement frameworkElement))
|
||||
throw new InvalidOperationException("The dialog is not a derived class of the FrameworkElement. ");
|
||||
|
||||
if (!(frameworkElement.DataContext is TViewModel viewModel))
|
||||
throw new InvalidOperationException($"The view model of the dialog is not the {typeof(TViewModel)} type or its derived class. ");
|
||||
|
||||
return viewModel;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace HandyControl.Tools.Extension
|
||||
{
|
||||
public static class EnumerableExtension
|
||||
{
|
||||
public static IEnumerable<TSource> Do<TSource>(this IEnumerable<TSource> source, Action<TSource> predicate)
|
||||
{
|
||||
var enumerable = source as IList<TSource> ?? source.ToList();
|
||||
foreach (var item in enumerable)
|
||||
{
|
||||
predicate.Invoke(item);
|
||||
}
|
||||
|
||||
return enumerable;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
using System;
|
||||
using System.Windows;
|
||||
using System.Windows.Media;
|
||||
using HandyControl.Expression.Drawing;
|
||||
|
||||
namespace HandyControl.Tools.Extension
|
||||
{
|
||||
public static class GeometryExtension
|
||||
{
|
||||
/// <summary>
|
||||
/// 获取路径总长度
|
||||
/// </summary>
|
||||
/// <param name="geometry"></param>
|
||||
/// <returns></returns>
|
||||
public static double GetTotalLength(this Geometry geometry)
|
||||
{
|
||||
if (geometry == null) return 0;
|
||||
|
||||
var pathGeometry = PathGeometry.CreateFromGeometry(geometry);
|
||||
pathGeometry.GetPointAtFractionLength(1e-4, out var point, out _);
|
||||
var length = (pathGeometry.Figures[0].StartPoint - point).Length * 1e+4;
|
||||
|
||||
return length;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取路径总长度
|
||||
/// </summary>
|
||||
/// <param name="geometry"></param>
|
||||
/// <param name="size"></param>
|
||||
/// <param name="strokeThickness"></param>
|
||||
/// <returns></returns>
|
||||
public static double GetTotalLength(this Geometry geometry, Size size, double strokeThickness = 1)
|
||||
{
|
||||
if (geometry == null) return 0;
|
||||
|
||||
if (MathHelper.IsVerySmall(size.Width) || MathHelper.IsVerySmall(size.Height)) return 0;
|
||||
|
||||
var length = GetTotalLength(geometry);
|
||||
var sw = geometry.Bounds.Width / size.Width;
|
||||
var sh = geometry.Bounds.Height / size.Height;
|
||||
var min = Math.Min(sw, sh);
|
||||
|
||||
if (MathHelper.IsVerySmall(min) || MathHelper.IsVerySmall(strokeThickness)) return 0;
|
||||
|
||||
length /= min;
|
||||
return length / strokeThickness;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
using System.Windows;
|
||||
|
||||
namespace HandyControl.Tools.Extension
|
||||
{
|
||||
// ReSharper disable once InconsistentNaming
|
||||
public static class UIElementExtension
|
||||
{
|
||||
/// <summary>
|
||||
/// 显示元素
|
||||
/// </summary>
|
||||
/// <param name="element"></param>
|
||||
public static void Show(this UIElement element)
|
||||
{
|
||||
element.Visibility = Visibility.Visible;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 显示元素
|
||||
/// </summary>
|
||||
/// <param name="element"></param>
|
||||
/// <param name="show"></param>
|
||||
public static void Show(this UIElement element, bool show)
|
||||
{
|
||||
element.Visibility = show ? Visibility.Visible : Visibility.Collapsed;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 不现实元素,但保留空间
|
||||
/// </summary>
|
||||
/// <param name="element"></param>
|
||||
public static void Hide(this UIElement element)
|
||||
{
|
||||
element.Visibility = Visibility.Hidden;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 不显示元素,且不保留空间
|
||||
/// </summary>
|
||||
/// <param name="element"></param>
|
||||
public static void Collapse(this UIElement element)
|
||||
{
|
||||
element.Visibility = Visibility.Collapsed;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,271 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using HandyControl.Controls;
|
||||
|
||||
namespace HandyControl.Data
|
||||
{
|
||||
internal class ImageAnimator
|
||||
{
|
||||
private static List<GifImageInfo> ImageInfoList;
|
||||
|
||||
private static readonly ReaderWriterLock RwImgListLock = new ReaderWriterLock();
|
||||
|
||||
private static Thread AnimationThread;
|
||||
|
||||
private static bool AnyFrameDirty;
|
||||
|
||||
[ThreadStatic]
|
||||
private static int ThreadWriterLockWaitCount;
|
||||
|
||||
public static bool CanAnimate(GifImage image)
|
||||
{
|
||||
if (image == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
lock (image)
|
||||
{
|
||||
var dimensions = image.FrameDimensionsList;
|
||||
|
||||
if (dimensions.Select(guid => new GifFrameDimension(guid)).Contains(GifFrameDimension.Time))
|
||||
{
|
||||
return image.GetFrameCount(GifFrameDimension.Time) > 1;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static void Animate(GifImage image, EventHandler onFrameChangedHandler)
|
||||
{
|
||||
if (image == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
GifImageInfo imageInfo;
|
||||
|
||||
// See comment in the class header about locking the image ref.
|
||||
lock (image)
|
||||
{
|
||||
// could we avoid creating an ImageInfo object if FrameCount == 1 ?
|
||||
imageInfo = new GifImageInfo(image);
|
||||
}
|
||||
|
||||
// If the image is already animating, stop animating it
|
||||
StopAnimate(image, onFrameChangedHandler);
|
||||
|
||||
// Acquire a writer lock to modify the image info list. If the thread has a reader lock we need to upgrade
|
||||
// it to a writer lock; acquiring a reader lock in this case would block the thread on itself.
|
||||
// If the thread already has a writer lock its ref count will be incremented w/o placing the request in the
|
||||
// writer queue. See ReaderWriterLock.AcquireWriterLock method in the MSDN.
|
||||
|
||||
var readerLockHeld = RwImgListLock.IsReaderLockHeld;
|
||||
var lockDowngradeCookie = new LockCookie();
|
||||
|
||||
ThreadWriterLockWaitCount++;
|
||||
|
||||
try
|
||||
{
|
||||
if (readerLockHeld)
|
||||
{
|
||||
lockDowngradeCookie = RwImgListLock.UpgradeToWriterLock(Timeout.Infinite);
|
||||
}
|
||||
else
|
||||
{
|
||||
RwImgListLock.AcquireWriterLock(Timeout.Infinite);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
ThreadWriterLockWaitCount--;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
if (imageInfo.Animated)
|
||||
{
|
||||
// Construct the image array
|
||||
//
|
||||
if (ImageInfoList == null)
|
||||
{
|
||||
ImageInfoList = new List<GifImageInfo>();
|
||||
}
|
||||
|
||||
// Add the new image
|
||||
//
|
||||
imageInfo.FrameChangedHandler = onFrameChangedHandler;
|
||||
ImageInfoList.Add(imageInfo);
|
||||
|
||||
// Construct a new timer thread if we haven't already
|
||||
//
|
||||
if (AnimationThread == null)
|
||||
{
|
||||
AnimationThread = new Thread(AnimateImages50Ms)
|
||||
{
|
||||
Name = typeof(ImageAnimator).Name,
|
||||
IsBackground = true
|
||||
};
|
||||
AnimationThread.Start();
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (readerLockHeld)
|
||||
{
|
||||
RwImgListLock.DowngradeFromWriterLock(ref lockDowngradeCookie);
|
||||
}
|
||||
else
|
||||
{
|
||||
RwImgListLock.ReleaseWriterLock();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void StopAnimate(GifImage image, EventHandler onFrameChangedHandler)
|
||||
{
|
||||
// Make sure we have a list of images
|
||||
if (image == null || ImageInfoList == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Acquire a writer lock to modify the image info list - See comments on Animate() about this locking.
|
||||
|
||||
var readerLockHeld = RwImgListLock.IsReaderLockHeld;
|
||||
var lockDowngradeCookie = new LockCookie();
|
||||
|
||||
ThreadWriterLockWaitCount++;
|
||||
|
||||
try
|
||||
{
|
||||
if (readerLockHeld)
|
||||
{
|
||||
lockDowngradeCookie = RwImgListLock.UpgradeToWriterLock(Timeout.Infinite);
|
||||
}
|
||||
else
|
||||
{
|
||||
RwImgListLock.AcquireWriterLock(Timeout.Infinite);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
ThreadWriterLockWaitCount--;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// Find the corresponding reference and remove it
|
||||
for (int i = 0; i < ImageInfoList.Count; i++)
|
||||
{
|
||||
var imageInfo = ImageInfoList[i];
|
||||
|
||||
if (Equals(image, imageInfo.Image))
|
||||
{
|
||||
if ((onFrameChangedHandler == imageInfo.FrameChangedHandler) || (onFrameChangedHandler != null && onFrameChangedHandler.Equals(imageInfo.FrameChangedHandler)))
|
||||
{
|
||||
ImageInfoList.Remove(imageInfo);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (readerLockHeld)
|
||||
{
|
||||
RwImgListLock.DowngradeFromWriterLock(ref lockDowngradeCookie);
|
||||
}
|
||||
else
|
||||
{
|
||||
RwImgListLock.ReleaseWriterLock();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void UpdateFrames()
|
||||
{
|
||||
if (!AnyFrameDirty || ImageInfoList == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (ThreadWriterLockWaitCount > 0)
|
||||
{
|
||||
// Cannot acquire reader lock at this time, frames update will be missed.
|
||||
return;
|
||||
}
|
||||
|
||||
RwImgListLock.AcquireReaderLock(Timeout.Infinite);
|
||||
|
||||
try
|
||||
{
|
||||
foreach (var imageInfo in ImageInfoList)
|
||||
{
|
||||
// See comment in the class header about locking the image ref.
|
||||
lock (imageInfo.Image)
|
||||
{
|
||||
imageInfo.UpdateFrame();
|
||||
}
|
||||
}
|
||||
AnyFrameDirty = false;
|
||||
}
|
||||
finally
|
||||
{
|
||||
RwImgListLock.ReleaseReaderLock();
|
||||
}
|
||||
}
|
||||
|
||||
[SuppressMessage("Microsoft.Performance", "CA1804:RemoveUnusedLocals")]
|
||||
private static void AnimateImages50Ms()
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
// Acquire reader-lock to access imageInfoList, elemens in the list can be modified w/o needing a writer-lock.
|
||||
// Observe that we don't need to check if the thread is waiting or a writer lock here since the thread this
|
||||
// method runs in never acquires a writer lock.
|
||||
RwImgListLock.AcquireReaderLock(Timeout.Infinite);
|
||||
try
|
||||
{
|
||||
foreach (var imageInfo in ImageInfoList)
|
||||
{
|
||||
// Frame delay is measured in 1/100ths of a second. This thread
|
||||
// sleeps for 50 ms = 5/100ths of a second between frame updates,
|
||||
// so we increase the frame delay count 5/100ths of a second
|
||||
// at a time.
|
||||
//
|
||||
imageInfo.FrameTimer += 5;
|
||||
if (imageInfo.FrameTimer >= imageInfo.FrameDelay(imageInfo.Frame))
|
||||
{
|
||||
imageInfo.FrameTimer = 0;
|
||||
|
||||
if (imageInfo.Frame + 1 < imageInfo.FrameCount)
|
||||
{
|
||||
imageInfo.Frame++;
|
||||
}
|
||||
else
|
||||
{
|
||||
imageInfo.Frame = 0;
|
||||
}
|
||||
|
||||
if (imageInfo.FrameDirty)
|
||||
{
|
||||
AnyFrameDirty = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
RwImgListLock.ReleaseReaderLock();
|
||||
}
|
||||
|
||||
Thread.Sleep(50);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using System.Windows;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Shapes;
|
||||
using HandyControl.Controls;
|
||||
|
||||
namespace HandyControl.Tools
|
||||
{
|
||||
public class GithubGravatarGenerator : IGravatarGenerator
|
||||
{
|
||||
private const int RenderDataMaxLength = 15;
|
||||
|
||||
public object GetGravatar(string id)
|
||||
{
|
||||
var hashcode = GetHashCode(id);
|
||||
var renderData = GetRenderData(hashcode);
|
||||
var renderBrush = GetRenderBrush(hashcode);
|
||||
var geometryGroup = new GeometryGroup();
|
||||
|
||||
void AddRec(int i, int j, bool hidden = false)
|
||||
{
|
||||
var rec = new RectangleGeometry(new Rect(new Point(i, j), hidden ? new Size() : new Size(1, 1)));
|
||||
geometryGroup.Children.Add(rec);
|
||||
}
|
||||
|
||||
var index = 0;
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
for (int j = 0; j < 5; j++, index++)
|
||||
{
|
||||
AddRec(i, j, renderData[index] == 0);
|
||||
}
|
||||
}
|
||||
|
||||
for (int j = 0; j < 5; j++, index++)
|
||||
{
|
||||
AddRec(2, j, renderData[index] == 0);
|
||||
}
|
||||
|
||||
index -= 10;
|
||||
for (int i = 3; i < 5; i++)
|
||||
{
|
||||
for (int j = 0; j < 5; j++, index++)
|
||||
{
|
||||
AddRec(i, j, renderData[index] == 0);
|
||||
}
|
||||
index -= 10;
|
||||
}
|
||||
|
||||
var path = new Path
|
||||
{
|
||||
Data = geometryGroup,
|
||||
Fill = renderBrush,
|
||||
VerticalAlignment = VerticalAlignment.Top
|
||||
};
|
||||
|
||||
RenderOptions.SetEdgeMode(path, EdgeMode.Aliased);
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
private string GetHashCode(string id)
|
||||
{
|
||||
var md5 = MD5.Create();
|
||||
var bytes = Encoding.ASCII.GetBytes(id);
|
||||
var hash = md5.ComputeHash(bytes);
|
||||
var stringBuilder = new StringBuilder();
|
||||
foreach (var item in hash)
|
||||
{
|
||||
stringBuilder.Append(item.ToString("X2"));
|
||||
}
|
||||
return stringBuilder.ToString();
|
||||
}
|
||||
|
||||
private int[] GetRenderData(string hashcode)
|
||||
{
|
||||
var arr = new int[RenderDataMaxLength];
|
||||
for (int i = 0; i < RenderDataMaxLength; i++)
|
||||
{
|
||||
var c = hashcode[i];
|
||||
var v = (int) c;
|
||||
arr[i] = v % 2;
|
||||
}
|
||||
|
||||
return arr;
|
||||
}
|
||||
|
||||
private Brush GetRenderBrush(string hashcode)
|
||||
{
|
||||
var v = (double)int.Parse(hashcode.Substring(hashcode.Length - 7), NumberStyles.HexNumber);
|
||||
var scale = v / 0xfffffff;
|
||||
var color = Hsl2Rgb(scale);
|
||||
return new SolidColorBrush(color);
|
||||
}
|
||||
|
||||
//adapted from https://github.com/stewartlord/identicon.js/blob/4fad7cafec1b7a4d896015e084e861625ef5d64f/identicon.js#L110
|
||||
private Color Hsl2Rgb(double h, double s = 0.7, double b = 0.5)
|
||||
{
|
||||
h *= 6;
|
||||
var arr = new[]
|
||||
{
|
||||
b += s *= b < .5 ? b : 1 - b,
|
||||
b - h % 1 * s * 2,
|
||||
b -= s *= 2,
|
||||
b,
|
||||
b + h % 1 * s,
|
||||
b + s
|
||||
};
|
||||
|
||||
var hValue = (int) Math.Floor(h);
|
||||
|
||||
return Color.FromRgb((byte) (arr[hValue % 6] * 255), (byte) (arr[(hValue | 16) % 6] * 255),
|
||||
(byte) (arr[(hValue | 8) % 6] * 255));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
using System;
|
||||
using System.Windows;
|
||||
using System.Windows.Media.Animation;
|
||||
|
||||
namespace HandyControl.Tools
|
||||
{
|
||||
/// <summary>
|
||||
/// 包含一些常用的动画辅助方法
|
||||
/// </summary>
|
||||
public class AnimationHelper
|
||||
{
|
||||
/// <summary>
|
||||
/// 创建一个Thickness动画
|
||||
/// </summary>
|
||||
/// <param name="thickness"></param>
|
||||
/// <param name="milliseconds"></param>
|
||||
/// <returns></returns>
|
||||
public static ThicknessAnimation CreateAnimation(Thickness thickness = default(Thickness), double milliseconds = 200)
|
||||
{
|
||||
return new ThicknessAnimation(thickness, new Duration(TimeSpan.FromMilliseconds(milliseconds)))
|
||||
{
|
||||
EasingFunction = new PowerEase { EasingMode = EasingMode.EaseInOut }
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建一个Double动画
|
||||
/// </summary>
|
||||
/// <param name="toValue"></param>
|
||||
/// <param name="milliseconds"></param>
|
||||
/// <returns></returns>
|
||||
public static DoubleAnimation CreateAnimation(double toValue, double milliseconds = 200)
|
||||
{
|
||||
return new DoubleAnimation(toValue, new Duration(TimeSpan.FromMilliseconds(milliseconds)))
|
||||
{
|
||||
EasingFunction = new PowerEase { EasingMode = EasingMode.EaseInOut }
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,152 @@
|
||||
using System;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Controls.Primitives;
|
||||
using System.Windows.Media.Media3D;
|
||||
|
||||
namespace HandyControl.Tools
|
||||
{
|
||||
/// <summary>
|
||||
/// 包含内部使用的一些简单算法
|
||||
/// </summary>
|
||||
internal class ArithmeticHelper
|
||||
{
|
||||
/// <summary>
|
||||
/// 平分一个整数到一个数组中
|
||||
/// </summary>
|
||||
/// <param name="num"></param>
|
||||
/// <param name="count"></param>
|
||||
/// <returns></returns>
|
||||
public static int[] DivideInt2Arr(int num, int count)
|
||||
{
|
||||
var arr = new int[count];
|
||||
var div = num / count;
|
||||
var rest = num % count;
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
arr[i] = div;
|
||||
}
|
||||
for (int i = 0; i < rest; i++)
|
||||
{
|
||||
arr[i] += 1;
|
||||
}
|
||||
return arr;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 计算控件在窗口中的可见坐标
|
||||
/// </summary>
|
||||
public static Point CalSafePoint(FrameworkElement element, FrameworkElement showElement, Thickness thickness = default(Thickness))
|
||||
{
|
||||
if (element == null || showElement == null) return default(Point);
|
||||
var point = element.PointToScreen(new Point(0, 0));
|
||||
|
||||
if (point.X < 0) point.X = 0;
|
||||
if (point.Y < 0) point.Y = 0;
|
||||
|
||||
var maxLeft = SystemParameters.WorkArea.Width -
|
||||
((double.IsNaN(showElement.Width) ? showElement.ActualWidth : showElement.Width) +
|
||||
thickness.Left + thickness.Right);
|
||||
var maxTop = SystemParameters.WorkArea.Height -
|
||||
((double.IsNaN(showElement.Height) ? showElement.ActualHeight : showElement.Height) +
|
||||
thickness.Top + thickness.Bottom);
|
||||
return new Point(maxLeft > point.X ? point.X : maxLeft, maxTop > point.Y ? point.Y : maxTop);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取布局范围框
|
||||
/// </summary>
|
||||
/// <param name="element"></param>
|
||||
/// <returns></returns>
|
||||
public static Rect GetLayoutRect(FrameworkElement element)
|
||||
{
|
||||
var num1 = element.ActualWidth;
|
||||
var num2 = element.ActualHeight;
|
||||
if (element is Image || element is MediaElement)
|
||||
if (element.Parent is Canvas)
|
||||
{
|
||||
num1 = double.IsNaN(element.Width) ? num1 : element.Width;
|
||||
num2 = double.IsNaN(element.Height) ? num2 : element.Height;
|
||||
}
|
||||
else
|
||||
{
|
||||
num1 = element.RenderSize.Width;
|
||||
num2 = element.RenderSize.Height;
|
||||
}
|
||||
var width = element.Visibility == Visibility.Collapsed ? 0.0 : num1;
|
||||
var height = element.Visibility == Visibility.Collapsed ? 0.0 : num2;
|
||||
var margin = element.Margin;
|
||||
var layoutSlot = LayoutInformation.GetLayoutSlot(element);
|
||||
var x = 0.0;
|
||||
var y = 0.0;
|
||||
switch (element.HorizontalAlignment)
|
||||
{
|
||||
case HorizontalAlignment.Left:
|
||||
x = layoutSlot.Left + margin.Left;
|
||||
break;
|
||||
case HorizontalAlignment.Center:
|
||||
x = (layoutSlot.Left + margin.Left + layoutSlot.Right - margin.Right) / 2.0 - width / 2.0;
|
||||
break;
|
||||
case HorizontalAlignment.Right:
|
||||
x = layoutSlot.Right - margin.Right - width;
|
||||
break;
|
||||
case HorizontalAlignment.Stretch:
|
||||
x = Math.Max(layoutSlot.Left + margin.Left,
|
||||
(layoutSlot.Left + margin.Left + layoutSlot.Right - margin.Right) / 2.0 - width / 2.0);
|
||||
break;
|
||||
}
|
||||
switch (element.VerticalAlignment)
|
||||
{
|
||||
case VerticalAlignment.Top:
|
||||
y = layoutSlot.Top + margin.Top;
|
||||
break;
|
||||
case VerticalAlignment.Center:
|
||||
y = (layoutSlot.Top + margin.Top + layoutSlot.Bottom - margin.Bottom) / 2.0 - height / 2.0;
|
||||
break;
|
||||
case VerticalAlignment.Bottom:
|
||||
y = layoutSlot.Bottom - margin.Bottom - height;
|
||||
break;
|
||||
case VerticalAlignment.Stretch:
|
||||
y = Math.Max(layoutSlot.Top + margin.Top,
|
||||
(layoutSlot.Top + margin.Top + layoutSlot.Bottom - margin.Bottom) / 2.0 - height / 2.0);
|
||||
break;
|
||||
}
|
||||
return new Rect(x, y, width, height);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 计算两点的连线和x轴的夹角
|
||||
/// </summary>
|
||||
/// <param name="center"></param>
|
||||
/// <param name="p"></param>
|
||||
/// <returns></returns>
|
||||
public static double CalAngle(Point center, Point p) => Math.Atan2(p.Y - center.Y, p.X - center.X) * 180 / Math.PI;
|
||||
|
||||
/// <summary>
|
||||
/// 判断是否是有效的双精度浮点数
|
||||
/// </summary>
|
||||
/// <param name="value"></param>
|
||||
/// <returns></returns>
|
||||
public static bool IsValidDoubleValue(object value)
|
||||
{
|
||||
var d = (double)value;
|
||||
if (!double.IsNaN(d))
|
||||
return !double.IsInfinity(d);
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 计算法线
|
||||
/// </summary>
|
||||
/// <param name="p0"></param>
|
||||
/// <param name="p1"></param>
|
||||
/// <param name="p2"></param>
|
||||
/// <returns></returns>
|
||||
public static Vector3D CalNormal(Point3D p0, Point3D p1, Point3D p2)
|
||||
{
|
||||
var v0 = new Vector3D(p1.X - p0.X, p1.Y - p0.Y, p1.Z - p0.Z);
|
||||
var v1 = new Vector3D(p2.X - p1.X, p2.Y - p1.Y, p2.Z - p1.Z);
|
||||
return Vector3D.CrossProduct(v0, v1);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
using System;
|
||||
using System.Windows.Media;
|
||||
|
||||
namespace HandyControl.Tools
|
||||
{
|
||||
public class ColorHelper
|
||||
{
|
||||
private const int Win32RedShift = 0;
|
||||
private const int Win32GreenShift = 8;
|
||||
private const int Win32BlueShift = 16;
|
||||
|
||||
public static int ToWin32(Color c) => c.R << Win32RedShift | c.G << Win32GreenShift | c.B << Win32BlueShift;
|
||||
|
||||
public static Color ToColor(uint c)
|
||||
{
|
||||
var bytes = BitConverter.GetBytes(c);
|
||||
return Color.FromArgb(bytes[3], bytes[2], bytes[1], bytes[0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
using System;
|
||||
using System.Text;
|
||||
|
||||
namespace HandyControl.Tools
|
||||
{
|
||||
internal class CreateParams
|
||||
{
|
||||
public string ClassName { get; set; }
|
||||
|
||||
public string Caption { get; set; }
|
||||
|
||||
public int Style { get; set; }
|
||||
|
||||
public int ExStyle{ get; set; }
|
||||
|
||||
public int ClassStyle { get; set; }
|
||||
|
||||
public int X { get; set; }
|
||||
|
||||
public int Y { get; set; }
|
||||
|
||||
public int Width { get; set; }
|
||||
|
||||
public int Height { get; set; }
|
||||
|
||||
public IntPtr Parent { get; set; }
|
||||
|
||||
public object Param { get; set; }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
var sb = new StringBuilder(64);
|
||||
sb.Append("CreateParams {'");
|
||||
sb.Append(ClassName);
|
||||
sb.Append("', '");
|
||||
sb.Append(Caption);
|
||||
sb.Append("', 0x");
|
||||
sb.Append(Convert.ToString(Style, 16));
|
||||
sb.Append(", 0x");
|
||||
sb.Append(Convert.ToString(ExStyle, 16));
|
||||
sb.Append(", {");
|
||||
sb.Append(X);
|
||||
sb.Append(", ");
|
||||
sb.Append(Y);
|
||||
sb.Append(", ");
|
||||
sb.Append(Width);
|
||||
sb.Append(", ");
|
||||
sb.Append(Height);
|
||||
sb.Append("}");
|
||||
sb.Append("}");
|
||||
return sb.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
using System.ComponentModel;
|
||||
using System.Windows;
|
||||
|
||||
namespace HandyControl.Tools
|
||||
{
|
||||
public class DesignerHelper
|
||||
{
|
||||
private static bool? _isInDesignMode;
|
||||
|
||||
public static bool IsInDesignMode
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!_isInDesignMode.HasValue)
|
||||
{
|
||||
_isInDesignMode = (bool)DependencyPropertyDescriptor.FromProperty(DesignerProperties.IsInDesignModeProperty, typeof(FrameworkElement)).Metadata.DefaultValue;
|
||||
}
|
||||
return _isInDesignMode.Value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,362 @@
|
||||
using System;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.IO;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.ConstrainedExecution;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Runtime.Versioning;
|
||||
using System.Security.Permissions;
|
||||
using System.Threading;
|
||||
|
||||
namespace HandyControl.Tools
|
||||
{
|
||||
[SuppressMessage("ReSharper", "InconsistentNaming")]
|
||||
internal class ExternDllHelper
|
||||
{
|
||||
private const string Gdi32 = "gdi32.dll";
|
||||
|
||||
private const string User32 = "user32.dll";
|
||||
|
||||
public const int E_FAIL = unchecked((int) 0x80004005);
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
internal struct WINCOMPATTRDATA
|
||||
{
|
||||
public WINDOWCOMPOSITIONATTRIB Attribute;
|
||||
public IntPtr Data;
|
||||
public int DataSize;
|
||||
}
|
||||
|
||||
internal enum WINDOWCOMPOSITIONATTRIB
|
||||
{
|
||||
WCA_ACCENT_POLICY = 19
|
||||
}
|
||||
|
||||
internal enum ACCENTSTATE
|
||||
{
|
||||
ACCENT_DISABLED = 0,
|
||||
ACCENT_ENABLE_GRADIENT = 1,
|
||||
ACCENT_ENABLE_TRANSPARENTGRADIENT = 2,
|
||||
ACCENT_ENABLE_BLURBEHIND = 3,
|
||||
ACCENT_ENABLE_ACRYLICBLURBEHIND = 4,
|
||||
ACCENT_INVALID_STATE = 5
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
internal struct ACCENTPOLICY
|
||||
{
|
||||
public ACCENTSTATE AccentState;
|
||||
public int AccentFlags;
|
||||
public uint GradientColor;
|
||||
public int AnimationId;
|
||||
}
|
||||
|
||||
[DllImport(User32)]
|
||||
internal static extern int SetWindowCompositionAttribute(IntPtr hwnd, ref WINCOMPATTRDATA data);
|
||||
|
||||
[ReflectionPermission(SecurityAction.Assert, Unrestricted = true), SecurityPermission(SecurityAction.Assert, Flags = SecurityPermissionFlag.UnmanagedCode)]
|
||||
public static object PtrToStructure(IntPtr lparam, Type cls) => Marshal.PtrToStructure(lparam, cls);
|
||||
|
||||
[DllImport(Gdi32, CharSet = CharSet.Auto, SetLastError = true)]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
public static extern bool DeleteObject(IntPtr hObject);
|
||||
|
||||
[ComImport, Guid("0000000C-0000-0000-C000-000000000046"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
|
||||
public interface IStream
|
||||
{
|
||||
int Read(
|
||||
[In]
|
||||
IntPtr buf,
|
||||
[In]
|
||||
int len);
|
||||
|
||||
int Write(
|
||||
[In]
|
||||
IntPtr buf,
|
||||
[In]
|
||||
int len);
|
||||
|
||||
[return: MarshalAs(UnmanagedType.I8)]
|
||||
long Seek(
|
||||
[In, MarshalAs(UnmanagedType.I8)]
|
||||
long dlibMove,
|
||||
[In]
|
||||
int dwOrigin);
|
||||
|
||||
void SetSize(
|
||||
[In, MarshalAs(UnmanagedType.I8)]
|
||||
long libNewSize);
|
||||
|
||||
[return: MarshalAs(UnmanagedType.I8)]
|
||||
long CopyTo(
|
||||
[In, MarshalAs(UnmanagedType.Interface)]
|
||||
IStream pstm,
|
||||
[In, MarshalAs(UnmanagedType.I8)]
|
||||
long cb,
|
||||
[Out, MarshalAs(UnmanagedType.LPArray)]
|
||||
long[] pcbRead);
|
||||
|
||||
void Commit(
|
||||
[In]
|
||||
int grfCommitFlags);
|
||||
|
||||
void Revert();
|
||||
|
||||
void LockRegion(
|
||||
[In, MarshalAs(UnmanagedType.I8)]
|
||||
long libOffset,
|
||||
[In, MarshalAs(UnmanagedType.I8)]
|
||||
long cb,
|
||||
[In]
|
||||
int dwLockType);
|
||||
|
||||
void UnlockRegion(
|
||||
[In, MarshalAs(UnmanagedType.I8)]
|
||||
long libOffset,
|
||||
[In, MarshalAs(UnmanagedType.I8)]
|
||||
long cb,
|
||||
[In]
|
||||
int dwLockType);
|
||||
|
||||
void Stat(
|
||||
[In]
|
||||
IntPtr pStatstg,
|
||||
[In]
|
||||
int grfStatFlag);
|
||||
|
||||
[return: MarshalAs(UnmanagedType.Interface)]
|
||||
IStream Clone();
|
||||
}
|
||||
|
||||
[SuppressMessage("ReSharper", "InconsistentNaming")]
|
||||
public class StreamConsts
|
||||
{
|
||||
public const int LOCK_WRITE = 0x1;
|
||||
public const int LOCK_EXCLUSIVE = 0x2;
|
||||
public const int LOCK_ONLYONCE = 0x4;
|
||||
public const int STATFLAG_DEFAULT = 0x0;
|
||||
public const int STATFLAG_NONAME = 0x1;
|
||||
public const int STATFLAG_NOOPEN = 0x2;
|
||||
public const int STGC_DEFAULT = 0x0;
|
||||
public const int STGC_OVERWRITE = 0x1;
|
||||
public const int STGC_ONLYIFCURRENT = 0x2;
|
||||
public const int STGC_DANGEROUSLYCOMMITMERELYTODISKCACHE = 0x4;
|
||||
public const int STREAM_SEEK_SET = 0x0;
|
||||
public const int STREAM_SEEK_CUR = 0x1;
|
||||
public const int STREAM_SEEK_END = 0x2;
|
||||
}
|
||||
|
||||
internal class Gdip
|
||||
{
|
||||
private const string Gdiplus = "gdiplus.dll";
|
||||
|
||||
private const string ThreadDataSlotName = "system.drawing.threaddata";
|
||||
|
||||
private static IntPtr InitToken;
|
||||
|
||||
private static bool Initialized => InitToken != IntPtr.Zero;
|
||||
|
||||
static Gdip()
|
||||
{
|
||||
Initialize();
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
private struct StartupInput
|
||||
{
|
||||
private int GdiplusVersion;
|
||||
|
||||
private readonly IntPtr DebugEventCallback;
|
||||
|
||||
private bool SuppressBackgroundThread;
|
||||
|
||||
private bool SuppressExternalCodecs;
|
||||
|
||||
public static StartupInput GetDefault()
|
||||
{
|
||||
var result = new StartupInput
|
||||
{
|
||||
GdiplusVersion = 1,
|
||||
SuppressBackgroundThread = false,
|
||||
SuppressExternalCodecs = false
|
||||
};
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
private struct StartupOutput
|
||||
{
|
||||
private readonly IntPtr hook;
|
||||
|
||||
private readonly IntPtr unhook;
|
||||
}
|
||||
|
||||
[ResourceExposure(ResourceScope.None)]
|
||||
[ResourceConsumption(ResourceScope.AppDomain, ResourceScope.AppDomain)]
|
||||
[SuppressMessage("Microsoft.Performance", "CA1804:RemoveUnusedLocals")]
|
||||
private static void Initialize()
|
||||
{
|
||||
var input = StartupInput.GetDefault();
|
||||
|
||||
var status = GdiplusStartup(out InitToken, ref input, out _);
|
||||
|
||||
if (status != Ok)
|
||||
{
|
||||
throw StatusException(status);
|
||||
}
|
||||
|
||||
var currentDomain = AppDomain.CurrentDomain;
|
||||
currentDomain.ProcessExit += OnProcessExit;
|
||||
|
||||
if (!currentDomain.IsDefaultAppDomain())
|
||||
{
|
||||
currentDomain.DomainUnload += OnProcessExit;
|
||||
}
|
||||
}
|
||||
|
||||
[PrePrepareMethod]
|
||||
[ResourceExposure(ResourceScope.AppDomain)]
|
||||
[ResourceConsumption(ResourceScope.AppDomain)]
|
||||
private static void OnProcessExit(object sender, EventArgs e) => Shutdown();
|
||||
|
||||
[SuppressMessage("Microsoft.Reliability", "CA2001:AvoidCallingProblematicMethods")]
|
||||
[ResourceExposure(ResourceScope.AppDomain)]
|
||||
[ResourceConsumption(ResourceScope.AppDomain)]
|
||||
private static void Shutdown()
|
||||
{
|
||||
if (Initialized)
|
||||
{
|
||||
ClearThreadData();
|
||||
// unhook our shutdown handlers as we do not need to shut down more than once
|
||||
var currentDomain = AppDomain.CurrentDomain;
|
||||
currentDomain.ProcessExit -= OnProcessExit;
|
||||
if (!currentDomain.IsDefaultAppDomain())
|
||||
{
|
||||
currentDomain.DomainUnload -= OnProcessExit;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
private static void ClearThreadData()
|
||||
{
|
||||
var slot = Thread.GetNamedDataSlot(ThreadDataSlotName);
|
||||
Thread.SetData(slot, null);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------
|
||||
// Status codes
|
||||
//----------------------------------------------------------------------------------------
|
||||
internal const int Ok = 0;
|
||||
internal const int GenericError = 1;
|
||||
internal const int InvalidParameter = 2;
|
||||
internal const int OutOfMemory = 3;
|
||||
internal const int ObjectBusy = 4;
|
||||
internal const int InsufficientBuffer = 5;
|
||||
internal const int NotImplemented = 6;
|
||||
internal const int Win32Error = 7;
|
||||
internal const int WrongState = 8;
|
||||
internal const int Aborted = 9;
|
||||
internal const int FileNotFound = 10;
|
||||
internal const int ValueOverflow = 11;
|
||||
internal const int AccessDenied = 12;
|
||||
internal const int UnknownImageFormat = 13;
|
||||
internal const int FontFamilyNotFound = 14;
|
||||
internal const int FontStyleNotFound = 15;
|
||||
internal const int NotTrueTypeFont = 16;
|
||||
internal const int UnsupportedGdiplusVersion = 17;
|
||||
internal const int GdiplusNotInitialized = 18;
|
||||
internal const int PropertyNotFound = 19;
|
||||
internal const int PropertyNotSupported = 20;
|
||||
|
||||
[DllImport(Gdiplus, SetLastError = true, ExactSpelling = true, CharSet = CharSet.Unicode)]
|
||||
[ResourceExposure(ResourceScope.None)]
|
||||
internal static extern int GdipImageGetFrameDimensionsCount(HandleRef image, out int count);
|
||||
|
||||
internal static Exception StatusException(int status)
|
||||
{
|
||||
switch (status)
|
||||
{
|
||||
case GenericError: return new ExternalException("GdiplusGenericError");
|
||||
case InvalidParameter: return new ArgumentException("GdiplusInvalidParameter");
|
||||
case OutOfMemory: return new OutOfMemoryException("GdiplusOutOfMemory");
|
||||
case ObjectBusy: return new InvalidOperationException("GdiplusObjectBusy");
|
||||
case InsufficientBuffer: return new OutOfMemoryException("GdiplusInsufficientBuffer");
|
||||
case NotImplemented: return new NotImplementedException("GdiplusNotImplemented");
|
||||
case Win32Error: return new ExternalException("GdiplusGenericError");
|
||||
case WrongState: return new InvalidOperationException("GdiplusWrongState");
|
||||
case Aborted: return new ExternalException("GdiplusAborted");
|
||||
case FileNotFound: return new FileNotFoundException("GdiplusFileNotFound");
|
||||
case ValueOverflow: return new OverflowException("GdiplusOverflow");
|
||||
case AccessDenied: return new ExternalException("GdiplusAccessDenied");
|
||||
case UnknownImageFormat: return new ArgumentException("GdiplusUnknownImageFormat");
|
||||
case PropertyNotFound: return new ArgumentException("GdiplusPropertyNotFoundError");
|
||||
case PropertyNotSupported: return new ArgumentException("GdiplusPropertyNotSupportedError");
|
||||
case FontFamilyNotFound: return new ArgumentException("GdiplusFontFamilyNotFound");
|
||||
case FontStyleNotFound: return new ArgumentException("GdiplusFontStyleNotFound");
|
||||
case NotTrueTypeFont: return new ArgumentException("GdiplusNotTrueTypeFont_NoName");
|
||||
case UnsupportedGdiplusVersion: return new ExternalException("GdiplusUnsupportedGdiplusVersion");
|
||||
case GdiplusNotInitialized: return new ExternalException("GdiplusNotInitialized");
|
||||
}
|
||||
|
||||
return new ExternalException("GdiplusUnknown");
|
||||
}
|
||||
|
||||
[DllImport(Gdiplus, SetLastError = true, ExactSpelling = true, CharSet = CharSet.Unicode)]
|
||||
[ResourceExposure(ResourceScope.None)]
|
||||
internal static extern int GdipImageGetFrameDimensionsList(HandleRef image, IntPtr buffer, int count);
|
||||
|
||||
[DllImport(Gdiplus, SetLastError = true, ExactSpelling = true, CharSet = CharSet.Unicode)]
|
||||
[ResourceExposure(ResourceScope.None)]
|
||||
internal static extern int GdipImageGetFrameCount(HandleRef image, ref Guid dimensionId, int[] count);
|
||||
|
||||
[DllImport(Gdiplus, SetLastError = true, ExactSpelling = true, CharSet = CharSet.Unicode)]
|
||||
[ResourceExposure(ResourceScope.None)]
|
||||
internal static extern int GdipGetPropertyItemSize(HandleRef image, int propid, out int size);
|
||||
|
||||
[DllImport(Gdiplus, SetLastError = true, ExactSpelling = true, CharSet = CharSet.Unicode)]
|
||||
[ResourceExposure(ResourceScope.None)]
|
||||
internal static extern int GdipGetPropertyItem(HandleRef image, int propid, int size, IntPtr buffer);
|
||||
|
||||
[DllImport(Gdiplus, SetLastError = true, ExactSpelling = true, CharSet = CharSet.Unicode)]
|
||||
[ResourceExposure(ResourceScope.Machine)]
|
||||
internal static extern int GdipCreateHBITMAPFromBitmap(HandleRef nativeBitmap, out IntPtr hbitmap, int argbBackground);
|
||||
|
||||
[DllImport(Gdiplus, SetLastError = true, ExactSpelling = true, CharSet = CharSet.Unicode)]
|
||||
[ResourceExposure(ResourceScope.None)]
|
||||
internal static extern int GdipImageSelectActiveFrame(HandleRef image, ref Guid dimensionId, int frameIndex);
|
||||
|
||||
[DllImport(Gdiplus, SetLastError = true, ExactSpelling = true, CharSet = CharSet.Unicode)]
|
||||
[ResourceExposure(ResourceScope.Machine)]
|
||||
internal static extern int GdipCreateBitmapFromFile(string filename, out IntPtr bitmap);
|
||||
|
||||
[DllImport(Gdiplus, SetLastError = true, ExactSpelling = true, CharSet = CharSet.Unicode)]
|
||||
[ResourceExposure(ResourceScope.None)]
|
||||
internal static extern int GdipImageForceValidation(HandleRef image);
|
||||
|
||||
[DllImport(Gdiplus, SetLastError = true, ExactSpelling = true, EntryPoint = "GdipDisposeImage", CharSet = CharSet.Unicode)]
|
||||
[ResourceExposure(ResourceScope.None)]
|
||||
private static extern int IntGdipDisposeImage(HandleRef image);
|
||||
|
||||
internal static int GdipDisposeImage(HandleRef image)
|
||||
{
|
||||
if (!Initialized) return Ok;
|
||||
var result = IntGdipDisposeImage(image);
|
||||
return result;
|
||||
}
|
||||
|
||||
[DllImport(Gdiplus, SetLastError = true, ExactSpelling = true, CharSet = CharSet.Unicode)]
|
||||
[ResourceExposure(ResourceScope.Process)]
|
||||
private static extern int GdiplusStartup(out IntPtr token, ref StartupInput input, out StartupOutput output);
|
||||
|
||||
[DllImport(Gdiplus, SetLastError = true, ExactSpelling = true, CharSet = CharSet.Unicode)]
|
||||
[ResourceExposure(ResourceScope.None)]
|
||||
internal static extern int GdipGetImageRawFormat(HandleRef image, ref Guid format);
|
||||
|
||||
[DllImport(Gdiplus, SetLastError = true, ExactSpelling = true, CharSet = CharSet.Unicode)]
|
||||
[ResourceExposure(ResourceScope.Machine)]
|
||||
internal static extern int GdipCreateBitmapFromStream(IStream stream, out IntPtr bitmap);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,294 @@
|
||||
using System;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Security;
|
||||
using System.Windows;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
using HandyControl.Tools.Interop;
|
||||
|
||||
namespace HandyControl.Tools
|
||||
{
|
||||
internal static class IconHelper
|
||||
{
|
||||
private static Size SmallIconSize;
|
||||
|
||||
private static Size IconSize;
|
||||
|
||||
private static int SystemBitDepth;
|
||||
|
||||
[SecurityCritical, SecuritySafeCritical]
|
||||
public static void GetIconHandlesFromImageSource(ImageSource image, out IconHandle largeIconHandle, out IconHandle smallIconHandle)
|
||||
{
|
||||
EnsureSystemMetrics();
|
||||
largeIconHandle = CreateIconHandleFromImageSource(image, IconSize);
|
||||
smallIconHandle = CreateIconHandleFromImageSource(image, SmallIconSize);
|
||||
}
|
||||
|
||||
[SecurityCritical]
|
||||
public static IconHandle CreateIconHandleFromImageSource(ImageSource image, Size size)
|
||||
{
|
||||
EnsureSystemMetrics();
|
||||
|
||||
var asGoodAsItGets = false;
|
||||
|
||||
var bf = image as BitmapFrame;
|
||||
if (bf?.Decoder?.Frames != null)
|
||||
{
|
||||
bf = GetBestMatch(bf.Decoder.Frames, size);
|
||||
|
||||
asGoodAsItGets = bf.Decoder is IconBitmapDecoder || bf.PixelWidth == (int)size.Width && bf.PixelHeight == (int)size.Height;
|
||||
|
||||
image = bf;
|
||||
}
|
||||
|
||||
if (!asGoodAsItGets)
|
||||
{
|
||||
bf = BitmapFrame.Create(GenerateBitmapSource(image, size));
|
||||
}
|
||||
|
||||
return CreateIconHandleFromBitmapFrame(bf);
|
||||
}
|
||||
|
||||
[SecurityCritical]
|
||||
private static IconHandle CreateIconHandleFromBitmapFrame(BitmapFrame sourceBitmapFrame)
|
||||
{
|
||||
BitmapSource bitmapSource = sourceBitmapFrame;
|
||||
|
||||
if (bitmapSource.Format != PixelFormats.Bgra32 && bitmapSource.Format != PixelFormats.Pbgra32)
|
||||
{
|
||||
bitmapSource = new FormatConvertedBitmap(bitmapSource, PixelFormats.Bgra32, null, 0.0);
|
||||
}
|
||||
|
||||
var w = bitmapSource.PixelWidth;
|
||||
var h = bitmapSource.PixelHeight;
|
||||
var bpp = bitmapSource.Format.BitsPerPixel;
|
||||
var stride = (bpp * w + 31) / 32 * 4;
|
||||
var sizeCopyPixels = stride * h;
|
||||
var xor = new byte[sizeCopyPixels];
|
||||
bitmapSource.CopyPixels(xor, stride, 0);
|
||||
|
||||
return CreateIconCursor(xor, w, h, 0, 0, true);
|
||||
}
|
||||
|
||||
[SecurityCritical]
|
||||
internal static IconHandle CreateIconCursor(byte[] colorArray, int width, int height, int xHotspot, int yHotspot, bool isIcon)
|
||||
{
|
||||
BitmapHandle colorBitmap = null;
|
||||
BitmapHandle maskBitmap = null;
|
||||
|
||||
try
|
||||
{
|
||||
var bi = new BITMAPINFO(width, -height, 32)
|
||||
{
|
||||
bmiHeader_biCompression = NativeMethods.BI_RGB
|
||||
};
|
||||
|
||||
var bits = IntPtr.Zero;
|
||||
colorBitmap = UnsafeNativeMethods.CreateDIBSection(new HandleRef(null, IntPtr.Zero), ref bi, NativeMethods.DIB_RGB_COLORS, ref bits, null, 0);
|
||||
|
||||
if (colorBitmap.IsInvalid || bits == IntPtr.Zero)
|
||||
{
|
||||
return IconHandle.GetInvalidIcon();
|
||||
}
|
||||
|
||||
Marshal.Copy(colorArray, 0, bits, colorArray.Length);
|
||||
var maskArray = GenerateMaskArray(width, height, colorArray);
|
||||
|
||||
maskBitmap = UnsafeNativeMethods.CreateBitmap(width, height, 1, 1, maskArray);
|
||||
if (maskBitmap.IsInvalid)
|
||||
{
|
||||
return IconHandle.GetInvalidIcon();
|
||||
}
|
||||
|
||||
var iconInfo = new ICONINFO
|
||||
{
|
||||
fIcon = isIcon,
|
||||
xHotspot = xHotspot,
|
||||
yHotspot = yHotspot,
|
||||
hbmMask = maskBitmap,
|
||||
hbmColor = colorBitmap
|
||||
};
|
||||
|
||||
return UnsafeNativeMethods.CreateIconIndirect(iconInfo);
|
||||
}
|
||||
finally
|
||||
{
|
||||
colorBitmap?.Dispose();
|
||||
maskBitmap?.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
private static byte[] GenerateMaskArray(int width, int height, byte[] colorArray)
|
||||
{
|
||||
var nCount = width * height;
|
||||
var bytesPerScanLine = AlignToBytes(width, 2) / 8;
|
||||
var bitsMask = new byte[bytesPerScanLine * height];
|
||||
|
||||
for (var i = 0; i < nCount; i++)
|
||||
{
|
||||
var hPos = i % width;
|
||||
var vPos = i / width;
|
||||
var byteIndex = hPos / 8;
|
||||
var offsetBit = (byte)(0x80 >> (hPos % 8));
|
||||
|
||||
if (colorArray[i * 4 + 3] == 0x00)
|
||||
{
|
||||
bitsMask[byteIndex + bytesPerScanLine * vPos] |= offsetBit;
|
||||
}
|
||||
else
|
||||
{
|
||||
bitsMask[byteIndex + bytesPerScanLine * vPos] &= (byte)~offsetBit;
|
||||
}
|
||||
|
||||
if (hPos == width - 1 && width == 8)
|
||||
{
|
||||
bitsMask[1 + bytesPerScanLine * vPos] = 0xff;
|
||||
}
|
||||
}
|
||||
|
||||
return bitsMask;
|
||||
}
|
||||
|
||||
internal static int AlignToBytes(double original, int nBytesCount)
|
||||
{
|
||||
var nBitsCount = 8 << (nBytesCount - 1);
|
||||
return ((int)Math.Ceiling(original) + (nBitsCount - 1)) / nBitsCount * nBitsCount;
|
||||
}
|
||||
|
||||
private static BitmapSource GenerateBitmapSource(ImageSource img, Size renderSize)
|
||||
{
|
||||
var drawingDimensions = new Rect(0, 0, renderSize.Width, renderSize.Height);
|
||||
|
||||
var renderRatio = renderSize.Width / renderSize.Height;
|
||||
var aspectRatio = img.Width / img.Height;
|
||||
|
||||
if (img.Width <= renderSize.Width && img.Height <= renderSize.Height)
|
||||
{
|
||||
drawingDimensions = new Rect((renderSize.Width - img.Width) / 2, (renderSize.Height - img.Height) / 2, img.Width, img.Height);
|
||||
}
|
||||
else if (renderRatio > aspectRatio)
|
||||
{
|
||||
var scaledRenderWidth = (img.Width / img.Height) * renderSize.Width;
|
||||
drawingDimensions = new Rect((renderSize.Width - scaledRenderWidth) / 2, 0, scaledRenderWidth, renderSize.Height);
|
||||
}
|
||||
else if (renderRatio < aspectRatio)
|
||||
{
|
||||
var scaledRenderHeight = img.Height / img.Width * renderSize.Height;
|
||||
drawingDimensions = new Rect(0, (renderSize.Height - scaledRenderHeight) / 2, renderSize.Width, scaledRenderHeight);
|
||||
}
|
||||
|
||||
var dv = new DrawingVisual();
|
||||
var dc = dv.RenderOpen();
|
||||
dc.DrawImage(img, drawingDimensions);
|
||||
dc.Close();
|
||||
|
||||
var bmp = new RenderTargetBitmap((int)renderSize.Width, (int)renderSize.Height, 96, 96, PixelFormats.Pbgra32);
|
||||
bmp.Render(dv);
|
||||
|
||||
return bmp;
|
||||
}
|
||||
|
||||
private static BitmapFrame GetBestMatch(ReadOnlyCollection<BitmapFrame> frames, Size size)
|
||||
{
|
||||
var bestScore = int.MaxValue;
|
||||
var bestBpp = 0;
|
||||
var bestIndex = 0;
|
||||
|
||||
var isBitmapIconDecoder = frames[0].Decoder is IconBitmapDecoder;
|
||||
|
||||
for (int i = 0; i < frames.Count && bestScore != 0; ++i)
|
||||
{
|
||||
var currentIconBitDepth = isBitmapIconDecoder ? frames[i].Thumbnail.Format.BitsPerPixel : frames[i].Format.BitsPerPixel;
|
||||
|
||||
if (currentIconBitDepth == 0)
|
||||
{
|
||||
currentIconBitDepth = 8;
|
||||
}
|
||||
|
||||
var score = MatchImage(frames[i], size, currentIconBitDepth);
|
||||
if (score < bestScore)
|
||||
{
|
||||
bestIndex = i;
|
||||
bestBpp = currentIconBitDepth;
|
||||
bestScore = score;
|
||||
}
|
||||
else if (score == bestScore)
|
||||
{
|
||||
if (bestBpp < currentIconBitDepth)
|
||||
{
|
||||
bestIndex = i;
|
||||
bestBpp = currentIconBitDepth;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return frames[bestIndex];
|
||||
}
|
||||
|
||||
private static int MatchImage(BitmapFrame frame, Size size, int bpp)
|
||||
{
|
||||
var score = 2 * MyAbs(bpp, SystemBitDepth, false) +
|
||||
MyAbs(frame.PixelWidth, (int)size.Width, true) +
|
||||
MyAbs(frame.PixelHeight, (int)size.Height, true);
|
||||
|
||||
return score;
|
||||
}
|
||||
|
||||
private static int MyAbs(int valueHave, int valueWant, bool fPunish)
|
||||
{
|
||||
var diff = (valueHave - valueWant);
|
||||
|
||||
if (diff < 0)
|
||||
{
|
||||
diff = (fPunish ? -2 : -1) * diff;
|
||||
}
|
||||
|
||||
return diff;
|
||||
}
|
||||
|
||||
[SecurityCritical, SecuritySafeCritical]
|
||||
private static void EnsureSystemMetrics()
|
||||
{
|
||||
if (SystemBitDepth == 0)
|
||||
{
|
||||
var hdcDesktop = new HandleRef(null, UnsafeNativeMethods.GetDC(new HandleRef()));
|
||||
try
|
||||
{
|
||||
var sysBitDepth = UnsafeNativeMethods.GetDeviceCaps(hdcDesktop, NativeMethods.BITSPIXEL);
|
||||
sysBitDepth *= UnsafeNativeMethods.GetDeviceCaps(hdcDesktop, NativeMethods.PLANES);
|
||||
|
||||
if (sysBitDepth == 8)
|
||||
{
|
||||
sysBitDepth = 4;
|
||||
}
|
||||
|
||||
var cxSmallIcon = UnsafeNativeMethods.GetSystemMetrics(SM.CXSMICON);
|
||||
var cySmallIcon = UnsafeNativeMethods.GetSystemMetrics(SM.CYSMICON);
|
||||
var cxIcon = UnsafeNativeMethods.GetSystemMetrics(SM.CXICON);
|
||||
var cyIcon = UnsafeNativeMethods.GetSystemMetrics(SM.CYICON);
|
||||
|
||||
SmallIconSize = new Size(cxSmallIcon, cySmallIcon);
|
||||
IconSize = new Size(cxIcon, cyIcon);
|
||||
SystemBitDepth = sysBitDepth;
|
||||
}
|
||||
finally
|
||||
{
|
||||
UnsafeNativeMethods.ReleaseDC(new HandleRef(), hdcDesktop);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[SecurityCritical, SecuritySafeCritical]
|
||||
public static void GetDefaultIconHandles(out IconHandle largeIconHandle, out IconHandle smallIconHandle)
|
||||
{
|
||||
largeIconHandle = null;
|
||||
smallIconHandle = null;
|
||||
|
||||
SecurityHelper.DemandUIWindowPermission();
|
||||
|
||||
var iconModuleFile = UnsafeNativeMethods.GetModuleFileName(new HandleRef());
|
||||
UnsafeNativeMethods.ExtractIconEx(iconModuleFile, 0, out largeIconHandle, out smallIconHandle, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Windows;
|
||||
using HandyControl.Data;
|
||||
|
||||
namespace HandyControl.Tools
|
||||
{
|
||||
/// <summary>
|
||||
/// 资源帮助类
|
||||
/// </summary>
|
||||
public class ResourceHelper
|
||||
{
|
||||
/// <summary>
|
||||
/// 获取字符串
|
||||
/// </summary>
|
||||
/// <param name="key"></param>
|
||||
/// <returns></returns>
|
||||
public static string GetString(string key) => Application.Current.TryFindResource(key) as string;
|
||||
|
||||
/// <summary>
|
||||
/// 获取字符串
|
||||
/// </summary>
|
||||
/// <param name="separator"></param>
|
||||
/// <param name="keyArr"></param>
|
||||
/// <returns></returns>
|
||||
public static string GetString(string separator = ";", params string[] keyArr) =>
|
||||
string.Join(separator, keyArr.Select(key => Application.Current.TryFindResource(key) as string).ToList());
|
||||
|
||||
/// <summary>
|
||||
/// 获取字符串
|
||||
/// </summary>
|
||||
/// <param name="keyArr"></param>
|
||||
/// <returns></returns>
|
||||
public static List<string> GetStringList(params string[] keyArr) => keyArr.Select(key => Application.Current.TryFindResource(key) as string).ToList();
|
||||
|
||||
/// <summary>
|
||||
/// 获取资源
|
||||
/// </summary>
|
||||
/// <param name="key"></param>
|
||||
/// <returns></returns>
|
||||
public static T GetResource<T>(string key)
|
||||
{
|
||||
if (Application.Current.TryFindResource(key) is T resource)
|
||||
{
|
||||
return resource;
|
||||
}
|
||||
|
||||
return default(T);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取HandyControl皮肤
|
||||
/// </summary>
|
||||
/// <param name="skin"></param>
|
||||
/// <returns></returns>
|
||||
public static ResourceDictionary GetSkin(SkinType skin) => new ResourceDictionary
|
||||
{
|
||||
Source = new Uri($"pack://application:,,,/HandyControl;component/Themes/Skin{skin.ToString()}.xaml")
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// 获取皮肤
|
||||
/// </summary>
|
||||
/// <param name="assembly"></param>
|
||||
/// <param name="themePath"></param>
|
||||
/// <param name="skin"></param>
|
||||
/// <returns></returns>
|
||||
public static ResourceDictionary GetSkin(Assembly assembly, string themePath, SkinType skin)
|
||||
{
|
||||
try
|
||||
{
|
||||
var uri = new Uri($"pack://application:,,,/{assembly.GetName().Name};component/{themePath}/Skin{skin.ToString()}.xaml");
|
||||
return new ResourceDictionary
|
||||
{
|
||||
Source = uri
|
||||
};
|
||||
}
|
||||
catch
|
||||
{
|
||||
return new ResourceDictionary
|
||||
{
|
||||
Source = new Uri($"pack://application:,,,/{assembly.GetName().Name};component/{themePath}/Skin{SkinType.Default.ToString()}.xaml")
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Security;
|
||||
using System.Security.Permissions;
|
||||
|
||||
namespace HandyControl.Tools
|
||||
{
|
||||
[SuppressMessage("ReSharper", "InconsistentNaming")]
|
||||
internal class SecurityHelper
|
||||
{
|
||||
private static UIPermission _allWindowsUIPermission;
|
||||
|
||||
[SecurityCritical]
|
||||
internal static void DemandUIWindowPermission()
|
||||
{
|
||||
if (_allWindowsUIPermission == null)
|
||||
{
|
||||
_allWindowsUIPermission = new UIPermission(UIPermissionWindow.AllWindows);
|
||||
}
|
||||
_allWindowsUIPermission.Demand();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Windows.Media;
|
||||
using HandyControl.Controls;
|
||||
|
||||
namespace HandyControl.Tools
|
||||
{
|
||||
/// <summary>
|
||||
/// 该类可以为可视化元素提供单开的功能
|
||||
/// </summary>
|
||||
public class SingleOpenHelper
|
||||
{
|
||||
private static readonly Dictionary<string, ISingleOpen> OpenDic = new Dictionary<string, ISingleOpen>();
|
||||
|
||||
/// <summary>
|
||||
/// 根据指定的类型创建实例
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <returns></returns>
|
||||
public static T CreateControl<T>() where T : Visual, ISingleOpen, new()
|
||||
{
|
||||
var typeStr = typeof(T).FullName;
|
||||
|
||||
if (string.IsNullOrEmpty(typeStr)) return default(T);
|
||||
|
||||
var temp = new T();
|
||||
if (!OpenDic.Keys.Contains(typeStr))
|
||||
{
|
||||
OpenDic.Add(typeStr, temp);
|
||||
return temp;
|
||||
}
|
||||
var currentCtl = OpenDic[typeStr];
|
||||
if (currentCtl.CanDispose)
|
||||
{
|
||||
currentCtl.Dispose();
|
||||
OpenDic[typeStr] = temp;
|
||||
return temp;
|
||||
}
|
||||
return default(T);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
namespace HandyControl.Tools
|
||||
{
|
||||
/// <summary>
|
||||
/// 验证帮助类
|
||||
/// </summary>
|
||||
public class ValidateHelper
|
||||
{
|
||||
/// <summary>
|
||||
/// 是否在浮点数范围内
|
||||
/// </summary>
|
||||
/// <param name="value"></param>
|
||||
/// <returns></returns>
|
||||
public static bool IsInRangeOfDouble(object value)
|
||||
{
|
||||
var v = (double)value;
|
||||
return !(double.IsNaN(v) || double.IsInfinity(v));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 是否在正浮点数范围内
|
||||
/// </summary>
|
||||
/// <param name="value"></param>
|
||||
/// <param name="includeZero"></param>
|
||||
/// <returns></returns>
|
||||
public static bool IsInRangeOfPosDouble(object value, bool includeZero = false)
|
||||
{
|
||||
var v = (double)value;
|
||||
return !(double.IsNaN(v) || double.IsInfinity(v)) && (includeZero ? v >= 0 : v > 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 是否在正浮点数范围内
|
||||
/// </summary>
|
||||
/// <param name="value"></param>
|
||||
/// <param name="includeZero"></param>
|
||||
/// <returns></returns>
|
||||
public static bool IsInRangeOfNegDouble(object value, bool includeZero = false)
|
||||
{
|
||||
var v = (double)value;
|
||||
return !(double.IsNaN(v) || double.IsInfinity(v)) && (includeZero ? v <= 0 : v < 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
using System.Linq;
|
||||
using System.Windows;
|
||||
using System.Windows.Media;
|
||||
|
||||
namespace HandyControl.Tools
|
||||
{
|
||||
public class VisualHelper
|
||||
{
|
||||
internal static VisualStateGroup TryGetVisualStateGroup(DependencyObject d, string groupName)
|
||||
{
|
||||
var root = GetImplementationRoot(d);
|
||||
if (root == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return VisualStateManager
|
||||
.GetVisualStateGroups(root)?
|
||||
.OfType<VisualStateGroup>()
|
||||
.FirstOrDefault(group => string.CompareOrdinal(groupName, group.Name) == 0);
|
||||
}
|
||||
|
||||
internal static FrameworkElement GetImplementationRoot(DependencyObject d)
|
||||
{
|
||||
return 1 == VisualTreeHelper.GetChildrenCount(d)
|
||||
? VisualTreeHelper.GetChild(d, 0) as FrameworkElement
|
||||
: null;
|
||||
}
|
||||
|
||||
internal static T GetChild<T>(DependencyObject d) where T : DependencyObject
|
||||
{
|
||||
if (d is T t)
|
||||
{
|
||||
return t;
|
||||
}
|
||||
|
||||
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(d); i++)
|
||||
{
|
||||
var child = VisualTreeHelper.GetChild(d, i);
|
||||
|
||||
var result = GetChild<T>(child);
|
||||
if (result != null) return result;
|
||||
}
|
||||
|
||||
return default(T);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取当前应用中处于激活的一个窗口
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static Window GetActiveWindow() => Application.Current.Windows.OfType<Window>().SingleOrDefault(x => x.IsActive);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Security;
|
||||
|
||||
namespace HandyControl.Tools.Interop
|
||||
{
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 2)]
|
||||
[SuppressMessage("ReSharper", "InconsistentNaming")]
|
||||
internal struct BITMAPINFO
|
||||
{
|
||||
public int bmiHeader_biSize;
|
||||
|
||||
public int bmiHeader_biWidth;
|
||||
|
||||
public int bmiHeader_biHeight;
|
||||
|
||||
public short bmiHeader_biPlanes;
|
||||
|
||||
public short bmiHeader_biBitCount;
|
||||
|
||||
public int bmiHeader_biCompression;
|
||||
|
||||
public int bmiHeader_biSizeImage;
|
||||
|
||||
public int bmiHeader_biXPelsPerMeter;
|
||||
|
||||
public int bmiHeader_biYPelsPerMeter;
|
||||
|
||||
public int bmiHeader_biClrUsed;
|
||||
|
||||
public int bmiHeader_biClrImportant;
|
||||
|
||||
public BITMAPINFO(int width, int height, short bpp)
|
||||
{
|
||||
bmiHeader_biSize = SizeOf();
|
||||
bmiHeader_biWidth = width;
|
||||
bmiHeader_biHeight = height;
|
||||
bmiHeader_biPlanes = 1;
|
||||
bmiHeader_biBitCount = bpp;
|
||||
bmiHeader_biCompression = 0;
|
||||
bmiHeader_biSizeImage = 0;
|
||||
bmiHeader_biXPelsPerMeter = 0;
|
||||
bmiHeader_biYPelsPerMeter = 0;
|
||||
bmiHeader_biClrUsed = 0;
|
||||
bmiHeader_biClrImportant = 0;
|
||||
}
|
||||
|
||||
[SecuritySafeCritical]
|
||||
private static int SizeOf()
|
||||
{
|
||||
return Marshal.SizeOf(typeof(BITMAPINFO));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
using System;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Runtime.ConstrainedExecution;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Security;
|
||||
|
||||
namespace HandyControl.Tools.Interop
|
||||
{
|
||||
[SuppressMessage("ReSharper", "UnusedMember.Local")]
|
||||
internal sealed class BitmapHandle : WpfSafeHandle
|
||||
{
|
||||
[SecurityCritical]
|
||||
private BitmapHandle() : this(true)
|
||||
{
|
||||
//请不要删除此构造函数,否则当使用自定义ico文件时会报错
|
||||
}
|
||||
|
||||
[SecurityCritical]
|
||||
private BitmapHandle(bool ownsHandle) : base(ownsHandle, CommonHandles.GDI)
|
||||
{
|
||||
}
|
||||
|
||||
[SecurityCritical]
|
||||
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
|
||||
protected override bool ReleaseHandle()
|
||||
{
|
||||
return UnsafeNativeMethods.DeleteObject(handle);
|
||||
}
|
||||
|
||||
[SecurityCritical]
|
||||
internal HandleRef MakeHandleRef(object obj)
|
||||
{
|
||||
return new HandleRef(obj, handle);
|
||||
}
|
||||
|
||||
[SecurityCritical]
|
||||
internal static BitmapHandle CreateFromHandle(IntPtr hbitmap, bool ownsHandle = true)
|
||||
{
|
||||
return new BitmapHandle(ownsHandle)
|
||||
{
|
||||
handle = hbitmap,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
namespace HandyControl.Tools.Interop
|
||||
{
|
||||
[SuppressMessage("ReSharper", "InconsistentNaming")]
|
||||
internal static class CommonHandles
|
||||
{
|
||||
public static readonly int Icon = HandleCollector.RegisterType(nameof(Icon), 20, 500);
|
||||
|
||||
public static readonly int HDC = HandleCollector.RegisterType(nameof(HDC), 100, 2);
|
||||
|
||||
public static readonly int GDI = HandleCollector.RegisterType(nameof(GDI), 50, 500);
|
||||
|
||||
public static readonly int Kernel = HandleCollector.RegisterType(nameof(Kernel), 0, 1000);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
namespace HandyControl.Tools.Interop
|
||||
{
|
||||
internal static class ExternDll
|
||||
{
|
||||
public const string
|
||||
User32 = "user32.dll",
|
||||
Gdi32 = "gdi32.dll",
|
||||
Kernel32 = "kernel32.dll",
|
||||
Shell32 = "shell32.dll";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
// reference from https://referencesource.microsoft.com/#WindowsBase/Shared/MS/Win32/HandleCollector.cs,d0f99220d8e1b708
|
||||
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace HandyControl.Tools.Interop
|
||||
{
|
||||
internal static class HandleCollector
|
||||
{
|
||||
private static HandleType[] HandleTypes;
|
||||
private static int HandleTypeCount;
|
||||
|
||||
private static readonly object HandleMutex = new object();
|
||||
|
||||
internal static IntPtr Add(IntPtr handle, int type)
|
||||
{
|
||||
HandleTypes[type - 1].Add();
|
||||
return handle;
|
||||
}
|
||||
|
||||
[System.Security.SecuritySafeCritical]
|
||||
internal static SafeHandle Add(SafeHandle handle, int type)
|
||||
{
|
||||
HandleTypes[type - 1].Add();
|
||||
return handle;
|
||||
}
|
||||
|
||||
internal static void Add(int type)
|
||||
{
|
||||
HandleTypes[type - 1].Add();
|
||||
}
|
||||
|
||||
internal static int RegisterType(string typeName, int expense, int initialThreshold)
|
||||
{
|
||||
lock (HandleMutex)
|
||||
{
|
||||
if (HandleTypeCount == 0 || HandleTypeCount == HandleTypes.Length)
|
||||
{
|
||||
HandleType[] newTypes = new HandleType[HandleTypeCount + 10];
|
||||
if (HandleTypes != null)
|
||||
{
|
||||
Array.Copy(HandleTypes, 0, newTypes, 0, HandleTypeCount);
|
||||
}
|
||||
HandleTypes = newTypes;
|
||||
}
|
||||
|
||||
HandleTypes[HandleTypeCount++] = new HandleType(expense, initialThreshold);
|
||||
return HandleTypeCount;
|
||||
}
|
||||
}
|
||||
|
||||
internal static IntPtr Remove(IntPtr handle, int type)
|
||||
{
|
||||
HandleTypes[type - 1].Remove();
|
||||
return handle;
|
||||
}
|
||||
|
||||
[System.Security.SecuritySafeCritical]
|
||||
internal static SafeHandle Remove(SafeHandle handle, int type)
|
||||
{
|
||||
HandleTypes[type - 1].Remove();
|
||||
return handle;
|
||||
}
|
||||
|
||||
internal static void Remove(int type)
|
||||
{
|
||||
HandleTypes[type - 1].Remove();
|
||||
}
|
||||
|
||||
private class HandleType
|
||||
{
|
||||
private readonly int _initialThreshHold;
|
||||
private int _threshHold;
|
||||
private int _handleCount;
|
||||
private readonly int _deltaPercent;
|
||||
|
||||
internal HandleType(int expense, int initialThreshHold)
|
||||
{
|
||||
_initialThreshHold = initialThreshHold;
|
||||
_threshHold = initialThreshHold;
|
||||
_deltaPercent = 100 - expense;
|
||||
}
|
||||
|
||||
internal void Add()
|
||||
{
|
||||
lock (this)
|
||||
{
|
||||
_handleCount++;
|
||||
var performCollect = NeedCollection();
|
||||
|
||||
if (!performCollect)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
GC.Collect();
|
||||
|
||||
var sleep = (100 - _deltaPercent) / 4;
|
||||
System.Threading.Thread.Sleep(sleep);
|
||||
}
|
||||
|
||||
private bool NeedCollection()
|
||||
{
|
||||
|
||||
if (_handleCount > _threshHold)
|
||||
{
|
||||
_threshHold = _handleCount + _handleCount * _deltaPercent / 100;
|
||||
return true;
|
||||
}
|
||||
|
||||
var oldThreshHold = 100 * _threshHold / (100 + _deltaPercent);
|
||||
if (oldThreshHold >= _initialThreshHold && _handleCount < (int)(oldThreshHold * .9F))
|
||||
{
|
||||
_threshHold = oldThreshHold;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
internal void Remove()
|
||||
{
|
||||
lock (this)
|
||||
{
|
||||
_handleCount--;
|
||||
|
||||
_handleCount = Math.Max(0, _handleCount);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace HandyControl.Tools.Interop
|
||||
{
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
[SuppressMessage("ReSharper", "InconsistentNaming")]
|
||||
internal class ICONINFO
|
||||
{
|
||||
public bool fIcon = false;
|
||||
public int xHotspot = 0;
|
||||
public int yHotspot = 0;
|
||||
public BitmapHandle hbmMask = null;
|
||||
public BitmapHandle hbmColor = null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
using System;
|
||||
using System.Runtime.ConstrainedExecution;
|
||||
using System.Security;
|
||||
|
||||
namespace HandyControl.Tools.Interop
|
||||
{
|
||||
internal sealed class IconHandle : WpfSafeHandle
|
||||
{
|
||||
[SecurityCritical]
|
||||
private IconHandle() : base(true, CommonHandles.Icon)
|
||||
{
|
||||
}
|
||||
|
||||
[SecurityCritical]
|
||||
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
|
||||
protected override bool ReleaseHandle()
|
||||
{
|
||||
return UnsafeNativeMethods.DestroyIcon(handle);
|
||||
}
|
||||
|
||||
[SecurityCritical, SecuritySafeCritical]
|
||||
internal static IconHandle GetInvalidIcon()
|
||||
{
|
||||
return new IconHandle();
|
||||
}
|
||||
|
||||
[SecurityCritical]
|
||||
internal IntPtr CriticalGetHandle()
|
||||
{
|
||||
return handle;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
using System;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace HandyControl.Tools.Interop
|
||||
{
|
||||
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
|
||||
[SuppressMessage("ReSharper", "InconsistentNaming")]
|
||||
public class NOTIFYICONDATA
|
||||
{
|
||||
public int cbSize = Marshal.SizeOf(typeof(NOTIFYICONDATA));
|
||||
public IntPtr hWnd;
|
||||
public int uID;
|
||||
public int uFlags;
|
||||
public int uCallbackMessage;
|
||||
public IntPtr hIcon;
|
||||
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
|
||||
public string szTip = string.Empty;
|
||||
public int dwState = 0x01;
|
||||
public int dwStateMask = 0x01;
|
||||
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
|
||||
public string szInfo = string.Empty;
|
||||
public int uTimeoutOrVersion;
|
||||
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 64)]
|
||||
public string szInfoTitle = string.Empty;
|
||||
public int dwInfoFlags;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
using System;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Runtime.Versioning;
|
||||
|
||||
namespace HandyControl.Tools.Interop
|
||||
{
|
||||
public delegate IntPtr WndProc(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam);
|
||||
|
||||
[SuppressMessage("ReSharper", "InconsistentNaming")]
|
||||
[SuppressMessage("ReSharper", "IdentifierTypo")]
|
||||
internal class NativeMethods
|
||||
{
|
||||
public const int
|
||||
WS_CHILD = 0x40000000,
|
||||
BITSPIXEL = 12,
|
||||
PLANES = 14,
|
||||
BI_RGB = 0,
|
||||
DIB_RGB_COLORS = 0,
|
||||
NIF_MESSAGE = 0x00000001,
|
||||
NIF_ICON = 0x00000002,
|
||||
NIF_TIP = 0x00000004,
|
||||
NIF_INFO = 0x00000010,
|
||||
NIN_BALLOONSHOW = WM_USER + 2,
|
||||
NIN_BALLOONHIDE = WM_USER + 3,
|
||||
NIN_BALLOONTIMEOUT = WM_USER + 4,
|
||||
NIM_ADD = 0x00000000,
|
||||
NIM_MODIFY = 0x00000001,
|
||||
NIM_DELETE = 0x00000002,
|
||||
NIIF_NONE = 0x00000000,
|
||||
NIIF_INFO = 0x00000001,
|
||||
NIIF_WARNING = 0x00000002,
|
||||
NIIF_ERROR = 0x00000003,
|
||||
WM_MOUSEMOVE = 0x0200,
|
||||
WM_LBUTTONDOWN = 0x0201,
|
||||
WM_LBUTTONUP = 0x0202,
|
||||
WM_LBUTTONDBLCLK = 0x0203,
|
||||
WM_RBUTTONDOWN = 0x0204,
|
||||
WM_RBUTTONUP = 0x0205,
|
||||
WM_RBUTTONDBLCLK = 0x0206,
|
||||
WM_MBUTTONDOWN = 0x0207,
|
||||
WM_MBUTTONUP = 0x0208,
|
||||
WM_MBUTTONDBLCLK = 0x0209,
|
||||
WM_USER = 0x0400;
|
||||
|
||||
[DllImport(ExternDll.User32, CharSet = CharSet.Auto)]
|
||||
[ResourceExposure(ResourceScope.None)]
|
||||
public static extern int RegisterWindowMessage(string msg);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
// ReSharper disable InconsistentNaming
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace HandyControl.Tools.Interop
|
||||
{
|
||||
internal enum SM
|
||||
{
|
||||
CXSCREEN = 0,
|
||||
CYSCREEN = 1,
|
||||
CXVSCROLL = 2,
|
||||
CYHSCROLL = 3,
|
||||
CYCAPTION = 4,
|
||||
CXBORDER = 5,
|
||||
CYBORDER = 6,
|
||||
CXFIXEDFRAME = 7,
|
||||
CYFIXEDFRAME = 8,
|
||||
CYVTHUMB = 9,
|
||||
CXHTHUMB = 10,
|
||||
CXICON = 11,
|
||||
CYICON = 12,
|
||||
CXCURSOR = 13,
|
||||
CYCURSOR = 14,
|
||||
CYMENU = 15,
|
||||
CXFULLSCREEN = 16,
|
||||
CYFULLSCREEN = 17,
|
||||
CYKANJIWINDOW = 18,
|
||||
MOUSEPRESENT = 19,
|
||||
CYVSCROLL = 20,
|
||||
CXHSCROLL = 21,
|
||||
DEBUG = 22,
|
||||
SWAPBUTTON = 23,
|
||||
CXMIN = 28,
|
||||
CYMIN = 29,
|
||||
CXSIZE = 30,
|
||||
CYSIZE = 31,
|
||||
CXFRAME = 32,
|
||||
CXSIZEFRAME = CXFRAME,
|
||||
CYFRAME = 33,
|
||||
CYSIZEFRAME = CYFRAME,
|
||||
CXMINTRACK = 34,
|
||||
CYMINTRACK = 35,
|
||||
CXDOUBLECLK = 36,
|
||||
CYDOUBLECLK = 37,
|
||||
CXICONSPACING = 38,
|
||||
CYICONSPACING = 39,
|
||||
MENUDROPALIGNMENT = 40,
|
||||
PENWINDOWS = 41,
|
||||
DBCSENABLED = 42,
|
||||
CMOUSEBUTTONS = 43,
|
||||
SECURE = 44,
|
||||
CXEDGE = 45,
|
||||
CYEDGE = 46,
|
||||
CXMINSPACING = 47,
|
||||
CYMINSPACING = 48,
|
||||
CXSMICON = 49,
|
||||
CYSMICON = 50,
|
||||
CYSMCAPTION = 51,
|
||||
CXSMSIZE = 52,
|
||||
CYSMSIZE = 53,
|
||||
CXMENUSIZE = 54,
|
||||
CYMENUSIZE = 55,
|
||||
ARRANGE = 56,
|
||||
CXMINIMIZED = 57,
|
||||
CYMINIMIZED = 58,
|
||||
CXMAXTRACK = 59,
|
||||
CYMAXTRACK = 60,
|
||||
CXMAXIMIZED = 61,
|
||||
CYMAXIMIZED = 62,
|
||||
NETWORK = 63,
|
||||
CLEANBOOT = 67,
|
||||
CXDRAG = 68,
|
||||
CYDRAG = 69,
|
||||
SHOWSOUNDS = 70,
|
||||
CXMENUCHECK = 71,
|
||||
CYMENUCHECK = 72,
|
||||
SLOWMACHINE = 73,
|
||||
MIDEASTENABLED = 74,
|
||||
MOUSEWHEELPRESENT = 75,
|
||||
XVIRTUALSCREEN = 76,
|
||||
YVIRTUALSCREEN = 77,
|
||||
CXVIRTUALSCREEN = 78,
|
||||
CYVIRTUALSCREEN = 79,
|
||||
CMONITORS = 80,
|
||||
SAMEDISPLAYFORMAT = 81,
|
||||
IMMENABLED = 82,
|
||||
CXFOCUSBORDER = 83,
|
||||
CYFOCUSBORDER = 84,
|
||||
TABLETPC = 86,
|
||||
MEDIACENTER = 87,
|
||||
REMOTESESSION = 0x1000,
|
||||
REMOTECONTROL = 0x2001,
|
||||
}
|
||||
|
||||
internal static class Win32Constant
|
||||
{
|
||||
internal const int MAX_PATH = 260;
|
||||
internal const int INFOTIPSIZE = 1024;
|
||||
internal const int TRUE = 1;
|
||||
internal const int FALSE = 0;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public class POINT
|
||||
{
|
||||
public int x;
|
||||
public int y;
|
||||
|
||||
public POINT()
|
||||
{
|
||||
}
|
||||
|
||||
public POINT(int x, int y)
|
||||
{
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Security;
|
||||
using System.Security.Permissions;
|
||||
using Microsoft.Win32.SafeHandles;
|
||||
|
||||
namespace HandyControl.Tools.Interop
|
||||
{
|
||||
internal sealed class SafeFileMappingHandle : SafeHandleZeroOrMinusOneIsInvalid
|
||||
{
|
||||
[SecurityCritical]
|
||||
internal SafeFileMappingHandle(IntPtr handle) : base(false)
|
||||
{
|
||||
SetHandle(handle);
|
||||
}
|
||||
|
||||
[SecurityCritical, SecuritySafeCritical]
|
||||
internal SafeFileMappingHandle() : base(true)
|
||||
{
|
||||
}
|
||||
|
||||
public override bool IsInvalid
|
||||
{
|
||||
[SecurityCritical, SecuritySafeCritical]
|
||||
get => handle == IntPtr.Zero;
|
||||
}
|
||||
|
||||
[SecurityCritical, SecuritySafeCritical]
|
||||
protected override bool ReleaseHandle()
|
||||
{
|
||||
new SecurityPermission(SecurityPermissionFlag.UnmanagedCode).Assert();
|
||||
try
|
||||
{
|
||||
return CloseHandleNoThrow(new HandleRef(null, handle));
|
||||
}
|
||||
finally
|
||||
{
|
||||
CodeAccessPermission.RevertAssert();
|
||||
}
|
||||
}
|
||||
|
||||
[SecurityCritical]
|
||||
public static bool CloseHandleNoThrow(HandleRef handle)
|
||||
{
|
||||
HandleCollector.Remove((IntPtr)handle, CommonHandles.Kernel);
|
||||
var result = UnsafeNativeMethods.IntCloseHandle(handle);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,182 @@
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Runtime.Versioning;
|
||||
using System.Security;
|
||||
using System.Text;
|
||||
|
||||
namespace HandyControl.Tools.Interop
|
||||
{
|
||||
[SuppressMessage("ReSharper", "InconsistentNaming")]
|
||||
internal class UnsafeNativeMethods
|
||||
{
|
||||
[SecurityCritical]
|
||||
[SuppressUnmanagedCodeSecurity]
|
||||
[DllImport(ExternDll.User32, SetLastError = true, ExactSpelling = true, EntryPoint = nameof(GetDC), CharSet = CharSet.Auto)]
|
||||
public static extern IntPtr IntGetDC(HandleRef hWnd);
|
||||
|
||||
[SecurityCritical]
|
||||
public static IntPtr GetDC(HandleRef hWnd)
|
||||
{
|
||||
var hDc = IntGetDC(hWnd);
|
||||
if (hDc == IntPtr.Zero)
|
||||
{
|
||||
throw new Win32Exception();
|
||||
}
|
||||
|
||||
return HandleCollector.Add(hDc, CommonHandles.HDC);
|
||||
}
|
||||
|
||||
[SecurityCritical]
|
||||
[SuppressUnmanagedCodeSecurity]
|
||||
[DllImport(ExternDll.User32, ExactSpelling = true, EntryPoint = nameof(ReleaseDC), CharSet = CharSet.Auto)]
|
||||
public static extern int IntReleaseDC(HandleRef hWnd, HandleRef hDC);
|
||||
|
||||
[SecurityCritical]
|
||||
public static int ReleaseDC(HandleRef hWnd, HandleRef hDC)
|
||||
{
|
||||
HandleCollector.Remove((IntPtr)hDC, CommonHandles.HDC);
|
||||
return IntReleaseDC(hWnd, hDC);
|
||||
}
|
||||
|
||||
[SecurityCritical]
|
||||
[SuppressUnmanagedCodeSecurity]
|
||||
[DllImport(ExternDll.Gdi32, SetLastError = true, ExactSpelling = true, CharSet = CharSet.Auto)]
|
||||
public static extern int GetDeviceCaps(HandleRef hDC, int nIndex);
|
||||
|
||||
[SecurityCritical]
|
||||
[SuppressUnmanagedCodeSecurity]
|
||||
[DllImport(ExternDll.User32)]
|
||||
public static extern int GetSystemMetrics(SM nIndex);
|
||||
|
||||
[SecurityCritical]
|
||||
[SuppressUnmanagedCodeSecurity]
|
||||
[DllImport(ExternDll.User32, EntryPoint = nameof(DestroyIcon), CharSet = CharSet.Auto, SetLastError = true)]
|
||||
private static extern bool IntDestroyIcon(IntPtr hIcon);
|
||||
|
||||
[SecurityCritical]
|
||||
public static bool DestroyIcon(IntPtr hIcon)
|
||||
{
|
||||
var result = IntDestroyIcon(hIcon);
|
||||
return result;
|
||||
}
|
||||
|
||||
[SecurityCritical]
|
||||
[SuppressUnmanagedCodeSecurity]
|
||||
[DllImport(ExternDll.Gdi32, EntryPoint = nameof(DeleteObject), CharSet = CharSet.Auto, SetLastError = true)]
|
||||
private static extern bool IntDeleteObject(IntPtr hObject);
|
||||
|
||||
[SecurityCritical]
|
||||
public static bool DeleteObject(IntPtr hObject)
|
||||
{
|
||||
var result = IntDeleteObject(hObject);
|
||||
return result;
|
||||
}
|
||||
|
||||
[SecurityCritical]
|
||||
public static BitmapHandle CreateDIBSection(HandleRef hdc, ref BITMAPINFO bitmapInfo, int iUsage, ref IntPtr ppvBits, SafeFileMappingHandle hSection, int dwOffset)
|
||||
{
|
||||
if (hSection == null)
|
||||
{
|
||||
hSection = new SafeFileMappingHandle(IntPtr.Zero);
|
||||
}
|
||||
|
||||
var hBitmap = PrivateCreateDIBSection(hdc, ref bitmapInfo, iUsage, ref ppvBits, hSection, dwOffset);
|
||||
return hBitmap;
|
||||
}
|
||||
|
||||
[DllImport(ExternDll.Kernel32, EntryPoint = "CloseHandle", CharSet = CharSet.Auto, SetLastError = true)]
|
||||
public static extern bool IntCloseHandle(HandleRef handle);
|
||||
|
||||
[SecurityCritical, SuppressUnmanagedCodeSecurity]
|
||||
[DllImport(ExternDll.Gdi32, SetLastError = true, ExactSpelling = true, CharSet = CharSet.Auto, EntryPoint = nameof(CreateDIBSection))]
|
||||
private static extern BitmapHandle PrivateCreateDIBSection(HandleRef hdc, ref BITMAPINFO bitmapInfo, int iUsage, ref IntPtr ppvBits, SafeFileMappingHandle hSection, int dwOffset);
|
||||
|
||||
[SecurityCritical, SuppressUnmanagedCodeSecurity]
|
||||
[DllImport(ExternDll.User32, SetLastError = true, ExactSpelling = true, CharSet = CharSet.Auto, EntryPoint = nameof(CreateIconIndirect))]
|
||||
private static extern IconHandle PrivateCreateIconIndirect([In, MarshalAs(UnmanagedType.LPStruct)]ICONINFO iconInfo);
|
||||
|
||||
[SecurityCritical]
|
||||
public static IconHandle CreateIconIndirect([In, MarshalAs(UnmanagedType.LPStruct)]ICONINFO iconInfo)
|
||||
{
|
||||
var hIcon = PrivateCreateIconIndirect(iconInfo);
|
||||
return hIcon;
|
||||
}
|
||||
|
||||
[SecurityCritical, SuppressUnmanagedCodeSecurity]
|
||||
[DllImport(ExternDll.Gdi32, SetLastError = true, ExactSpelling = true, CharSet = CharSet.Auto, EntryPoint = nameof(CreateBitmap))]
|
||||
private static extern BitmapHandle PrivateCreateBitmap(int width, int height, int planes, int bitsPerPixel, byte[] lpvBits);
|
||||
|
||||
[SecurityCritical]
|
||||
internal static BitmapHandle CreateBitmap(int width, int height, int planes, int bitsPerPixel, byte[] lpvBits)
|
||||
{
|
||||
var hBitmap = PrivateCreateBitmap(width, height, planes, bitsPerPixel, lpvBits);
|
||||
return hBitmap;
|
||||
}
|
||||
|
||||
[SecurityCritical, SuppressUnmanagedCodeSecurity]
|
||||
[DllImport(ExternDll.Kernel32, EntryPoint = "GetModuleFileName", CharSet = CharSet.Unicode, SetLastError = true)]
|
||||
private static extern int IntGetModuleFileName(HandleRef hModule, StringBuilder buffer, int length);
|
||||
|
||||
[SecurityCritical]
|
||||
internal static string GetModuleFileName(HandleRef hModule)
|
||||
{
|
||||
var buffer = new StringBuilder(Win32Constant.MAX_PATH);
|
||||
while (true)
|
||||
{
|
||||
var size = IntGetModuleFileName(hModule, buffer, buffer.Capacity);
|
||||
if (size == 0)
|
||||
{
|
||||
throw new Win32Exception();
|
||||
}
|
||||
|
||||
if (size == buffer.Capacity)
|
||||
{
|
||||
buffer.EnsureCapacity(buffer.Capacity * 2);
|
||||
continue;
|
||||
}
|
||||
|
||||
return buffer.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
[SecurityCritical, SuppressUnmanagedCodeSecurity]
|
||||
[DllImport(ExternDll.Shell32, CharSet = CharSet.Auto, BestFitMapping = false, ThrowOnUnmappableChar = true)]
|
||||
internal static extern int ExtractIconEx(string szExeFileName, int nIconIndex, out IconHandle phiconLarge, out IconHandle phiconSmall, int nIcons);
|
||||
|
||||
[DllImport(ExternDll.Shell32, CharSet = CharSet.Auto)]
|
||||
public static extern int Shell_NotifyIcon(int message, NOTIFYICONDATA pnid);
|
||||
|
||||
[SecurityCritical]
|
||||
[SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
|
||||
[DllImport(ExternDll.User32, SetLastError = true, CharSet = CharSet.Unicode, EntryPoint = "CreateWindowExW")]
|
||||
public static extern IntPtr CreateWindowEx(
|
||||
int dwExStyle,
|
||||
[MarshalAs(UnmanagedType.LPWStr)] string lpClassName,
|
||||
[MarshalAs(UnmanagedType.LPWStr)] string lpWindowName,
|
||||
int dwStyle,
|
||||
int x,
|
||||
int y,
|
||||
int nWidth,
|
||||
int nHeight,
|
||||
IntPtr hWndParent,
|
||||
IntPtr hMenu,
|
||||
IntPtr hInstance,
|
||||
IntPtr lpParam);
|
||||
|
||||
[SecurityCritical]
|
||||
[SuppressUnmanagedCodeSecurity]
|
||||
[DllImport(ExternDll.User32, CharSet = CharSet.Unicode, SetLastError = true, BestFitMapping = false)]
|
||||
public static extern short RegisterClass(WNDCLASS wc);
|
||||
|
||||
[DllImport(ExternDll.User32, CharSet = CharSet.Auto)]
|
||||
public static extern IntPtr DefWindowProc(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam);
|
||||
|
||||
[DllImport(ExternDll.User32, ExactSpelling = true, CharSet = CharSet.Auto)]
|
||||
public static extern bool GetCursorPos([In, Out] POINT pt);
|
||||
|
||||
[DllImport(ExternDll.User32, ExactSpelling = true, CharSet = CharSet.Auto)]
|
||||
public static extern bool SetForegroundWindow(IntPtr hWnd);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
using System;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace HandyControl.Tools.Interop
|
||||
{
|
||||
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
|
||||
[SuppressMessage("ReSharper", "InconsistentNaming")]
|
||||
[SuppressMessage("ReSharper", "IdentifierTypo")]
|
||||
internal class WNDCLASS
|
||||
{
|
||||
public int style;
|
||||
public WndProc lpfnWndProc;
|
||||
public int cbClsExtra;
|
||||
public int cbWndExtra;
|
||||
public IntPtr hInstance;
|
||||
public IntPtr hIcon;
|
||||
public IntPtr hCursor;
|
||||
public IntPtr hbrBackground;
|
||||
public string lpszMenuName;
|
||||
public string lpszClassName;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
using System.Security;
|
||||
using Microsoft.Win32.SafeHandles;
|
||||
|
||||
namespace HandyControl.Tools.Interop
|
||||
{
|
||||
internal abstract class WpfSafeHandle : SafeHandleZeroOrMinusOneIsInvalid
|
||||
{
|
||||
private readonly int _collectorId;
|
||||
|
||||
[SecurityCritical]
|
||||
protected WpfSafeHandle(bool ownsHandle, int collectorId) : base(ownsHandle)
|
||||
{
|
||||
HandleCollector.Add(collectorId);
|
||||
_collectorId = collectorId;
|
||||
}
|
||||
|
||||
[SecurityCritical, SecuritySafeCritical]
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
HandleCollector.Remove(_collectorId);
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,369 @@
|
||||
using System.Windows;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media;
|
||||
using HandyControl.Data;
|
||||
using HandyControl.Interactivity;
|
||||
|
||||
namespace HandyControl.Tools
|
||||
{
|
||||
/// <summary>
|
||||
/// 鼠标拖动行为,该类是反编译微软的System.Windows.Interactivity程序集得到的,并对其做了些扩展
|
||||
/// </summary>
|
||||
internal class MouseDragElementBehaviorEx : Behavior<FrameworkElement>
|
||||
{
|
||||
public static readonly DependencyProperty XProperty = DependencyProperty.Register(nameof(X), typeof(double),
|
||||
typeof(MouseDragElementBehaviorEx), new PropertyMetadata(double.NaN, OnXChanged));
|
||||
|
||||
public static readonly DependencyProperty YProperty = DependencyProperty.Register(nameof(Y), typeof(double),
|
||||
typeof(MouseDragElementBehaviorEx), new PropertyMetadata(double.NaN, OnYChanged));
|
||||
|
||||
public static readonly DependencyProperty ConstrainToParentBoundsProperty =
|
||||
DependencyProperty.Register(nameof(ConstrainToParentBounds), typeof(bool),
|
||||
typeof(MouseDragElementBehaviorEx), new PropertyMetadata(ValueBoxes.FalseBox, OnConstrainToParentBoundsChanged));
|
||||
|
||||
private Transform _cachedRenderTransform;
|
||||
private Point _relativePosition;
|
||||
private bool _settingPosition;
|
||||
|
||||
/// <summary>
|
||||
/// 是否固定住Y轴
|
||||
/// </summary>
|
||||
public bool LockY { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 是否固定住X轴
|
||||
/// </summary>
|
||||
public bool LockX { get; set; }
|
||||
|
||||
public double X
|
||||
{
|
||||
get => (double)GetValue(MouseDragElementBehavior.XProperty);
|
||||
set => SetValue(MouseDragElementBehavior.XProperty, value);
|
||||
}
|
||||
|
||||
public double Y
|
||||
{
|
||||
get => (double)GetValue(MouseDragElementBehavior.YProperty);
|
||||
set => SetValue(MouseDragElementBehavior.YProperty, value);
|
||||
}
|
||||
|
||||
public bool ConstrainToParentBounds
|
||||
{
|
||||
get => (bool)GetValue(MouseDragElementBehavior.ConstrainToParentBoundsProperty);
|
||||
set => SetValue(MouseDragElementBehavior.ConstrainToParentBoundsProperty, value);
|
||||
}
|
||||
|
||||
private Rect ElementBounds
|
||||
{
|
||||
get
|
||||
{
|
||||
var layoutRect = ArithmeticHelper.GetLayoutRect(AssociatedObject);
|
||||
return new Rect(new Point(0.0, 0.0), new Size(layoutRect.Width, layoutRect.Height));
|
||||
}
|
||||
}
|
||||
|
||||
private FrameworkElement ParentElement => AssociatedObject.Parent as FrameworkElement;
|
||||
|
||||
private UIElement RootElement
|
||||
{
|
||||
get
|
||||
{
|
||||
DependencyObject reference = AssociatedObject;
|
||||
for (var dependencyObject = reference;
|
||||
dependencyObject != null;
|
||||
dependencyObject = VisualTreeHelper.GetParent(reference))
|
||||
reference = dependencyObject;
|
||||
return reference as UIElement;
|
||||
}
|
||||
}
|
||||
|
||||
private Transform RenderTransform
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_cachedRenderTransform == null ||
|
||||
!ReferenceEquals(_cachedRenderTransform, AssociatedObject.RenderTransform))
|
||||
RenderTransform = CloneTransform(AssociatedObject.RenderTransform);
|
||||
return _cachedRenderTransform;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (Equals(_cachedRenderTransform, value))
|
||||
return;
|
||||
_cachedRenderTransform = value;
|
||||
AssociatedObject.RenderTransform = value;
|
||||
}
|
||||
}
|
||||
|
||||
public event MouseEventHandler DragBegun;
|
||||
|
||||
public event MouseEventHandler Dragging;
|
||||
|
||||
public event MouseEventHandler DragFinished;
|
||||
|
||||
private static void OnXChanged(object sender, DependencyPropertyChangedEventArgs args)
|
||||
{
|
||||
var dragElementBehavior = (MouseDragElementBehaviorEx)sender;
|
||||
dragElementBehavior.UpdatePosition(new Point((double)args.NewValue, dragElementBehavior.Y));
|
||||
}
|
||||
|
||||
private static void OnYChanged(object sender, DependencyPropertyChangedEventArgs args)
|
||||
{
|
||||
var dragElementBehavior = (MouseDragElementBehaviorEx)sender;
|
||||
dragElementBehavior.UpdatePosition(new Point(dragElementBehavior.X, (double)args.NewValue));
|
||||
}
|
||||
|
||||
private static void OnConstrainToParentBoundsChanged(object sender, DependencyPropertyChangedEventArgs args)
|
||||
{
|
||||
var dragElementBehavior = (MouseDragElementBehaviorEx)sender;
|
||||
dragElementBehavior.UpdatePosition(new Point(dragElementBehavior.X, dragElementBehavior.Y));
|
||||
}
|
||||
|
||||
private void UpdatePosition(Point point)
|
||||
{
|
||||
if (_settingPosition || AssociatedObject == null)
|
||||
return;
|
||||
var transformOffset = GetTransformOffset(AssociatedObject.TransformToVisual(RootElement));
|
||||
ApplyTranslation(double.IsNaN(point.X) ? 0.0 : point.X - transformOffset.X,
|
||||
double.IsNaN(point.Y) ? 0.0 : point.Y - transformOffset.Y);
|
||||
}
|
||||
|
||||
private void ApplyTranslation(double x, double y)
|
||||
{
|
||||
if (ParentElement == null)
|
||||
return;
|
||||
var point = TransformAsVector(RootElement.TransformToVisual(ParentElement), x, y);
|
||||
x = point.X;
|
||||
y = point.Y;
|
||||
if (ConstrainToParentBounds)
|
||||
{
|
||||
var parentElement = ParentElement;
|
||||
var rect1 = new Rect(0.0, 0.0, parentElement.ActualWidth, parentElement.ActualHeight);
|
||||
var rect2 = AssociatedObject.TransformToVisual(parentElement).TransformBounds(ElementBounds);
|
||||
rect2.X += x;
|
||||
rect2.Y += y;
|
||||
if (!RectContainsRect(rect1, rect2))
|
||||
{
|
||||
if (rect2.X < rect1.Left)
|
||||
{
|
||||
var num = rect2.X - rect1.Left;
|
||||
x -= num;
|
||||
}
|
||||
else if (rect2.Right > rect1.Right)
|
||||
{
|
||||
var num = rect2.Right - rect1.Right;
|
||||
x -= num;
|
||||
}
|
||||
if (rect2.Y < rect1.Top)
|
||||
{
|
||||
var num = rect2.Y - rect1.Top;
|
||||
y -= num;
|
||||
}
|
||||
else if (rect2.Bottom > rect1.Bottom)
|
||||
{
|
||||
var num = rect2.Bottom - rect1.Bottom;
|
||||
y -= num;
|
||||
}
|
||||
}
|
||||
}
|
||||
ApplyTranslationTransform(x, y);
|
||||
}
|
||||
|
||||
internal void ApplyTranslationTransform(double x, double y)
|
||||
{
|
||||
var renderTransform = RenderTransform;
|
||||
var translateTransform = renderTransform as TranslateTransform;
|
||||
if (translateTransform == null)
|
||||
{
|
||||
var matrixTransform = renderTransform as MatrixTransform;
|
||||
if (renderTransform is TransformGroup transformGroup1)
|
||||
{
|
||||
if (transformGroup1.Children.Count > 0)
|
||||
translateTransform =
|
||||
transformGroup1.Children[transformGroup1.Children.Count - 1] as TranslateTransform;
|
||||
if (translateTransform == null)
|
||||
{
|
||||
translateTransform = new TranslateTransform();
|
||||
transformGroup1.Children.Add(translateTransform);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (matrixTransform != null)
|
||||
{
|
||||
var matrix = matrixTransform.Matrix;
|
||||
//在该处对微软的类进行了修改
|
||||
if (!LockX)
|
||||
{
|
||||
matrix.OffsetX += x;
|
||||
}
|
||||
if (!LockY)
|
||||
{
|
||||
matrix.OffsetY += y;
|
||||
}
|
||||
//修改结束
|
||||
RenderTransform = new MatrixTransform
|
||||
{
|
||||
Matrix = matrix
|
||||
};
|
||||
return;
|
||||
}
|
||||
var transformGroup2 = new TransformGroup();
|
||||
translateTransform = new TranslateTransform();
|
||||
if (renderTransform != null)
|
||||
transformGroup2.Children.Add(renderTransform);
|
||||
transformGroup2.Children.Add(translateTransform);
|
||||
RenderTransform = transformGroup2;
|
||||
}
|
||||
}
|
||||
//在该处对微软的类进行了修改
|
||||
if (!LockX)
|
||||
{
|
||||
translateTransform.X += x;
|
||||
}
|
||||
if (!LockY)
|
||||
{
|
||||
translateTransform.Y += y;
|
||||
}
|
||||
//修改结束
|
||||
}
|
||||
|
||||
internal static Transform CloneTransform(Transform transform)
|
||||
{
|
||||
if (transform == null)
|
||||
return null;
|
||||
if (transform is ScaleTransform scaleTransform)
|
||||
return new ScaleTransform
|
||||
{
|
||||
CenterX = scaleTransform.CenterX,
|
||||
CenterY = scaleTransform.CenterY,
|
||||
ScaleX = scaleTransform.ScaleX,
|
||||
ScaleY = scaleTransform.ScaleY
|
||||
};
|
||||
if (transform is RotateTransform rotateTransform)
|
||||
return new RotateTransform
|
||||
{
|
||||
Angle = rotateTransform.Angle,
|
||||
CenterX = rotateTransform.CenterX,
|
||||
CenterY = rotateTransform.CenterY
|
||||
};
|
||||
if (transform is SkewTransform skewTransform)
|
||||
return new SkewTransform
|
||||
{
|
||||
AngleX = skewTransform.AngleX,
|
||||
AngleY = skewTransform.AngleY,
|
||||
CenterX = skewTransform.CenterX,
|
||||
CenterY = skewTransform.CenterY
|
||||
};
|
||||
if (transform is TranslateTransform translateTransform)
|
||||
return new TranslateTransform
|
||||
{
|
||||
X = translateTransform.X,
|
||||
Y = translateTransform.Y
|
||||
};
|
||||
if (transform is MatrixTransform matrixTransform)
|
||||
return new MatrixTransform
|
||||
{
|
||||
Matrix = matrixTransform.Matrix
|
||||
};
|
||||
if (!(transform is TransformGroup transformGroup1))
|
||||
return null;
|
||||
var transformGroup2 = new TransformGroup();
|
||||
foreach (var child in transformGroup1.Children)
|
||||
transformGroup2.Children.Add(CloneTransform(child));
|
||||
return transformGroup2;
|
||||
}
|
||||
|
||||
private void UpdatePosition()
|
||||
{
|
||||
var transformOffset = GetTransformOffset(AssociatedObject.TransformToVisual(RootElement));
|
||||
X = transformOffset.X;
|
||||
Y = transformOffset.Y;
|
||||
}
|
||||
|
||||
internal void StartDrag(Point positionInElementCoordinates)
|
||||
{
|
||||
_relativePosition = positionInElementCoordinates;
|
||||
AssociatedObject.CaptureMouse();
|
||||
AssociatedObject.MouseMove += OnMouseMove;
|
||||
AssociatedObject.LostMouseCapture += OnLostMouseCapture;
|
||||
AssociatedObject.AddHandler(UIElement.MouseLeftButtonUpEvent,
|
||||
new MouseButtonEventHandler(OnMouseLeftButtonUp), false);
|
||||
}
|
||||
|
||||
internal void HandleDrag(Point newPositionInElementCoordinates)
|
||||
{
|
||||
var point = TransformAsVector(AssociatedObject.TransformToVisual(RootElement),
|
||||
newPositionInElementCoordinates.X - _relativePosition.X,
|
||||
newPositionInElementCoordinates.Y - _relativePosition.Y);
|
||||
_settingPosition = true;
|
||||
ApplyTranslation(point.X, point.Y);
|
||||
UpdatePosition();
|
||||
_settingPosition = false;
|
||||
}
|
||||
|
||||
internal void EndDrag()
|
||||
{
|
||||
AssociatedObject.MouseMove -= OnMouseMove;
|
||||
AssociatedObject.LostMouseCapture -= OnLostMouseCapture;
|
||||
AssociatedObject.RemoveHandler(UIElement.MouseLeftButtonUpEvent,
|
||||
new MouseButtonEventHandler(OnMouseLeftButtonUp));
|
||||
}
|
||||
|
||||
private void OnMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
|
||||
{
|
||||
StartDrag(e.GetPosition(AssociatedObject));
|
||||
DragBegun?.Invoke(this, e);
|
||||
}
|
||||
|
||||
private void OnLostMouseCapture(object sender, MouseEventArgs e)
|
||||
{
|
||||
EndDrag();
|
||||
DragFinished?.Invoke(this, e);
|
||||
}
|
||||
|
||||
private void OnMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
|
||||
{
|
||||
AssociatedObject.ReleaseMouseCapture();
|
||||
}
|
||||
|
||||
private void OnMouseMove(object sender, MouseEventArgs e)
|
||||
{
|
||||
HandleDrag(e.GetPosition(AssociatedObject));
|
||||
Dragging?.Invoke(this, e);
|
||||
}
|
||||
|
||||
private static bool RectContainsRect(Rect rect1, Rect rect2)
|
||||
{
|
||||
if (rect1.IsEmpty || rect2.IsEmpty || rect1.X > rect2.X || rect1.Y > rect2.Y ||
|
||||
rect1.X + rect1.Width < rect2.X + rect2.Width)
|
||||
return false;
|
||||
return rect1.Y + rect1.Height >= rect2.Y + rect2.Height;
|
||||
}
|
||||
|
||||
private static Point TransformAsVector(GeneralTransform transform, double x, double y)
|
||||
{
|
||||
var point1 = transform.Transform(new Point(0.0, 0.0));
|
||||
var point2 = transform.Transform(new Point(x, y));
|
||||
return new Point(point2.X - point1.X, point2.Y - point1.Y);
|
||||
}
|
||||
|
||||
private static Point GetTransformOffset(GeneralTransform transform)
|
||||
{
|
||||
return transform.Transform(new Point(0.0, 0.0));
|
||||
}
|
||||
|
||||
protected override void OnAttached()
|
||||
{
|
||||
AssociatedObject.AddHandler(UIElement.MouseLeftButtonDownEvent,
|
||||
new MouseButtonEventHandler(OnMouseLeftButtonDown), false);
|
||||
}
|
||||
|
||||
protected override void OnDetaching()
|
||||
{
|
||||
AssociatedObject.RemoveHandler(UIElement.MouseLeftButtonDownEvent,
|
||||
new MouseButtonEventHandler(OnMouseLeftButtonDown));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
using System;
|
||||
using System.Text.RegularExpressions;
|
||||
using HandyControl.Data;
|
||||
|
||||
namespace HandyControl.Tools
|
||||
{
|
||||
/// <summary>
|
||||
/// 包含一些正则验证操作
|
||||
/// </summary>
|
||||
public static class RegularJudgment
|
||||
{
|
||||
private static readonly RegularPatterns RegularPatterns = new RegularPatterns();
|
||||
|
||||
/// <summary>
|
||||
/// 判断字符串格式是否符合某种要求
|
||||
/// </summary>
|
||||
/// <param name="str">需要判断的字符串</param>
|
||||
/// <param name="pattern">正则表达式</param>
|
||||
/// <returns></returns>
|
||||
public static bool IsKindOf(this string str, string pattern)
|
||||
{
|
||||
return Regex.IsMatch(str, pattern);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 判断字符串是否满足指定的格式
|
||||
/// </summary>
|
||||
/// <param name="text">需要判断的字符串</param>
|
||||
/// <param name="textType">指定格式的文本</param>
|
||||
/// <returns></returns>
|
||||
public static bool IsKindOf(this string text, TextType textType)
|
||||
{
|
||||
if (textType == TextType.Common) return true;
|
||||
return Regex.IsMatch(text,
|
||||
RegularPatterns.GetValue(Enum.GetName(typeof(TextType), textType) + "Pattern").ToString());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 判断字符串格式是否为电子邮件
|
||||
/// </summary>
|
||||
/// <param name="email">需要判断的Email字符串</param>
|
||||
/// <returns>方法返回布尔值</returns>
|
||||
public static bool IsEmail(this string email)
|
||||
{
|
||||
return Regex.IsMatch(email, RegularPatterns.MailPattern);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 判断字符串格式是否为指定类型的IP地址
|
||||
/// </summary>
|
||||
/// <param name="ip">需要判断的IP字符串</param>
|
||||
/// <param name="ipType">指定的IP类型</param>
|
||||
/// <returns>方法返回布尔值</returns>
|
||||
public static bool IsIp(this string ip, IpType ipType)
|
||||
{
|
||||
switch (ipType)
|
||||
{
|
||||
case IpType.A: return Regex.IsMatch(ip, RegularPatterns.IpAPattern);
|
||||
case IpType.B: return Regex.IsMatch(ip, RegularPatterns.IpBPattern);
|
||||
case IpType.C: return Regex.IsMatch(ip, RegularPatterns.IpCPattern);
|
||||
case IpType.D: return Regex.IsMatch(ip, RegularPatterns.IpDPattern);
|
||||
case IpType.E: return Regex.IsMatch(ip, RegularPatterns.IpEPattern);
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 判断字符串格式是否为IP地址
|
||||
/// </summary>
|
||||
/// <param name="ip">需要判断的IP字符串</param>
|
||||
/// <returns>方法返回布尔值</returns>
|
||||
public static bool IsIp(this string ip)
|
||||
{
|
||||
return Regex.IsMatch(ip, RegularPatterns.IpPattern);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 判断字符串格式是否为单个汉字
|
||||
/// </summary>
|
||||
/// <param name="str">需要判断的单个汉字字符串</param>
|
||||
/// <returns>方法返回布尔值</returns>
|
||||
public static bool IsChinese(this string str)
|
||||
{
|
||||
return Regex.IsMatch(str, RegularPatterns.ChinesePattern);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 判断字符串格式是否为url
|
||||
/// </summary>
|
||||
/// <param name="str">需要判断的url字符串</param>
|
||||
/// <returns>方法返回布尔值</returns>
|
||||
public static bool IsUrl(this string str)
|
||||
{
|
||||
return Regex.IsMatch(str, RegularPatterns.UrlPattern);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,151 @@
|
||||
namespace HandyControl.Tools
|
||||
{
|
||||
/// <summary>
|
||||
/// 包含一些正则验证所需要的字符串
|
||||
/// </summary>
|
||||
public sealed class RegularPatterns
|
||||
{
|
||||
/// <summary>
|
||||
/// 邮件正则匹配表达式
|
||||
/// </summary>
|
||||
public const string MailPattern =
|
||||
@"^([\w-\.]+)@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.)|(([\w-]+\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\]?)$";
|
||||
|
||||
/// <summary>
|
||||
/// 手机号正则匹配表达式
|
||||
/// </summary>
|
||||
public const string PhonePattern = @"^((13[0-9])|(15[^4,\d])|(18[0,5-9]))\d{8}$";
|
||||
|
||||
/// <summary>
|
||||
/// IP正则匹配
|
||||
/// </summary>
|
||||
public const string IpPattern =
|
||||
@"^(25[0-5]|2[0-4]\d|1\d{2}|[1-9]?\d)\."
|
||||
+ @"(25[0-5]|2[0-4]\d|1\d{2}|[1-9]?\d)\."
|
||||
+ @"(25[0-5]|2[0-4]\d|1\d{2}|[1-9]?\d)\."
|
||||
+ @"(25[0-5]|2[0-4]\d|1\d{2}|[1-9]?\d)$";
|
||||
|
||||
/// <summary>
|
||||
/// A类IP正则匹配
|
||||
/// </summary>
|
||||
public const string IpAPattern =
|
||||
@"^(12[0-6]|1[0-1]\d|[1-9]?\d)\."
|
||||
+ @"(25[0-5]|2[0-4]\d|1\d{2}|[1-9]?\d)\."
|
||||
+ @"(25[0-5]|2[0-4]\d|1\d{2}|[1-9]?\d)\."
|
||||
+ @"(25[0-5]|2[0-4]\d|1\d{2}|[1-9]?\d)$";
|
||||
|
||||
/// <summary>
|
||||
/// B类IP正则匹配
|
||||
/// </summary>
|
||||
public const string IpBPattern =
|
||||
@"^(19[0-1]|12[8-9]|1[3-8]\d)\."
|
||||
+ @"(25[0-5]|2[0-4]\d|1\d{2}|[1-9]?\d)\."
|
||||
+ @"(25[0-5]|2[0-4]\d|1\d{2}|[1-9]?\d)\."
|
||||
+ @"(25[0-5]|2[0-4]\d|1\d{2}|[1-9]?\d)$";
|
||||
|
||||
/// <summary>
|
||||
/// C类IP正则匹配
|
||||
/// </summary>
|
||||
public const string IpCPattern =
|
||||
@"^(19[2-9]|22[0-3]|2[0-1]\d)\."
|
||||
+ @"(25[0-5]|2[0-4]\d|1\d{2}|[1-9]?\d)\."
|
||||
+ @"(25[0-5]|2[0-4]\d|1\d{2}|[1-9]?\d)\."
|
||||
+ @"(25[0-5]|2[0-4]\d|1\d{2}|[1-9]?\d)$";
|
||||
|
||||
/// <summary>
|
||||
/// D类IP正则匹配
|
||||
/// </summary>
|
||||
public const string IpDPattern =
|
||||
@"^(22[4-9]|23\d\."
|
||||
+ @"(25[0-5]|2[0-4]\d|1\d{2}|[1-9]?\d)\."
|
||||
+ @"(25[0-5]|2[0-4]\d|1\d{2}|[1-9]?\d)\."
|
||||
+ @"(25[0-5]|2[0-4]\d|1\d{2}|[1-9]?\d)$";
|
||||
|
||||
/// <summary>
|
||||
/// E类IP正则匹配
|
||||
/// </summary>
|
||||
public const string IpEPattern =
|
||||
@"^(25[0-5]|24\d\."
|
||||
+ @"(25[0-5]|2[0-4]\d|1\d{2}|[1-9]?\d)\."
|
||||
+ @"(25[0-5]|2[0-4]\d|1\d{2}|[1-9]?\d)\."
|
||||
+ @"(25[0-5]|2[0-4]\d|1\d{2}|[1-9]?\d)$";
|
||||
|
||||
/// <summary>
|
||||
/// 汉字正则匹配
|
||||
/// </summary>
|
||||
public const string ChinesePattern = @"^[\u4e00-\u9fa5]$";
|
||||
|
||||
/// <summary>
|
||||
/// Url正则匹配
|
||||
/// </summary>
|
||||
public const string UrlPattern =
|
||||
@"((http|ftp|https)://)(([a-zA-Z0-9\._-]+\.[a-zA-Z]{2,6})|([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}))(:[0-9]{1,4})*(/[a-zA-Z0-9\&%_\./-~-]*)?";
|
||||
|
||||
/// <summary>
|
||||
/// 数字正则匹配
|
||||
/// </summary>
|
||||
public const string NumberPattern = @"^\d$";
|
||||
|
||||
/// <summary>
|
||||
/// 计算性质数字正则匹配
|
||||
/// </summary>
|
||||
public const string DigitsPattern = @"^[+-]?([0-9]*\.?[0-9]+|[0-9]+\.?[0-9]*)([eE][+-]?[0-9]+)?$";
|
||||
|
||||
/// <summary>
|
||||
/// 正整数正则匹配
|
||||
/// </summary>
|
||||
public const string PIntPattern = @"^[1-9]\d*$";
|
||||
|
||||
/// <summary>
|
||||
/// 负整数正则匹配
|
||||
/// </summary>
|
||||
public const string NIntPattern = @"^-[1-9]\d*$ ";
|
||||
|
||||
/// <summary>
|
||||
/// 整数正则匹配
|
||||
/// </summary>
|
||||
public const string IntPattern = @"^-?[1-9]\d*$";
|
||||
|
||||
/// <summary>
|
||||
/// 非负整数正则匹配
|
||||
/// </summary>
|
||||
public const string NnIntPattern = @"^[1-9]\d*|0$";
|
||||
|
||||
/// <summary>
|
||||
/// 非正整数正则匹配
|
||||
/// </summary>
|
||||
public const string NpIntPattern = @"^-[1-9]\d*|0$";
|
||||
|
||||
/// <summary>
|
||||
/// 正浮点数正则匹配
|
||||
/// </summary>
|
||||
public const string PDoublePattern = @"^[1-9]\d*\.\d*|0\.\d*[1-9]\d*$";
|
||||
|
||||
/// <summary>
|
||||
/// 负浮点数正则匹配
|
||||
/// </summary>
|
||||
public const string NDoublePattern = @"^-([1-9]\d*\.\d*|0\.\d*[1-9]\d*)$";
|
||||
|
||||
/// <summary>
|
||||
/// 浮点数正则匹配
|
||||
/// </summary>
|
||||
public const string DoublePattern = @"^-?([1-9]\d*\.\d*|0\.\d*[1-9]\d*|0?\.0+|0)$";
|
||||
|
||||
/// <summary>
|
||||
/// 非负浮点数正则匹配
|
||||
/// </summary>
|
||||
public const string NnDoublePattern = @"^[1-9]\d*\.\d*|0\.\d*[1-9]\d*|0?\.0+|0$";
|
||||
|
||||
/// <summary>
|
||||
/// 非正浮点数正则匹配
|
||||
/// </summary>
|
||||
public const string NpDoublePattern = @"^(-([1-9]\d*\.\d*|0\.\d*[1-9]\d*))|0?\.0+|0$";
|
||||
|
||||
/// <summary>
|
||||
/// 根据属性名称使用反射来获取值
|
||||
/// </summary>
|
||||
/// <param name="propertyName"></param>
|
||||
/// <returns></returns>
|
||||
public object GetValue(string propertyName) => GetType().GetField(propertyName).GetValue(null);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using HandyControl.Controls;
|
||||
using HandyControl.Data;
|
||||
|
||||
namespace HandyControl.Tools
|
||||
{
|
||||
public class RadioGroupItemStyleSelector : StyleSelector
|
||||
{
|
||||
public override Style SelectStyle(object item, DependencyObject container)
|
||||
{
|
||||
if (container is RadioGroup radioGroup && item is UIElement radioButton)
|
||||
{
|
||||
var count = radioGroup.Items.Count;
|
||||
if (count == 1)
|
||||
{
|
||||
return ResourceHelper.GetResource<Style>(ResourceToken.RadioGroupItemSingle);
|
||||
}
|
||||
|
||||
var index = radioGroup.Items.IndexOf(radioButton);
|
||||
return radioGroup.Orientation == Orientation.Horizontal
|
||||
? index == 0
|
||||
? ResourceHelper.GetResource<Style>(ResourceToken.RadioGroupItemHorizontalFirst)
|
||||
: ResourceHelper.GetResource<Style>(index == count - 1
|
||||
? ResourceToken.RadioGroupItemHorizontalLast
|
||||
: ResourceToken.RadioGroupItemDefault)
|
||||
: index == 0
|
||||
? ResourceHelper.GetResource<Style>(ResourceToken.RadioGroupItemVerticalFirst)
|
||||
: ResourceHelper.GetResource<Style>(index == count - 1
|
||||
? ResourceToken.RadioGroupItemVerticalLast
|
||||
: ResourceToken.RadioGroupItemDefault);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using HandyControl.Data;
|
||||
|
||||
namespace HandyControl.Tools
|
||||
{
|
||||
public class TabItemCapsuleStyleSelector : StyleSelector
|
||||
{
|
||||
public override Style SelectStyle(object item, DependencyObject container)
|
||||
{
|
||||
if (item is TabItem tabItem && tabItem.Parent is TabControl tabControl)
|
||||
{
|
||||
var count = tabControl.Items.Count;
|
||||
if (count == 1)
|
||||
{
|
||||
return ResourceHelper.GetResource<Style>(ResourceToken.TabItemCapsuleSingle);
|
||||
}
|
||||
|
||||
var index = tabControl.Items.IndexOf(tabItem);
|
||||
return index == 0
|
||||
? ResourceHelper.GetResource<Style>(ResourceToken.TabItemCapsuleHorizontalFirst)
|
||||
: ResourceHelper.GetResource<Style>(index == count - 1
|
||||
? ResourceToken.TabItemCapsuleHorizontalLast
|
||||
: ResourceToken.TabItemCapsuleDefault);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user