chore: use file-scoped namespace

This commit is contained in:
NaBian committed 2022-03-13 20:28:58 +08:00
1 parent 99d2fafb72
commit bbf1b7d4c6
748 files changed
+57929 -58678

No files matched your search

@@ -2,26 +2,25 @@
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;
}
namespace HandyControl.Tools.Converter;
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
public class Boolean2BooleanReConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is bool boolValue)
{
if (value is bool boolValue)
{
return !boolValue;
}
return value;
return !boolValue;
}
return value;
}
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is bool boolValue)
{
return !boolValue;
}
return value;
}
}
@@ -2,31 +2,30 @@
using System.Globalization;
using System.Windows.Data;
namespace HandyControl.Tools.Converter
namespace HandyControl.Tools.Converter;
public class Boolean2StringConverter : IValueConverter
{
public class Boolean2StringConverter : IValueConverter
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
if (value is bool boolValue)
{
if (value is bool boolValue)
if (parameter is string str)
{
if (parameter is string str)
var arr = str.Split(';');
if (arr.Length > 1)
{
var arr = str.Split(';');
if (arr.Length > 1)
{
return boolValue ? arr[1] : arr[0];
}
return "";
return boolValue ? arr[1] : arr[0];
}
return "";
}
return "";
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotSupportedException();
}
return "";
}
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotSupportedException();
}
}
@@ -3,18 +3,17 @@ 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;
}
namespace HandyControl.Tools.Converter;
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotSupportedException();
}
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 NotSupportedException();
}
}
@@ -5,36 +5,35 @@ using System.Linq;
using System.Windows.Data;
using HandyControl.Data;
namespace HandyControl.Tools.Converter
namespace HandyControl.Tools.Converter;
public class BooleanArr2BooleanConverter : IMultiValueConverter
{
public class BooleanArr2BooleanConverter : IMultiValueConverter
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
if (values == null)
{
if (values == null)
return ValueBoxes.FalseBox;
}
var arr = new List<bool>();
foreach (var item in values)
{
if (item is bool boolValue)
{
arr.Add(boolValue);
}
else
{
return ValueBoxes.FalseBox;
}
var arr = new List<bool>();
foreach (var item in values)
{
if (item is bool boolValue)
{
arr.Add(boolValue);
}
else
{
return ValueBoxes.FalseBox;
}
}
return ValueBoxes.BooleanBox(arr.All(item => item));
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotSupportedException();
}
return ValueBoxes.BooleanBox(arr.All(item => item));
}
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotSupportedException();
}
}
@@ -5,35 +5,34 @@ using System.Linq;
using System.Windows;
using System.Windows.Data;
namespace HandyControl.Tools.Converter
namespace HandyControl.Tools.Converter;
public class BooleanArr2VisibilityConverter : IMultiValueConverter
{
public class BooleanArr2VisibilityConverter : IMultiValueConverter
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
if (values == null)
{
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;
}
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 NotSupportedException();
}
return arr.All(item => item) ? Visibility.Visible : Visibility.Collapsed;
}
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotSupportedException();
}
}
@@ -6,31 +6,30 @@ using System.Windows;
using System.Windows.Data;
using System.Windows.Media;
namespace HandyControl.Tools.Converter
namespace HandyControl.Tools.Converter;
public class BorderCircularClipConverter : IMultiValueConverter
{
public class BorderCircularClipConverter : IMultiValueConverter
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
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 (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)
{
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 Geometry.Empty;
}
return DependencyProperty.UnsetValue;
var clip = new RectangleGeometry(new Rect(0, 0, width, height), radius.TopLeft, radius.TopLeft);
clip.Freeze();
return clip;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotSupportedException();
}
return DependencyProperty.UnsetValue;
}
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotSupportedException();
}
}
@@ -3,29 +3,28 @@ 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();
}
namespace HandyControl.Tools.Converter;
var min = Math.Min(width, height);
return new CornerRadius(min / 2);
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();
}
return DependencyProperty.UnsetValue;
var min = Math.Min(width, height);
return new CornerRadius(min / 2);
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotSupportedException();
}
return DependencyProperty.UnsetValue;
}
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotSupportedException();
}
}
@@ -4,47 +4,46 @@ using System.Windows;
using System.Windows.Data;
using System.Windows.Media;
namespace HandyControl.Tools.Converter
namespace HandyControl.Tools.Converter;
public class BorderClipConverter : IMultiValueConverter
{
public class BorderClipConverter : IMultiValueConverter
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
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 (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)
{
if (width < double.Epsilon || height < double.Epsilon)
{
return Geometry.Empty;
}
var clip = new PathGeometry
{
Figures = new PathFigureCollection
{
new(new Point(radius.TopLeft, 0), new PathSegment[]
{
new LineSegment(new Point(width - radius.TopRight, 0), false),
new ArcSegment(new Point(width, radius.TopRight), new Size(radius.TopRight, radius.TopRight), 90, false, SweepDirection.Clockwise, false),
new LineSegment(new Point(width, height - radius.BottomRight), false),
new ArcSegment(new Point(width - radius.BottomRight, height), new Size(radius.BottomRight, radius.BottomRight), 90, false, SweepDirection.Clockwise, false),
new LineSegment(new Point(radius.BottomLeft, height), false),
new ArcSegment(new Point(0, height - radius.BottomLeft), new Size(radius.BottomLeft, radius.BottomLeft), 90, false, SweepDirection.Clockwise, false),
new LineSegment(new Point(0, radius.TopLeft), false),
new ArcSegment(new Point(radius.TopLeft, 0), new Size(radius.TopLeft, radius.TopLeft), 90, false, SweepDirection.Clockwise, false),
}, false)
}
};
clip.Freeze();
return clip;
return Geometry.Empty;
}
return DependencyProperty.UnsetValue;
var clip = new PathGeometry
{
Figures = new PathFigureCollection
{
new(new Point(radius.TopLeft, 0), new PathSegment[]
{
new LineSegment(new Point(width - radius.TopRight, 0), false),
new ArcSegment(new Point(width, radius.TopRight), new Size(radius.TopRight, radius.TopRight), 90, false, SweepDirection.Clockwise, false),
new LineSegment(new Point(width, height - radius.BottomRight), false),
new ArcSegment(new Point(width - radius.BottomRight, height), new Size(radius.BottomRight, radius.BottomRight), 90, false, SweepDirection.Clockwise, false),
new LineSegment(new Point(radius.BottomLeft, height), false),
new ArcSegment(new Point(0, height - radius.BottomLeft), new Size(radius.BottomLeft, radius.BottomLeft), 90, false, SweepDirection.Clockwise, false),
new LineSegment(new Point(0, radius.TopLeft), false),
new ArcSegment(new Point(radius.TopLeft, 0), new Size(radius.TopLeft, radius.TopLeft), 90, false, SweepDirection.Clockwise, false),
}, false)
}
};
clip.Freeze();
return clip;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotSupportedException();
}
return DependencyProperty.UnsetValue;
}
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotSupportedException();
}
}
@@ -6,130 +6,129 @@ using System.Security;
using System.Text;
using HandyControl.Tools.Extension;
namespace HandyControl.Tools.Converter
namespace HandyControl.Tools.Converter;
public class ColLayoutConverter : TypeConverter
{
public class ColLayoutConverter : TypeConverter
public override bool CanConvertFrom(ITypeDescriptorContext typeDescriptorContext, Type sourceType)
{
public override bool CanConvertFrom(ITypeDescriptorContext typeDescriptorContext, Type sourceType)
switch (Type.GetTypeCode(sourceType))
{
switch (Type.GetTypeCode(sourceType))
{
case TypeCode.Int16:
case TypeCode.UInt16:
case TypeCode.Int32:
case TypeCode.UInt32:
case TypeCode.Int64:
case TypeCode.UInt64:
case TypeCode.Single:
case TypeCode.Double:
case TypeCode.Decimal:
case TypeCode.String:
return true;
default:
return false;
}
}
public override bool CanConvertTo(ITypeDescriptorContext typeDescriptorContext, Type destinationType) =>
destinationType == typeof(InstanceDescriptor) || destinationType == typeof(string);
public override object ConvertFrom(ITypeDescriptorContext typeDescriptorContext, CultureInfo cultureInfo, object source)
{
if (source == null) throw GetConvertFromException(null);
return source switch
{
string s => FromString(s, cultureInfo),
double d => new ColLayout((int) d),
_ => new ColLayout(Convert.ToInt32(source, cultureInfo))
};
}
[SecurityCritical]
public override object ConvertTo(ITypeDescriptorContext typeDescriptorContext, CultureInfo cultureInfo, object value, Type destinationType)
{
if (value == null) throw new ArgumentNullException(nameof(value));
if (destinationType == null) throw new ArgumentNullException(nameof(destinationType));
if (!(value is ColLayout th)) throw new ArgumentException("UnexpectedParameterType");
if (destinationType == typeof(string)) return ToString(th, cultureInfo);
if (destinationType == typeof(InstanceDescriptor))
{
var ci = typeof(ColLayout).GetConstructor(new[] { typeof(int), typeof(int), typeof(int), typeof(int), typeof(int), typeof(int) });
return new InstanceDescriptor(ci, new object[] { th.Xs, th.Sm, th.Md, th.Lg, th.Xl, th.Xxl });
}
throw new ArgumentException("CannotConvertType");
}
private static string ToString(ColLayout th, CultureInfo cultureInfo)
{
var listSeparator = TokenizerHelper.GetNumericListSeparator(cultureInfo);
// Initial capacity [128] is an estimate based on a sum of:
// 72 = 6x double (twelve digits is generous for the range of values likely)
// 4 = 4x separator characters
var sb = new StringBuilder(128);
sb.Append(th.Xs.ToString(cultureInfo));
sb.Append(listSeparator);
sb.Append(th.Sm.ToString(cultureInfo));
sb.Append(listSeparator);
sb.Append(th.Md.ToString(cultureInfo));
sb.Append(listSeparator);
sb.Append(th.Lg.ToString(cultureInfo));
sb.Append(listSeparator);
sb.Append(th.Xl.ToString(cultureInfo));
sb.Append(listSeparator);
sb.Append(th.Xxl.ToString(cultureInfo));
return th.ToString();
}
private static ColLayout FromString(string s, CultureInfo cultureInfo)
{
var th = new TokenizerHelper(s, cultureInfo);
var lengths = new int[6];
var i = 0;
while (th.NextToken())
{
if (i >= 6)
{
i = 7; // Set i to a bad value.
break;
}
lengths[i] = th.GetCurrentToken().Value<int>();
i++;
}
return i switch
{
1 => new ColLayout(lengths[0]),
2 => new ColLayout { Xs = lengths[0], Sm = lengths[1] },
3 => new ColLayout { Xs = lengths[0], Sm = lengths[1], Md = lengths[2] },
4 => new ColLayout { Xs = lengths[0], Sm = lengths[1], Md = lengths[2], Lg = lengths[3] },
5 => new ColLayout
{
Xs = lengths[0],
Sm = lengths[1],
Md = lengths[2],
Lg = lengths[3],
Xl = lengths[4]
},
6 => new ColLayout
{
Xs = lengths[0],
Sm = lengths[1],
Md = lengths[2],
Lg = lengths[3],
Xl = lengths[4],
Xxl = lengths[5]
},
_ => throw new FormatException("InvalidStringColLayout")
};
case TypeCode.Int16:
case TypeCode.UInt16:
case TypeCode.Int32:
case TypeCode.UInt32:
case TypeCode.Int64:
case TypeCode.UInt64:
case TypeCode.Single:
case TypeCode.Double:
case TypeCode.Decimal:
case TypeCode.String:
return true;
default:
return false;
}
}
}
public override bool CanConvertTo(ITypeDescriptorContext typeDescriptorContext, Type destinationType) =>
destinationType == typeof(InstanceDescriptor) || destinationType == typeof(string);
public override object ConvertFrom(ITypeDescriptorContext typeDescriptorContext, CultureInfo cultureInfo, object source)
{
if (source == null) throw GetConvertFromException(null);
return source switch
{
string s => FromString(s, cultureInfo),
double d => new ColLayout((int) d),
_ => new ColLayout(Convert.ToInt32(source, cultureInfo))
};
}
[SecurityCritical]
public override object ConvertTo(ITypeDescriptorContext typeDescriptorContext, CultureInfo cultureInfo, object value, Type destinationType)
{
if (value == null) throw new ArgumentNullException(nameof(value));
if (destinationType == null) throw new ArgumentNullException(nameof(destinationType));
if (!(value is ColLayout th)) throw new ArgumentException("UnexpectedParameterType");
if (destinationType == typeof(string)) return ToString(th, cultureInfo);
if (destinationType == typeof(InstanceDescriptor))
{
var ci = typeof(ColLayout).GetConstructor(new[] { typeof(int), typeof(int), typeof(int), typeof(int), typeof(int), typeof(int) });
return new InstanceDescriptor(ci, new object[] { th.Xs, th.Sm, th.Md, th.Lg, th.Xl, th.Xxl });
}
throw new ArgumentException("CannotConvertType");
}
private static string ToString(ColLayout th, CultureInfo cultureInfo)
{
var listSeparator = TokenizerHelper.GetNumericListSeparator(cultureInfo);
// Initial capacity [128] is an estimate based on a sum of:
// 72 = 6x double (twelve digits is generous for the range of values likely)
// 4 = 4x separator characters
var sb = new StringBuilder(128);
sb.Append(th.Xs.ToString(cultureInfo));
sb.Append(listSeparator);
sb.Append(th.Sm.ToString(cultureInfo));
sb.Append(listSeparator);
sb.Append(th.Md.ToString(cultureInfo));
sb.Append(listSeparator);
sb.Append(th.Lg.ToString(cultureInfo));
sb.Append(listSeparator);
sb.Append(th.Xl.ToString(cultureInfo));
sb.Append(listSeparator);
sb.Append(th.Xxl.ToString(cultureInfo));
return th.ToString();
}
private static ColLayout FromString(string s, CultureInfo cultureInfo)
{
var th = new TokenizerHelper(s, cultureInfo);
var lengths = new int[6];
var i = 0;
while (th.NextToken())
{
if (i >= 6)
{
i = 7; // Set i to a bad value.
break;
}
lengths[i] = th.GetCurrentToken().Value<int>();
i++;
}
return i switch
{
1 => new ColLayout(lengths[0]),
2 => new ColLayout { Xs = lengths[0], Sm = lengths[1] },
3 => new ColLayout { Xs = lengths[0], Sm = lengths[1], Md = lengths[2] },
4 => new ColLayout { Xs = lengths[0], Sm = lengths[1], Md = lengths[2], Lg = lengths[3] },
5 => new ColLayout
{
Xs = lengths[0],
Sm = lengths[1],
Md = lengths[2],
Lg = lengths[3],
Xl = lengths[4]
},
6 => new ColLayout
{
Xs = lengths[0],
Sm = lengths[1],
Md = lengths[2],
Lg = lengths[3],
Xl = lengths[4],
Xxl = lengths[5]
},
_ => throw new FormatException("InvalidStringColLayout")
};
}
}
@@ -3,37 +3,36 @@ 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;
}
namespace HandyControl.Tools.Converter;
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
public class Color2ChannelAConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is SolidColorBrush brush)
{
if (value is string str)
return brush.Color.A;
}
return 0;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is string str)
{
try
{
try
if (ColorConverter.ConvertFromString(str) is Color color)
{
if (ColorConverter.ConvertFromString(str) is Color color)
{
return new SolidColorBrush(color);
}
}
catch
{
return parameter;
return new SolidColorBrush(color);
}
}
catch
{
return parameter;
}
return parameter;
}
return parameter;
}
}
}
@@ -3,38 +3,37 @@ using System.Globalization;
using System.Windows.Data;
using System.Windows.Media;
namespace HandyControl.Tools.Converter
{
public class Color2HexStringConverter : 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 "";
}
namespace HandyControl.Tools.Converter;
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
public class Color2HexStringConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is SolidColorBrush brush)
{
if (value is string str)
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
{
try
if (ColorConverter.ConvertFromString(str) is Color color)
{
if (ColorConverter.ConvertFromString(str) is Color color)
{
return new SolidColorBrush(color);
}
}
catch
{
return new SolidColorBrush(default);
return new SolidColorBrush(color);
}
}
catch
{
return new SolidColorBrush(default);
}
return new SolidColorBrush(default);
}
return new SolidColorBrush(default);
}
}
}
@@ -3,32 +3,31 @@ using System.Globalization;
using System.Windows;
using System.Windows.Data;
namespace HandyControl.Tools.Converter
namespace HandyControl.Tools.Converter;
public class CornerRadiusSplitConverter : IValueConverter
{
public class CornerRadiusSplitConverter : IValueConverter
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
if (value is CornerRadius cornerRadius)
{
if (value is CornerRadius cornerRadius)
if (parameter is string str)
{
if (parameter is string str)
{
var arr = str.Split(',');
if (arr.Length != 4) return cornerRadius;
var arr = str.Split(',');
if (arr.Length != 4) return cornerRadius;
return new CornerRadius(
arr[0].Equals("1") ? cornerRadius.TopLeft : 0,
arr[1].Equals("1") ? cornerRadius.TopRight : 0,
arr[2].Equals("1") ? cornerRadius.BottomRight : 0,
arr[3].Equals("1") ? cornerRadius.BottomLeft : 0);
}
return new CornerRadius(
arr[0].Equals("1") ? cornerRadius.TopLeft : 0,
arr[1].Equals("1") ? cornerRadius.TopRight : 0,
arr[2].Equals("1") ? cornerRadius.BottomRight : 0,
arr[3].Equals("1") ? cornerRadius.BottomLeft : 0);
}
return value;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotSupportedException();
}
return value;
}
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotSupportedException();
}
}
@@ -4,46 +4,43 @@ using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
namespace HandyControl.Tools.Converter
namespace HandyControl.Tools.Converter;
public class DataGridSelectAllButtonVisibilityConverter : IMultiValueConverter
{
public class DataGridSelectAllButtonVisibilityConverter : IMultiValueConverter
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
if (values == null)
{
if (values == null)
{
return Visibility.Collapsed;
}
return Visibility.Collapsed;
}
if (values.Length != 2)
{
return Visibility.Collapsed;
}
if (values.Length != 2)
{
return Visibility.Collapsed;
}
if (values[0] is DataGridHeadersVisibility headersVisibility)
if (values[0] is DataGridHeadersVisibility headersVisibility)
{
if (values[1] is bool showButton)
{
if (values[1] is bool showButton)
if (headersVisibility == DataGridHeadersVisibility.All)
{
if (headersVisibility == DataGridHeadersVisibility.All)
{
return showButton ? Visibility.Visible : Visibility.Collapsed;
}
return Visibility.Collapsed;
return showButton ? Visibility.Visible : Visibility.Collapsed;
}
return Visibility.Collapsed;
}
else if (values[0] is bool showButton)
{
if (values[1] is DataGridHeadersVisibility visibility)
{
if (visibility == DataGridHeadersVisibility.All)
{
return showButton ? Visibility.Visible : Visibility.Collapsed;
}
return Visibility.Collapsed;
return Visibility.Collapsed;
}
else if (values[0] is bool showButton)
{
if (values[1] is DataGridHeadersVisibility visibility)
{
if (visibility == DataGridHeadersVisibility.All)
{
return showButton ? Visibility.Visible : Visibility.Collapsed;
}
return Visibility.Collapsed;
@@ -52,9 +49,11 @@ namespace HandyControl.Tools.Converter
return Visibility.Collapsed;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotSupportedException();
}
return Visibility.Collapsed;
}
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotSupportedException();
}
}
@@ -3,27 +3,26 @@ using System.Globalization;
using System.Windows;
using System.Windows.Data;
namespace HandyControl.Tools.Converter
namespace HandyControl.Tools.Converter;
public class Double2GridLengthConverter : IValueConverter
{
public class Double2GridLengthConverter : IValueConverter
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
if (value is double doubleValue)
{
if (value is double doubleValue)
if (double.IsNaN(doubleValue) || double.IsInfinity(doubleValue))
{
if (double.IsNaN(doubleValue) || double.IsInfinity(doubleValue))
{
return new GridLength(1.0, GridUnitType.Star);
}
return new GridLength(doubleValue);
return new GridLength(1.0, GridUnitType.Star);
}
return new GridLength(0);
return new GridLength(doubleValue);
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotSupportedException();
}
return new GridLength(0);
}
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotSupportedException();
}
}
@@ -3,29 +3,28 @@ using System.Globalization;
using System.Windows.Data;
using HandyControl.Tools.Extension;
namespace HandyControl.Tools.Converter
{
public class DoubleMinConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is double doubleValue)
{
if (parameter is string str)
{
var minValue = str.Value<double>();
return doubleValue < minValue ? minValue : doubleValue;
}
namespace HandyControl.Tools.Converter;
return doubleValue < .0 ? .0 : doubleValue;
public class DoubleMinConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is double doubleValue)
{
if (parameter is string str)
{
var minValue = str.Value<double>();
return doubleValue < minValue ? minValue : doubleValue;
}
return .0;
return doubleValue < .0 ? .0 : doubleValue;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotSupportedException();
}
return .0;
}
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotSupportedException();
}
}
@@ -2,37 +2,36 @@
using System.Globalization;
using System.Windows.Data;
namespace HandyControl.Tools.Converter
{
public class Int2StringConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value != null)
{
if (int.TryParse(value.ToString(), out var intValue))
{
if (parameter is string str)
{
var arr = str.Split(';');
if (arr.Length > intValue)
{
return arr[intValue];
}
namespace HandyControl.Tools.Converter;
return intValue;
public class Int2StringConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value != null)
{
if (int.TryParse(value.ToString(), out var 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 NotSupportedException();
}
return value;
}
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotSupportedException();
}
}
@@ -2,51 +2,50 @@
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;
}
namespace HandyControl.Tools.Converter;
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
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)
{
throw new NotSupportedException();
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 NotSupportedException();
}
}
@@ -4,33 +4,32 @@ using System.Windows.Data;
using HandyControl.Expression.Drawing;
using HandyControl.Tools.Extension;
namespace HandyControl.Tools.Converter
namespace HandyControl.Tools.Converter;
public class Number2PercentageConverter : IMultiValueConverter
{
public class Number2PercentageConverter : IMultiValueConverter
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
if (values == null || values.Length != 2) return .0;
if (values == null || values.Length != 2) return .0;
var obj1 = values[0];
var obj2 = values[1];
var obj1 = values[0];
var obj2 = values[1];
if (obj1 == null || obj2 == null) return .0;
if (obj1 == null || obj2 == null) return .0;
var str1 = values[0].ToString();
var str2 = values[1].ToString();
var str1 = values[0].ToString();
var str2 = values[1].ToString();
var v1 = str1.Value<double>();
var v2 = str2.Value<double>();
var v1 = str1.Value<double>();
var v2 = str2.Value<double>();
if (MathHelper.IsVerySmall(v2)) return 100.0;
if (MathHelper.IsVerySmall(v2)) return 100.0;
return v1 / v2 * 100;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotSupportedException();
}
return v1 / v2 * 100;
}
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotSupportedException();
}
}
@@ -2,15 +2,14 @@
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);
namespace HandyControl.Tools.Converter;
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotSupportedException();
}
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 NotSupportedException();
}
}
}
@@ -2,15 +2,14 @@
using System.Globalization;
using System.Windows.Data;
namespace HandyControl.Tools.Converter
{
public class Object2StringConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture) => value?.ToString();
namespace HandyControl.Tools.Converter;
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotSupportedException();
}
public class Object2StringConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture) => value?.ToString();
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotSupportedException();
}
}
}
@@ -3,18 +3,17 @@ using System.Globalization;
using System.Windows;
using System.Windows.Data;
namespace HandyControl.Tools.Converter
{
public class Object2VisibilityConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return value == null ? Visibility.Collapsed : Visibility.Visible;
}
namespace HandyControl.Tools.Converter;
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotSupportedException();
}
public class Object2VisibilityConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return value == null ? Visibility.Collapsed : Visibility.Visible;
}
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotSupportedException();
}
}
@@ -3,18 +3,17 @@ using System.Globalization;
using System.Windows;
using System.Windows.Data;
namespace HandyControl.Tools.Converter
{
public class Object2VisibilityReConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return value == null ? Visibility.Visible : Visibility.Collapsed;
}
namespace HandyControl.Tools.Converter;
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotSupportedException();
}
public class Object2VisibilityReConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return value == null ? Visibility.Visible : Visibility.Collapsed;
}
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotSupportedException();
}
}
@@ -3,29 +3,28 @@ using System.Globalization;
using System.Windows;
using System.Windows.Data;
namespace HandyControl.Tools.Converter
{
public class RectangleCircularConverter : 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 .0;
}
namespace HandyControl.Tools.Converter;
var min = Math.Min(width, height);
return min / 2;
public class RectangleCircularConverter : 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 .0;
}
return DependencyProperty.UnsetValue;
var min = Math.Min(width, height);
return min / 2;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotSupportedException();
}
return DependencyProperty.UnsetValue;
}
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotSupportedException();
}
}
@@ -3,18 +3,17 @@ 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;
}
namespace HandyControl.Tools.Converter;
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return value != null && (Visibility) value == Visibility.Collapsed;
}
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;
}
}
@@ -3,18 +3,17 @@ 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;
}
namespace HandyControl.Tools.Converter;
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return value != null && (Visibility) value == Visibility.Collapsed;
}
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;
}
}
@@ -3,32 +3,31 @@ using System.Globalization;
using System.Windows;
using System.Windows.Data;
namespace HandyControl.Tools.Converter
namespace HandyControl.Tools.Converter;
public class ThicknessSplitConverter : IValueConverter
{
public class ThicknessSplitConverter : IValueConverter
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
if (value is Thickness thickness)
{
if (value is Thickness thickness)
if (parameter is string str)
{
if (parameter is string str)
{
var arr = str.Split(',');
if (arr.Length != 4) return thickness;
var arr = str.Split(',');
if (arr.Length != 4) return thickness;
return new Thickness(
arr[0].Equals("1") ? thickness.Left : 0,
arr[1].Equals("1") ? thickness.Top : 0,
arr[2].Equals("1") ? thickness.Right : 0,
arr[3].Equals("1") ? thickness.Bottom : 0);
}
return new Thickness(
arr[0].Equals("1") ? thickness.Left : 0,
arr[1].Equals("1") ? thickness.Top : 0,
arr[2].Equals("1") ? thickness.Right : 0,
arr[3].Equals("1") ? thickness.Bottom : 0);
}
return value;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotSupportedException();
}
return value;
}
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotSupportedException();
}
}
@@ -4,25 +4,24 @@ using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Media;
namespace HandyControl.Tools.Converter
namespace HandyControl.Tools.Converter;
public class TreeViewItemMarginConverter : IValueConverter
{
public class TreeViewItemMarginConverter : IValueConverter
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
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))
{
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 NotSupportedException();
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 NotSupportedException();
}
}
@@ -6,122 +6,121 @@ using System.Windows.Markup;
using HandyControl.Data;
using HandyControl.Tools.Converter;
namespace HandyControl.Tools.Extension
namespace HandyControl.Tools.Extension;
[TypeConverter(typeof(ColLayoutConverter))]
public class ColLayout : MarkupExtension
{
[TypeConverter(typeof(ColLayoutConverter))]
public class ColLayout : MarkupExtension
public static readonly int ColMaxCellCount = 24;
public static readonly int HalfColMaxCellCount = 12;
public static readonly int XsMaxWidth = 768;
public static readonly int SmMaxWidth = 992;
public static readonly int MdMaxWidth = 1200;
public static readonly int LgMaxWidth = 1920;
public static readonly int XlMaxWidth = 2560;
public int Xs { get; set; } = 24;
public int Sm { get; set; } = 12;
public int Md { get; set; } = 8;
public int Lg { get; set; } = 6;
public int Xl { get; set; } = 4;
public int Xxl { get; set; } = 2;
public ColLayout()
{
public static readonly int ColMaxCellCount = 24;
public static readonly int HalfColMaxCellCount = 12;
public static readonly int XsMaxWidth = 768;
public static readonly int SmMaxWidth = 992;
public static readonly int MdMaxWidth = 1200;
public static readonly int LgMaxWidth = 1920;
public static readonly int XlMaxWidth = 2560;
public int Xs { get; set; } = 24;
public int Sm { get; set; } = 12;
public int Md { get; set; } = 8;
public int Lg { get; set; } = 6;
public int Xl { get; set; } = 4;
public int Xxl { get; set; } = 2;
public ColLayout()
{
}
public ColLayout(int uniformWidth)
{
Xs = uniformWidth;
Sm = uniformWidth;
Md = uniformWidth;
Lg = uniformWidth;
Xl = uniformWidth;
Xxl = uniformWidth;
}
public ColLayout(int xs, int sm, int md, int lg, int xl, int xxl)
{
Xs = xs;
Sm = sm;
Md = md;
Lg = lg;
Xl = xl;
Xxl = xxl;
}
public override object ProvideValue(IServiceProvider serviceProvider)
{
return new ColLayout
{
Xs = Xs,
Sm = Sm,
Md = Md,
Lg = Lg,
Xl = Xl,
Xxl = Xxl
};
}
public static ColLayoutStatus GetLayoutStatus(double width)
{
if (width < MdMaxWidth)
{
if (width < SmMaxWidth)
{
if (width < XsMaxWidth)
{
return ColLayoutStatus.Xs;
}
return ColLayoutStatus.Sm;
}
return ColLayoutStatus.Md;
}
if (width < XlMaxWidth)
{
if (width < LgMaxWidth)
{
return ColLayoutStatus.Lg;
}
return ColLayoutStatus.Xl;
}
return ColLayoutStatus.Xxl;
}
public override string ToString()
{
var cultureInfo = CultureInfo.CurrentCulture;
var listSeparator = TokenizerHelper.GetNumericListSeparator(cultureInfo);
// Initial capacity [128] is an estimate based on a sum of:
// 72 = 6x double (twelve digits is generous for the range of values likely)
// 4 = 4x separator characters
var sb = new StringBuilder(128);
sb.Append(Xs.ToString(cultureInfo));
sb.Append(listSeparator);
sb.Append(Sm.ToString(cultureInfo));
sb.Append(listSeparator);
sb.Append(Md.ToString(cultureInfo));
sb.Append(listSeparator);
sb.Append(Lg.ToString(cultureInfo));
sb.Append(listSeparator);
sb.Append(Xl.ToString(cultureInfo));
sb.Append(listSeparator);
sb.Append(Xxl.ToString(cultureInfo));
return sb.ToString();
}
}
}
public ColLayout(int uniformWidth)
{
Xs = uniformWidth;
Sm = uniformWidth;
Md = uniformWidth;
Lg = uniformWidth;
Xl = uniformWidth;
Xxl = uniformWidth;
}
public ColLayout(int xs, int sm, int md, int lg, int xl, int xxl)
{
Xs = xs;
Sm = sm;
Md = md;
Lg = lg;
Xl = xl;
Xxl = xxl;
}
public override object ProvideValue(IServiceProvider serviceProvider)
{
return new ColLayout
{
Xs = Xs,
Sm = Sm,
Md = Md,
Lg = Lg,
Xl = Xl,
Xxl = Xxl
};
}
public static ColLayoutStatus GetLayoutStatus(double width)
{
if (width < MdMaxWidth)
{
if (width < SmMaxWidth)
{
if (width < XsMaxWidth)
{
return ColLayoutStatus.Xs;
}
return ColLayoutStatus.Sm;
}
return ColLayoutStatus.Md;
}
if (width < XlMaxWidth)
{
if (width < LgMaxWidth)
{
return ColLayoutStatus.Lg;
}
return ColLayoutStatus.Xl;
}
return ColLayoutStatus.Xxl;
}
public override string ToString()
{
var cultureInfo = CultureInfo.CurrentCulture;
var listSeparator = TokenizerHelper.GetNumericListSeparator(cultureInfo);
// Initial capacity [128] is an estimate based on a sum of:
// 72 = 6x double (twelve digits is generous for the range of values likely)
// 4 = 4x separator characters
var sb = new StringBuilder(128);
sb.Append(Xs.ToString(cultureInfo));
sb.Append(listSeparator);
sb.Append(Sm.ToString(cultureInfo));
sb.Append(listSeparator);
sb.Append(Md.ToString(cultureInfo));
sb.Append(listSeparator);
sb.Append(Lg.ToString(cultureInfo));
sb.Append(listSeparator);
sb.Append(Xl.ToString(cultureInfo));
sb.Append(listSeparator);
sb.Append(Xxl.ToString(cultureInfo));
return sb.ToString();
}
}
@@ -2,32 +2,31 @@
using System.Windows.Media;
namespace HandyControl.Tools.Extension
namespace HandyControl.Tools.Extension;
/// <summary>
/// 颜色扩展类
/// </summary>
public static class ColorExtension
{
/// <summary>
/// 颜色扩展类
/// 将颜色转换为10进制表示(rgb顺序颠倒)
/// </summary>
public static class ColorExtension
/// <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()
{
/// <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()
{
color.R,
color.G,
color.B
};
}
}
color.R,
color.G,
color.B
};
}
@@ -1,18 +1,17 @@
using System.Windows;
using System.Windows.Media;
namespace HandyControl.Tools.Extension
namespace HandyControl.Tools.Extension;
public static class DependencyObjectExtension
{
public static class DependencyObjectExtension
public static DependencyObject GetVisualOrLogicalParent(this DependencyObject sourceElement)
{
public static DependencyObject GetVisualOrLogicalParent(this DependencyObject sourceElement)
return sourceElement switch
{
return sourceElement switch
{
null => null,
Visual _ => (VisualTreeHelper.GetParent(sourceElement) ?? LogicalTreeHelper.GetParent(sourceElement)),
_ => LogicalTreeHelper.GetParent(sourceElement)
};
}
null => null,
Visual _ => (VisualTreeHelper.GetParent(sourceElement) ?? LogicalTreeHelper.GetParent(sourceElement)),
_ => LogicalTreeHelper.GetParent(sourceElement)
};
}
}
}
@@ -3,76 +3,75 @@ using System.Threading.Tasks;
using System.Windows;
using HandyControl.Controls;
namespace HandyControl.Tools.Extension
namespace HandyControl.Tools.Extension;
public interface IDialogResultable<T>
{
public interface IDialogResultable<T>
{
T Result { get; set; }
T Result { get; set; }
Action CloseAction { get; set; }
}
Action CloseAction { get; set; }
}
public static class DialogExtension
public static class DialogExtension
{
public static Task<TResult> GetResultAsync<TResult>(this Dialog dialog)
{
public static Task<TResult> GetResultAsync<TResult>(this Dialog dialog)
var tcs = new TaskCompletionSource<TResult>();
try
{
var tcs = new TaskCompletionSource<TResult>();
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
{
if (dialog.IsClosed)
{
SetResult();
}
else
{
dialog.Unloaded += OnUnloaded;
dialog.GetViewModel<IDialogResultable<TResult>>().CloseAction = dialog.Close;
}
tcs.TrySetResult(dialog.GetViewModel<IDialogResultable<TResult>>().Result);
}
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;
}
}
}
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;
}
}
@@ -2,30 +2,29 @@
using System.Collections.Generic;
using System.Linq;
namespace HandyControl.Tools.Extension
namespace HandyControl.Tools.Extension;
public static class EnumerableExtension
{
public static class EnumerableExtension
public static IEnumerable<TSource> Do<TSource>(this IEnumerable<TSource> source, Action<TSource> predicate)
{
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)
{
var enumerable = source as IList<TSource> ?? source.ToList();
foreach (var item in enumerable)
{
predicate.Invoke(item);
}
return enumerable;
predicate.Invoke(item);
}
public static IEnumerable<TSource> DoWithIndex<TSource>(this IEnumerable<TSource> source, Action<TSource, int> predicate)
{
var enumerable = source as IList<TSource> ?? source.ToList();
for (var i = 0; i < enumerable.Count; i++)
{
predicate.Invoke(enumerable[i], i);
}
return enumerable;
}
return enumerable;
}
}
public static IEnumerable<TSource> DoWithIndex<TSource>(this IEnumerable<TSource> source, Action<TSource, int> predicate)
{
var enumerable = source as IList<TSource> ?? source.ToList();
for (var i = 0; i < enumerable.Count; i++)
{
predicate.Invoke(enumerable[i], i);
}
return enumerable;
}
}
@@ -1,58 +1,57 @@
using System.Windows;
namespace HandyControl.Tools.Extension
namespace HandyControl.Tools.Extension;
public static class FrameworkElementExtension
{
public static class FrameworkElementExtension
public static double GetValidWidth(this FrameworkElement element)
{
public static double GetValidWidth(this FrameworkElement element)
if (!double.IsNaN(element.Width))
{
if (!double.IsNaN(element.Width))
if (element.Width > 0)
{
if (element.Width > 0)
{
return element.Width;
}
return element.Width;
}
else
}
else
{
if (element.ActualWidth > 0)
{
if (element.ActualWidth > 0)
{
return element.ActualWidth;
}
if (element.DesiredSize.Width > 0)
{
return element.DesiredSize.Width;
}
return element.ActualWidth;
}
return 0;
if (element.DesiredSize.Width > 0)
{
return element.DesiredSize.Width;
}
}
public static double GetValidHeight(this FrameworkElement element)
{
if (!double.IsNaN(element.Height))
{
if (element.Height > 0)
{
return element.Height;
}
}
else
{
if (element.ActualHeight > 0)
{
return element.ActualHeight;
}
if (element.DesiredSize.Height > 0)
{
return element.DesiredSize.Height;
}
}
return 0;
}
return 0;
}
}
public static double GetValidHeight(this FrameworkElement element)
{
if (!double.IsNaN(element.Height))
{
if (element.Height > 0)
{
return element.Height;
}
}
else
{
if (element.ActualHeight > 0)
{
return element.ActualHeight;
}
if (element.DesiredSize.Height > 0)
{
return element.DesiredSize.Height;
}
}
return 0;
}
}
@@ -3,48 +3,47 @@ using System.Windows;
using System.Windows.Media;
using HandyControl.Expression.Drawing;
namespace HandyControl.Tools.Extension
namespace HandyControl.Tools.Extension;
public static class GeometryExtension
{
public static class GeometryExtension
/// <summary>
/// 获取路径总长度
/// </summary>
/// <param name="geometry"></param>
/// <returns></returns>
public static double GetTotalLength(this Geometry geometry)
{
/// <summary>
/// 获取路径总长度
/// </summary>
/// <param name="geometry"></param>
/// <returns></returns>
public static double GetTotalLength(this Geometry geometry)
{
if (geometry == null) return 0;
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;
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;
}
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;
}
}
@@ -4,148 +4,147 @@ using System.Windows.Data;
using System.Windows.Markup;
using HandyControl.Properties.Langs;
namespace HandyControl.Tools.Extension
namespace HandyControl.Tools.Extension;
public class LangExtension : MarkupExtension
{
public class LangExtension : MarkupExtension
private readonly DependencyObject _proxy;
public LangExtension()
{
private readonly DependencyObject _proxy;
public LangExtension()
{
_proxy = new DependencyObject();
Source = LangProvider.Instance;
}
public LangExtension(string key) : this()
{
Key = key;
}
public static readonly DependencyProperty KeyProperty = DependencyProperty.RegisterAttached(
"Key", typeof(object), typeof(LangExtension), new PropertyMetadata(default));
public object Key
{
get => _proxy.GetValue(KeyProperty);
set => _proxy.SetValue(KeyProperty, value);
}
private static readonly DependencyProperty TargetPropertyProperty = DependencyProperty.RegisterAttached(
"TargetProperty", typeof(DependencyProperty), typeof(LangExtension), new PropertyMetadata(default(DependencyProperty)));
private static void SetTargetProperty(DependencyObject element, DependencyProperty value)
=> element.SetValue(TargetPropertyProperty, value);
private static DependencyProperty GetTargetProperty(DependencyObject element)
=> (DependencyProperty) element.GetValue(TargetPropertyProperty);
public BindingMode Mode { get; set; }
public IValueConverter Converter { get; set; }
public object ConverterParameter { get; set; }
public object Source { get; set; }
public override object ProvideValue(IServiceProvider serviceProvider)
{
if (!(serviceProvider.GetService(typeof(IProvideValueTarget)) is IProvideValueTarget provideValueTarget)) return this;
if (provideValueTarget.TargetObject.GetType().FullName == "System.Windows.SharedDp") return this;
if (!(provideValueTarget.TargetObject is DependencyObject targetObject)) return this;
if (!(provideValueTarget.TargetProperty is DependencyProperty targetProperty)) return this;
switch (Key)
{
case string key:
{
var binding = CreateLangBinding(key);
BindingOperations.SetBinding(targetObject, targetProperty, binding);
return binding.ProvideValue(serviceProvider);
}
case Binding keyBinding when targetObject is FrameworkElement element:
{
if (element.DataContext != null)
{
var binding = SetLangBinding(element, targetProperty, keyBinding.Path, element.DataContext);
return binding.ProvideValue(serviceProvider);
}
SetTargetProperty(element, targetProperty);
element.DataContextChanged += LangExtension_DataContextChanged;
break;
}
case Binding keyBinding when targetObject is FrameworkContentElement element:
{
if (element.DataContext != null)
{
var binding = SetLangBinding(element, targetProperty, keyBinding.Path, element.DataContext);
return binding.ProvideValue(serviceProvider);
}
SetTargetProperty(element, targetProperty);
element.DataContextChanged += LangExtension_DataContextChanged;
break;
}
}
return string.Empty;
}
private void LangExtension_DataContextChanged(object sender, DependencyPropertyChangedEventArgs e)
{
switch (sender)
{
case FrameworkElement element:
{
element.DataContextChanged -= LangExtension_DataContextChanged;
if (!(Key is Binding keyBinding)) return;
var targetProperty = GetTargetProperty(element);
SetTargetProperty(element, null);
SetLangBinding(element, targetProperty, keyBinding.Path, element.DataContext);
break;
}
case FrameworkContentElement element:
{
element.DataContextChanged -= LangExtension_DataContextChanged;
if (!(Key is Binding keyBinding)) return;
var targetProperty = GetTargetProperty(element);
SetTargetProperty(element, null);
SetLangBinding(element, targetProperty, keyBinding.Path, element.DataContext);
break;
}
}
}
private BindingBase SetLangBinding(DependencyObject targetObject, DependencyProperty targetProperty, PropertyPath path, object dataContext)
{
if (targetProperty == null) return null;
BindingOperations.SetBinding(targetObject, targetProperty, new Binding
{
Path = path,
Source = dataContext,
Mode = BindingMode.OneWay
});
var key = targetObject.GetValue(targetProperty) as string;
if (string.IsNullOrEmpty(key)) return null;
var binding = CreateLangBinding(key);
BindingOperations.SetBinding(targetObject, targetProperty, binding);
return binding;
}
private BindingBase CreateLangBinding(string key) => new Binding(key)
{
Converter = Converter,
ConverterParameter = ConverterParameter,
UpdateSourceTrigger = UpdateSourceTrigger.Explicit,
Source = Source,
Mode = BindingMode.OneWay
};
_proxy = new DependencyObject();
Source = LangProvider.Instance;
}
}
public LangExtension(string key) : this()
{
Key = key;
}
public static readonly DependencyProperty KeyProperty = DependencyProperty.RegisterAttached(
"Key", typeof(object), typeof(LangExtension), new PropertyMetadata(default));
public object Key
{
get => _proxy.GetValue(KeyProperty);
set => _proxy.SetValue(KeyProperty, value);
}
private static readonly DependencyProperty TargetPropertyProperty = DependencyProperty.RegisterAttached(
"TargetProperty", typeof(DependencyProperty), typeof(LangExtension), new PropertyMetadata(default(DependencyProperty)));
private static void SetTargetProperty(DependencyObject element, DependencyProperty value)
=> element.SetValue(TargetPropertyProperty, value);
private static DependencyProperty GetTargetProperty(DependencyObject element)
=> (DependencyProperty) element.GetValue(TargetPropertyProperty);
public BindingMode Mode { get; set; }
public IValueConverter Converter { get; set; }
public object ConverterParameter { get; set; }
public object Source { get; set; }
public override object ProvideValue(IServiceProvider serviceProvider)
{
if (!(serviceProvider.GetService(typeof(IProvideValueTarget)) is IProvideValueTarget provideValueTarget)) return this;
if (provideValueTarget.TargetObject.GetType().FullName == "System.Windows.SharedDp") return this;
if (!(provideValueTarget.TargetObject is DependencyObject targetObject)) return this;
if (!(provideValueTarget.TargetProperty is DependencyProperty targetProperty)) return this;
switch (Key)
{
case string key:
{
var binding = CreateLangBinding(key);
BindingOperations.SetBinding(targetObject, targetProperty, binding);
return binding.ProvideValue(serviceProvider);
}
case Binding keyBinding when targetObject is FrameworkElement element:
{
if (element.DataContext != null)
{
var binding = SetLangBinding(element, targetProperty, keyBinding.Path, element.DataContext);
return binding.ProvideValue(serviceProvider);
}
SetTargetProperty(element, targetProperty);
element.DataContextChanged += LangExtension_DataContextChanged;
break;
}
case Binding keyBinding when targetObject is FrameworkContentElement element:
{
if (element.DataContext != null)
{
var binding = SetLangBinding(element, targetProperty, keyBinding.Path, element.DataContext);
return binding.ProvideValue(serviceProvider);
}
SetTargetProperty(element, targetProperty);
element.DataContextChanged += LangExtension_DataContextChanged;
break;
}
}
return string.Empty;
}
private void LangExtension_DataContextChanged(object sender, DependencyPropertyChangedEventArgs e)
{
switch (sender)
{
case FrameworkElement element:
{
element.DataContextChanged -= LangExtension_DataContextChanged;
if (!(Key is Binding keyBinding)) return;
var targetProperty = GetTargetProperty(element);
SetTargetProperty(element, null);
SetLangBinding(element, targetProperty, keyBinding.Path, element.DataContext);
break;
}
case FrameworkContentElement element:
{
element.DataContextChanged -= LangExtension_DataContextChanged;
if (!(Key is Binding keyBinding)) return;
var targetProperty = GetTargetProperty(element);
SetTargetProperty(element, null);
SetLangBinding(element, targetProperty, keyBinding.Path, element.DataContext);
break;
}
}
}
private BindingBase SetLangBinding(DependencyObject targetObject, DependencyProperty targetProperty, PropertyPath path, object dataContext)
{
if (targetProperty == null) return null;
BindingOperations.SetBinding(targetObject, targetProperty, new Binding
{
Path = path,
Source = dataContext,
Mode = BindingMode.OneWay
});
var key = targetObject.GetValue(targetProperty) as string;
if (string.IsNullOrEmpty(key)) return null;
var binding = CreateLangBinding(key);
BindingOperations.SetBinding(targetObject, targetProperty, binding);
return binding;
}
private BindingBase CreateLangBinding(string key) => new Binding(key)
{
Converter = Converter,
ConverterParameter = ConverterParameter,
UpdateSourceTrigger = UpdateSourceTrigger.Explicit,
Source = Source,
Mode = BindingMode.OneWay
};
}
@@ -1,32 +1,31 @@
using System;
using System.ComponentModel;
namespace HandyControl.Tools.Extension
{
public static class StringExtension
{
public static T Value<T>(this string input)
{
try
{
return (T) TypeDescriptor.GetConverter(typeof(T)).ConvertFromString(input);
}
catch
{
return default;
}
}
namespace HandyControl.Tools.Extension;
public static object Value(this string input, Type type)
public static class StringExtension
{
public static T Value<T>(this string input)
{
try
{
try
{
return TypeDescriptor.GetConverter(type).ConvertFromString(input);
}
catch
{
return null;
}
return (T) TypeDescriptor.GetConverter(typeof(T)).ConvertFromString(input);
}
catch
{
return default;
}
}
}
public static object Value(this string input, Type type)
{
try
{
return TypeDescriptor.GetConverter(type).ConvertFromString(input);
}
catch
{
return null;
}
}
}
@@ -1,33 +1,32 @@
using System.Windows;
namespace HandyControl.Tools.Extension
namespace HandyControl.Tools.Extension;
// ReSharper disable once InconsistentNaming
public static class UIElementExtension
{
// 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>
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>
/// <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 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;
}
}
/// <summary>
/// 不显示元素,且不保留空间
/// </summary>
/// <param name="element"></param>
public static void Collapse(this UIElement element) => element.Visibility = Visibility.Collapsed;
}
@@ -1,23 +1,22 @@
using System.Windows;
using HandyControl.Expression.Drawing;
namespace HandyControl.Tools.Extension
namespace HandyControl.Tools.Extension;
public static class ValueExtension
{
public static class ValueExtension
{
public static Thickness Add(this Thickness a, Thickness b) => new(a.Left + b.Left, a.Top + b.Top, a.Right + b.Right, a.Bottom + b.Bottom);
public static Thickness Add(this Thickness a, Thickness b) => new(a.Left + b.Left, a.Top + b.Top, a.Right + b.Right, a.Bottom + b.Bottom);
public static bool IsZero(this Thickness thickness) =>
MathHelper.IsZero(thickness.Left)
&& MathHelper.IsZero(thickness.Top)
&& MathHelper.IsZero(thickness.Right)
&& MathHelper.IsZero(thickness.Bottom);
public static bool IsZero(this Thickness thickness) =>
MathHelper.IsZero(thickness.Left)
&& MathHelper.IsZero(thickness.Top)
&& MathHelper.IsZero(thickness.Right)
&& MathHelper.IsZero(thickness.Bottom);
public static bool IsUniform(this Thickness thickness) =>
MathHelper.AreClose(thickness.Left, thickness.Top)
&& MathHelper.AreClose(thickness.Left, thickness.Right)
&& MathHelper.AreClose(thickness.Left, thickness.Bottom);
public static bool IsUniform(this Thickness thickness) =>
MathHelper.AreClose(thickness.Left, thickness.Top)
&& MathHelper.AreClose(thickness.Left, thickness.Right)
&& MathHelper.AreClose(thickness.Left, thickness.Bottom);
public static bool IsNaN(this double value) => double.IsNaN(value);
}
}
public static bool IsNaN(this double value) => double.IsNaN(value);
}
@@ -3,33 +3,32 @@ using System.Collections.Generic;
using HandyControl.Collections;
using HandyControl.Data;
namespace HandyControl.Tools
namespace HandyControl.Tools;
public class ComparerGenerator
{
public class ComparerGenerator
private static readonly Dictionary<Type, ComparerTypeCode> TypeCodeDic = new()
{
private static readonly Dictionary<Type, ComparerTypeCode> TypeCodeDic = new()
{
[typeof(DateTimeRange)] = ComparerTypeCode.DateTimeRange,
};
[typeof(DateTimeRange)] = ComparerTypeCode.DateTimeRange,
};
public static IComparer<T> GetComparer<T>()
public static IComparer<T> GetComparer<T>()
{
if (TypeCodeDic.TryGetValue(typeof(T), out var comparerType))
{
if (TypeCodeDic.TryGetValue(typeof(T), out var comparerType))
if (comparerType == ComparerTypeCode.DateTimeRange)
{
if (comparerType == ComparerTypeCode.DateTimeRange)
{
return (IComparer<T>) new DateTimeRangeComparer();
}
return null;
return (IComparer<T>) new DateTimeRangeComparer();
}
return null;
}
private enum ComparerTypeCode
{
DateTimeRange
}
return null;
}
}
private enum ComparerTypeCode
{
DateTimeRange
}
}
@@ -1,15 +1,14 @@
using System.Collections.Generic;
using HandyControl.Data;
namespace HandyControl.Tools
namespace HandyControl.Tools;
public class DateTimeRangeComparer : IComparer<DateTimeRange>
{
public class DateTimeRangeComparer : IComparer<DateTimeRange>
public int Compare(DateTimeRange x, DateTimeRange y)
{
public int Compare(DateTimeRange x, DateTimeRange y)
{
if (x.Start > y.Start && x.End > y.End) return 1;
if (x.Start < y.Start && x.End < y.End) return -1;
return 0;
}
if (x.Start > y.Start && x.End > y.End) return 1;
if (x.Start < y.Start && x.End < y.End) return -1;
return 0;
}
}
}
@@ -7,114 +7,113 @@ using System.Windows.Media;
using System.Windows.Shapes;
using HandyControl.Controls;
namespace HandyControl.Tools
namespace HandyControl.Tools;
public class GithubGravatarGenerator : IGravatarGenerator
{
public class GithubGravatarGenerator : IGravatarGenerator
private const int RenderDataMaxLength = 15;
public object GetGravatar(string id)
{
private const int RenderDataMaxLength = 15;
var hashcode = GetHashCode(id);
var renderData = GetRenderData(hashcode);
var renderBrush = GetRenderBrush(hashcode);
var geometryGroup = new GeometryGroup();
public object GetGravatar(string id)
void AddRec(int i, int j, bool hidden = false)
{
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 (var i = 0; i < 2; i++)
{
for (var j = 0; j < 5; j++, index++)
{
AddRec(i, j, renderData[index] == 0);
}
}
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 (var i = 0; i < 2; i++)
{
for (var j = 0; j < 5; j++, index++)
{
AddRec(2, j, renderData[index] == 0);
AddRec(i, j, renderData[index] == 0);
}
}
for (var j = 0; j < 5; j++, index++)
{
AddRec(2, j, renderData[index] == 0);
}
index -= 10;
for (var i = 3; i < 5; i++)
{
for (var j = 0; j < 5; j++, index++)
{
AddRec(i, j, renderData[index] == 0);
}
index -= 10;
for (var i = 3; i < 5; i++)
{
for (var 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,
Stretch = Stretch.Uniform
};
RenderOptions.SetEdgeMode(path, EdgeMode.Aliased);
return path;
}
private string GetHashCode(string id)
var path = new Path
{
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();
}
Data = geometryGroup,
Fill = renderBrush,
VerticalAlignment = VerticalAlignment.Top,
Stretch = Stretch.Uniform
};
private int[] GetRenderData(string hashcode)
{
var arr = new int[RenderDataMaxLength];
for (var i = 0; i < RenderDataMaxLength; i++)
{
var c = hashcode[i];
var v = (int) c;
arr[i] = v % 2;
}
RenderOptions.SetEdgeMode(path, EdgeMode.Aliased);
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));
}
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 (var 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));
}
}
@@ -3,115 +3,114 @@ using System.Windows;
using System.Windows.Media;
using HandyControl.Data;
namespace HandyControl.Tools
namespace HandyControl.Tools;
public class HatchBrushGenerator
{
public class HatchBrushGenerator
private static readonly byte[][] HatchBrushes =
{
private static readonly byte[][] HatchBrushes =
new byte[] {0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00}, // Horizontal
new byte[] {0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08}, // Vertical
new byte[] {0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80}, // ForwardDiagonal
new byte[] {0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01}, // BackwardDiagonal
new byte[] {0x08, 0x08, 0x08, 0xff, 0x08, 0x08, 0x08, 0x08}, // Cross
new byte[] {0x81, 0x42, 0x24, 0x18, 0x18, 0x24, 0x42, 0x81}, // DiagonalCross
new byte[] {0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x80}, // Percent05
new byte[] {0x00, 0x02, 0x00, 0x88, 0x00, 0x20, 0x00, 0x88}, // Percent10
new byte[] {0x00, 0x22, 0x00, 0xcc, 0x00, 0x22, 0x00, 0xcc}, // Percent20
new byte[] {0x00, 0xcc, 0x00, 0xcc, 0x00, 0xcc, 0x00, 0xcc}, // Percent25
new byte[] {0x00, 0xcc, 0x04, 0xcc, 0x00, 0xcc, 0x40, 0xcc}, // Percent30
new byte[] {0x44, 0xcc, 0x22, 0xcc, 0x44, 0xcc, 0x22, 0xcc}, // Percent40
new byte[] {0x55, 0xcc, 0x55, 0xcc, 0x55, 0xcc, 0x55, 0xcc}, // Percent50
new byte[] {0x55, 0xcd, 0x55, 0xee, 0x55, 0xdc, 0x55, 0xee}, // Percent60
new byte[] {0x55, 0xdd, 0x55, 0xff, 0x55, 0xdd, 0x55, 0xff}, // Percent70
new byte[] {0x55, 0xff, 0x55, 0xff, 0x55, 0xff, 0x55, 0xff}, // Percent75
new byte[] {0x55, 0xff, 0x59, 0xff, 0x55, 0xff, 0x99, 0xff}, // Percent80
new byte[] {0x77, 0xff, 0xdd, 0xff, 0x77, 0xff, 0xfd, 0xff}, // Percent90
new byte[] {0x11, 0x22, 0x44, 0x88, 0x11, 0x22, 0x44, 0x88}, // LightDownwardDiagonal
new byte[] {0x88, 0x44, 0x22, 0x11, 0x88, 0x44, 0x22, 0x11}, // LightUpwardDiagonal
new byte[] {0x99, 0x33, 0x66, 0xcc, 0x99, 0x33, 0x66, 0xcc}, // DarkDownwardDiagonal
new byte[] {0xcc, 0x66, 0x33, 0x99, 0xcc, 0x66, 0x33, 0x99}, // DarkUpwardDiagonal
new byte[] {0xc1, 0x83, 0x07, 0x0e, 0x1c, 0x38, 0x70, 0xe0}, // WideDownwardDiagonal
new byte[] {0xe0, 0x70, 0x38, 0x1c, 0x0e, 0x07, 0x83, 0xc1}, // WideUpwardDiagonal
new byte[] {0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88}, // LightVertical
new byte[] {0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff}, // LightHorizontal
new byte[] {0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa}, // NarrowVertical
new byte[] {0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff}, // NarrowHorizontal
new byte[] {0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc}, // DarkVertical
new byte[] {0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff}, // DarkHorizontal
new byte[] {0x11, 0x22, 0x44, 0x88, 0x00, 0x00, 0x00, 0x00}, // DashedDownwardDiagonal
new byte[] {0x88, 0x44, 0x22, 0x11, 0x00, 0x00, 0x00, 0x00}, // DashedUpwardDiagonal
new byte[] {0x0f, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x00, 0x00}, // DashedHorizontal
new byte[] {0x01, 0x01, 0x01, 0x01, 0x10, 0x10, 0x10, 0x10}, // DashedVertical
new byte[] {0x01, 0x08, 0x80, 0x10, 0x02, 0x40, 0x04, 0x20}, // SmallConfetti
new byte[] {0x03, 0x63, 0x6c, 0x0c, 0xc0, 0xc6, 0x36, 0x30}, // LargeConfetti
new byte[] {0x03, 0x84, 0x48, 0x30, 0x03, 0x84, 0x48, 0x30}, // ZigZag
new byte[] {0x30, 0x49, 0x06, 0x00, 0x30, 0x49, 0x06, 0x00}, // Wave
new byte[] {0x81, 0x42, 0x24, 0x18, 0x08, 0x04, 0x02, 0x01}, // DiagonalBrick
new byte[] {0xff, 0x01, 0x01, 0x01, 0xff, 0x10, 0x10, 0x10}, // HorizontalBrick
new byte[] {0x11, 0x82, 0x44, 0xa8, 0x11, 0xa2, 0x44, 0x2a}, // Weave
new byte[] {0x55, 0xaa, 0x55, 0xaa, 0x0f, 0x0f, 0x0f, 0x0f}, // Plaid
new byte[] {0x02, 0x01, 0x02, 0x00, 0x10, 0x20, 0x10, 0x00}, // Divot
new byte[] {0x55, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00}, // DottedGrid
new byte[] {0x11, 0x00, 0x04, 0x00, 0x11, 0x00, 0x40, 0x00}, // DottedDiamond
new byte[] {0x03, 0x0c, 0x10, 0x20, 0x20, 0x30, 0x48, 0x84}, // Shingle
new byte[] {0xff, 0x33, 0xff, 0xcc, 0xff, 0x33, 0xff, 0xcc}, // Trellis
new byte[] {0xee, 0x19, 0x1f, 0x1f, 0xee, 0x91, 0xf1, 0xf1}, // Sphere
new byte[] {0xff, 0x11, 0x11, 0x11, 0xff, 0x11, 0x11, 0x11}, // SmallGrid
new byte[] {0x33, 0x33, 0xcc, 0xcc, 0x33, 0x33, 0xcc, 0xcc}, // SmallCheckerBoard
new byte[] {0x0f, 0x0f, 0x0f, 0x0f, 0xf0, 0xf0, 0xf0, 0xf0}, // LargeCheckerBoard
new byte[] {0x01, 0x82, 0x44, 0x28, 0x10, 0x28, 0x44, 0x82}, // OutlinedDiamond
new byte[] {0x08, 0x1c, 0x3e, 0x7f, 0x3e, 0x1c, 0x08, 0x00} // SolidDiamond
};
public Brush GetHatchBrush(HatchStyle hatchStyle, Color foreColor, Color backColor)
{
var hatchData = GetHatchData(hatchStyle);
var foreGeometryGroup = new GeometryGroup();
for (var y = 0; y < 8; y++)
{
new byte[] {0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00}, // Horizontal
new byte[] {0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08}, // Vertical
new byte[] {0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80}, // ForwardDiagonal
new byte[] {0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01}, // BackwardDiagonal
new byte[] {0x08, 0x08, 0x08, 0xff, 0x08, 0x08, 0x08, 0x08}, // Cross
new byte[] {0x81, 0x42, 0x24, 0x18, 0x18, 0x24, 0x42, 0x81}, // DiagonalCross
new byte[] {0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x80}, // Percent05
new byte[] {0x00, 0x02, 0x00, 0x88, 0x00, 0x20, 0x00, 0x88}, // Percent10
new byte[] {0x00, 0x22, 0x00, 0xcc, 0x00, 0x22, 0x00, 0xcc}, // Percent20
new byte[] {0x00, 0xcc, 0x00, 0xcc, 0x00, 0xcc, 0x00, 0xcc}, // Percent25
new byte[] {0x00, 0xcc, 0x04, 0xcc, 0x00, 0xcc, 0x40, 0xcc}, // Percent30
new byte[] {0x44, 0xcc, 0x22, 0xcc, 0x44, 0xcc, 0x22, 0xcc}, // Percent40
new byte[] {0x55, 0xcc, 0x55, 0xcc, 0x55, 0xcc, 0x55, 0xcc}, // Percent50
new byte[] {0x55, 0xcd, 0x55, 0xee, 0x55, 0xdc, 0x55, 0xee}, // Percent60
new byte[] {0x55, 0xdd, 0x55, 0xff, 0x55, 0xdd, 0x55, 0xff}, // Percent70
new byte[] {0x55, 0xff, 0x55, 0xff, 0x55, 0xff, 0x55, 0xff}, // Percent75
new byte[] {0x55, 0xff, 0x59, 0xff, 0x55, 0xff, 0x99, 0xff}, // Percent80
new byte[] {0x77, 0xff, 0xdd, 0xff, 0x77, 0xff, 0xfd, 0xff}, // Percent90
new byte[] {0x11, 0x22, 0x44, 0x88, 0x11, 0x22, 0x44, 0x88}, // LightDownwardDiagonal
new byte[] {0x88, 0x44, 0x22, 0x11, 0x88, 0x44, 0x22, 0x11}, // LightUpwardDiagonal
new byte[] {0x99, 0x33, 0x66, 0xcc, 0x99, 0x33, 0x66, 0xcc}, // DarkDownwardDiagonal
new byte[] {0xcc, 0x66, 0x33, 0x99, 0xcc, 0x66, 0x33, 0x99}, // DarkUpwardDiagonal
new byte[] {0xc1, 0x83, 0x07, 0x0e, 0x1c, 0x38, 0x70, 0xe0}, // WideDownwardDiagonal
new byte[] {0xe0, 0x70, 0x38, 0x1c, 0x0e, 0x07, 0x83, 0xc1}, // WideUpwardDiagonal
new byte[] {0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88}, // LightVertical
new byte[] {0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff}, // LightHorizontal
new byte[] {0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa}, // NarrowVertical
new byte[] {0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff}, // NarrowHorizontal
new byte[] {0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc}, // DarkVertical
new byte[] {0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff}, // DarkHorizontal
new byte[] {0x11, 0x22, 0x44, 0x88, 0x00, 0x00, 0x00, 0x00}, // DashedDownwardDiagonal
new byte[] {0x88, 0x44, 0x22, 0x11, 0x00, 0x00, 0x00, 0x00}, // DashedUpwardDiagonal
new byte[] {0x0f, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x00, 0x00}, // DashedHorizontal
new byte[] {0x01, 0x01, 0x01, 0x01, 0x10, 0x10, 0x10, 0x10}, // DashedVertical
new byte[] {0x01, 0x08, 0x80, 0x10, 0x02, 0x40, 0x04, 0x20}, // SmallConfetti
new byte[] {0x03, 0x63, 0x6c, 0x0c, 0xc0, 0xc6, 0x36, 0x30}, // LargeConfetti
new byte[] {0x03, 0x84, 0x48, 0x30, 0x03, 0x84, 0x48, 0x30}, // ZigZag
new byte[] {0x30, 0x49, 0x06, 0x00, 0x30, 0x49, 0x06, 0x00}, // Wave
new byte[] {0x81, 0x42, 0x24, 0x18, 0x08, 0x04, 0x02, 0x01}, // DiagonalBrick
new byte[] {0xff, 0x01, 0x01, 0x01, 0xff, 0x10, 0x10, 0x10}, // HorizontalBrick
new byte[] {0x11, 0x82, 0x44, 0xa8, 0x11, 0xa2, 0x44, 0x2a}, // Weave
new byte[] {0x55, 0xaa, 0x55, 0xaa, 0x0f, 0x0f, 0x0f, 0x0f}, // Plaid
new byte[] {0x02, 0x01, 0x02, 0x00, 0x10, 0x20, 0x10, 0x00}, // Divot
new byte[] {0x55, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00}, // DottedGrid
new byte[] {0x11, 0x00, 0x04, 0x00, 0x11, 0x00, 0x40, 0x00}, // DottedDiamond
new byte[] {0x03, 0x0c, 0x10, 0x20, 0x20, 0x30, 0x48, 0x84}, // Shingle
new byte[] {0xff, 0x33, 0xff, 0xcc, 0xff, 0x33, 0xff, 0xcc}, // Trellis
new byte[] {0xee, 0x19, 0x1f, 0x1f, 0xee, 0x91, 0xf1, 0xf1}, // Sphere
new byte[] {0xff, 0x11, 0x11, 0x11, 0xff, 0x11, 0x11, 0x11}, // SmallGrid
new byte[] {0x33, 0x33, 0xcc, 0xcc, 0x33, 0x33, 0xcc, 0xcc}, // SmallCheckerBoard
new byte[] {0x0f, 0x0f, 0x0f, 0x0f, 0xf0, 0xf0, 0xf0, 0xf0}, // LargeCheckerBoard
new byte[] {0x01, 0x82, 0x44, 0x28, 0x10, 0x28, 0x44, 0x82}, // OutlinedDiamond
new byte[] {0x08, 0x1c, 0x3e, 0x7f, 0x3e, 0x1c, 0x08, 0x00} // SolidDiamond
};
public Brush GetHatchBrush(HatchStyle hatchStyle, Color foreColor, Color backColor)
{
var hatchData = GetHatchData(hatchStyle);
var foreGeometryGroup = new GeometryGroup();
for (var y = 0; y < 8; y++)
for (var x = 0; x < 8; x++)
{
for (var x = 0; x < 8; x++)
if ((hatchData[y] & (0x80 >> x)) > 0)
{
if ((hatchData[y] & (0x80 >> x)) > 0)
foreGeometryGroup.Children.Add(new RectangleGeometry(new Rect(x, y, 1, 1)));
}
}
}
var drawingBrush = new DrawingBrush
{
Viewport = new Rect(0, 0, 8, 8),
ViewportUnits = BrushMappingMode.Absolute,
Stretch = Stretch.None,
TileMode = TileMode.Tile,
Drawing = new DrawingGroup
{
Children =
{
new GeometryDrawing
{
foreGeometryGroup.Children.Add(new RectangleGeometry(new Rect(x, y, 1, 1)));
Brush = new SolidColorBrush(backColor),
Geometry = new RectangleGeometry(new Rect(0, 0, 8, 8))
},
new GeometryDrawing
{
Brush = new SolidColorBrush(foreColor),
Geometry = foreGeometryGroup
}
}
}
};
RenderOptions.SetCachingHint(drawingBrush, CachingHint.Cache);
var drawingBrush = new DrawingBrush
{
Viewport = new Rect(0, 0, 8, 8),
ViewportUnits = BrushMappingMode.Absolute,
Stretch = Stretch.None,
TileMode = TileMode.Tile,
Drawing = new DrawingGroup
{
Children =
{
new GeometryDrawing
{
Brush = new SolidColorBrush(backColor),
Geometry = new RectangleGeometry(new Rect(0, 0, 8, 8))
},
new GeometryDrawing
{
Brush = new SolidColorBrush(foreColor),
Geometry = foreGeometryGroup
}
}
}
};
RenderOptions.SetCachingHint(drawingBrush, CachingHint.Cache);
return drawingBrush;
}
private byte[] GetHatchData(HatchStyle hatchStyle) =>
hatchStyle < HatchStyle.Horizontal || hatchStyle > HatchStyle.SolidDiamond
? throw new ArgumentOutOfRangeException(nameof(hatchStyle))
: HatchBrushes[(int) hatchStyle];
return drawingBrush;
}
}
private byte[] GetHatchData(HatchStyle hatchStyle) =>
hatchStyle < HatchStyle.Horizontal || hatchStyle > HatchStyle.SolidDiamond
? throw new ArgumentOutOfRangeException(nameof(hatchStyle))
: HatchBrushes[(int) hatchStyle];
}
@@ -6,270 +6,269 @@ using System.Threading;
using HandyControl.Controls;
using HandyControl.Data;
namespace HandyControl.Tools
namespace HandyControl.Tools;
internal class ImageAnimator
{
internal class ImageAnimator
private static List<GifImageInfo> ImageInfoList;
private static readonly ReaderWriterLock RwImgListLock = new();
private static Thread AnimationThread;
private static bool AnyFrameDirty;
[ThreadStatic]
private static int ThreadWriterLockWaitCount;
public static bool CanAnimate(GifImage image)
{
private static List<GifImageInfo> ImageInfoList;
private static readonly ReaderWriterLock RwImgListLock = new();
private static Thread AnimationThread;
private static bool AnyFrameDirty;
[ThreadStatic]
private static int ThreadWriterLockWaitCount;
public static bool CanAnimate(GifImage image)
if (image == null)
{
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)
lock (image)
{
if (image == null)
var dimensions = image.FrameDimensionsList;
if (dimensions.Select(guid => new GifFrameDimension(guid)).Contains(GifFrameDimension.Time))
{
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
//
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 = nameof(ImageAnimator),
IsBackground = true
};
AnimationThread.Start();
}
}
}
finally
{
if (readerLockHeld)
{
RwImgListLock.DowngradeFromWriterLock(ref lockDowngradeCookie);
}
else
{
RwImgListLock.ReleaseWriterLock();
}
return image.GetFrameCount(GifFrameDimension.Time) > 1;
}
}
public static void StopAnimate(GifImage image, EventHandler onFrameChangedHandler)
return false;
}
public static void Animate(GifImage image, EventHandler onFrameChangedHandler)
{
if (image == null)
{
// Make sure we have a list of images
if (image == null || ImageInfoList == 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)
{
return;
lockDowngradeCookie = RwImgListLock.UpgradeToWriterLock(Timeout.Infinite);
}
// 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
else
{
if (readerLockHeld)
{
lockDowngradeCookie = RwImgListLock.UpgradeToWriterLock(Timeout.Infinite);
}
else
{
RwImgListLock.AcquireWriterLock(Timeout.Infinite);
}
}
finally
{
ThreadWriterLockWaitCount--;
RwImgListLock.AcquireWriterLock(Timeout.Infinite);
}
}
finally
{
ThreadWriterLockWaitCount--;
}
try
try
{
if (imageInfo.Animated)
{
// Find the corresponding reference and remove it
for (var i = 0; i < ImageInfoList.Count; i++)
// Construct the image array
//
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)
{
var imageInfo = ImageInfoList[i];
if (Equals(image, imageInfo.Image))
AnimationThread = new Thread(AnimateImages50Ms)
{
if (onFrameChangedHandler == imageInfo.FrameChangedHandler || onFrameChangedHandler != null && onFrameChangedHandler.Equals(imageInfo.FrameChangedHandler))
{
ImageInfoList.Remove(imageInfo);
}
break;
}
}
if (!ImageInfoList.Any())
{
AnimationThread?.Join();
AnimationThread = null;
}
}
finally
{
if (readerLockHeld)
{
RwImgListLock.DowngradeFromWriterLock(ref lockDowngradeCookie);
}
else
{
RwImgListLock.ReleaseWriterLock();
Name = nameof(ImageAnimator),
IsBackground = true
};
AnimationThread.Start();
}
}
}
public static void UpdateFrames()
finally
{
if (!AnyFrameDirty || ImageInfoList == null)
if (readerLockHeld)
{
return;
RwImgListLock.DowngradeFromWriterLock(ref lockDowngradeCookie);
}
if (ThreadWriterLockWaitCount > 0)
else
{
// Cannot acquire reader lock at this time, frames update will be missed.
return;
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 (var 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;
}
}
if (!ImageInfoList.Any())
{
AnimationThread?.Join();
AnimationThread = null;
}
}
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 (ImageInfoList.Any())
{
// 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)
{
// See comment in the class header about locking the image ref.
lock (imageInfo.Image)
// 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.UpdateFrame();
imageInfo.FrameTimer = 0;
if (imageInfo.Frame + 1 < imageInfo.FrameCount)
{
imageInfo.Frame++;
}
else
{
imageInfo.Frame = 0;
}
if (imageInfo.FrameDirty)
{
AnyFrameDirty = true;
}
}
}
AnyFrameDirty = false;
}
finally
{
RwImgListLock.ReleaseReaderLock();
}
}
[SuppressMessage("Microsoft.Performance", "CA1804:RemoveUnusedLocals")]
private static void AnimateImages50Ms()
{
while (ImageInfoList.Any())
{
// 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);
}
Thread.Sleep(50);
}
}
}
@@ -5,117 +5,116 @@ using System.Windows;
using System.Windows.Input;
using HandyControl.Data;
namespace HandyControl.Tools
namespace HandyControl.Tools;
public class GlobalShortcut
{
public class GlobalShortcut
private static ObservableCollection<KeyBinding> KeyBindingCollection;
private static readonly Dictionary<string, KeyBinding> CommandDic = new();
private static void KeyboardHook_KeyDown(object sender, KeyboardHookEventArgs e) => HitTest(e.Key);
static GlobalShortcut()
{
private static ObservableCollection<KeyBinding> KeyBindingCollection;
private static readonly Dictionary<string, KeyBinding> CommandDic = new();
private static void KeyboardHook_KeyDown(object sender, KeyboardHookEventArgs e) => HitTest(e.Key);
static GlobalShortcut()
{
KeyboardHook.KeyDown += KeyboardHook_KeyDown;
}
private static void HitTest(Key key)
{
if (IsModifierKey(key)) return;
var modifierKeys = Keyboard.Modifiers;
var keyStr = modifierKeys != ModifierKeys.None ? $"{modifierKeys.ToString()}, {key.ToString()}" : key.ToString();
ExecuteCommand(keyStr);
}
private static void ExecuteCommand(string key)
{
if (string.IsNullOrEmpty(key)) return;
if (CommandDic.ContainsKey(key))
{
var keyBinding = CommandDic[key];
var command = keyBinding.Command;
if (command != null && command.CanExecute(keyBinding.CommandParameter))
{
command.Execute(keyBinding.CommandParameter);
}
}
}
private static bool IsModifierKey(Key key)
{
return key == Key.LeftCtrl || key == Key.LeftAlt || key == Key.LeftShift || key == Key.LWin ||
key == Key.RightCtrl || key == Key.RightAlt || key == Key.RightShift || key == Key.RWin;
}
public static void Init(List<KeyBinding> list)
{
CommandDic.Clear();
if (list == null) return;
KeyBindingCollection = new ObservableCollection<KeyBinding>(list);
if (KeyBindingCollection.Count == 0) return;
AddKeyBindings(KeyBindingCollection);
KeyboardHook.Start();
}
private static void AddKeyBindings(IEnumerable<KeyBinding> keyBindings)
{
foreach (var item in keyBindings)
{
if (item.Key == Key.None) continue;
if (item.Modifiers == ModifierKeys.None)
{
CommandDic[item.Key.ToString()] = item;
}
else
{
var keyStr = $"{item.Modifiers.ToString()}, {item.Key.ToString()}";
CommandDic[keyStr] = item;
}
}
}
public static readonly DependencyProperty HostProperty = DependencyProperty.RegisterAttached(
"Host", typeof(bool), typeof(GlobalShortcut), new PropertyMetadata(ValueBoxes.FalseBox, OnHostChanged));
private static void OnHostChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (DesignerHelper.IsInDesignMode) return;
if (KeyBindingCollection != null)
{
KeyBindingCollection.CollectionChanged -= KeyBindingCollection_CollectionChanged;
}
KeyBindingCollection = GetKeyBindings(d);
if (KeyBindingCollection != null)
{
KeyBindingCollection.CollectionChanged += KeyBindingCollection_CollectionChanged;
}
}
private static void KeyBindingCollection_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
AddKeyBindings(KeyBindingCollection);
KeyboardHook.Start();
}
public static void SetHost(DependencyObject element, bool value)
=> element.SetValue(HostProperty, ValueBoxes.BooleanBox(value));
public static bool GetHost(DependencyObject element)
=> (bool) element.GetValue(HostProperty);
public static readonly DependencyProperty KeyBindingsProperty = DependencyProperty.RegisterAttached(
"KeyBindings", typeof(ObservableCollection<KeyBinding>), typeof(GlobalShortcut), new PropertyMetadata(new ObservableCollection<KeyBinding>()));
public static void SetKeyBindings(DependencyObject element, ObservableCollection<KeyBinding> value)
=> element.SetValue(KeyBindingsProperty, value);
public static ObservableCollection<KeyBinding> GetKeyBindings(DependencyObject element)
=> (ObservableCollection<KeyBinding>) element.GetValue(KeyBindingsProperty);
KeyboardHook.KeyDown += KeyboardHook_KeyDown;
}
}
private static void HitTest(Key key)
{
if (IsModifierKey(key)) return;
var modifierKeys = Keyboard.Modifiers;
var keyStr = modifierKeys != ModifierKeys.None ? $"{modifierKeys.ToString()}, {key.ToString()}" : key.ToString();
ExecuteCommand(keyStr);
}
private static void ExecuteCommand(string key)
{
if (string.IsNullOrEmpty(key)) return;
if (CommandDic.ContainsKey(key))
{
var keyBinding = CommandDic[key];
var command = keyBinding.Command;
if (command != null && command.CanExecute(keyBinding.CommandParameter))
{
command.Execute(keyBinding.CommandParameter);
}
}
}
private static bool IsModifierKey(Key key)
{
return key == Key.LeftCtrl || key == Key.LeftAlt || key == Key.LeftShift || key == Key.LWin ||
key == Key.RightCtrl || key == Key.RightAlt || key == Key.RightShift || key == Key.RWin;
}
public static void Init(List<KeyBinding> list)
{
CommandDic.Clear();
if (list == null) return;
KeyBindingCollection = new ObservableCollection<KeyBinding>(list);
if (KeyBindingCollection.Count == 0) return;
AddKeyBindings(KeyBindingCollection);
KeyboardHook.Start();
}
private static void AddKeyBindings(IEnumerable<KeyBinding> keyBindings)
{
foreach (var item in keyBindings)
{
if (item.Key == Key.None) continue;
if (item.Modifiers == ModifierKeys.None)
{
CommandDic[item.Key.ToString()] = item;
}
else
{
var keyStr = $"{item.Modifiers.ToString()}, {item.Key.ToString()}";
CommandDic[keyStr] = item;
}
}
}
public static readonly DependencyProperty HostProperty = DependencyProperty.RegisterAttached(
"Host", typeof(bool), typeof(GlobalShortcut), new PropertyMetadata(ValueBoxes.FalseBox, OnHostChanged));
private static void OnHostChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (DesignerHelper.IsInDesignMode) return;
if (KeyBindingCollection != null)
{
KeyBindingCollection.CollectionChanged -= KeyBindingCollection_CollectionChanged;
}
KeyBindingCollection = GetKeyBindings(d);
if (KeyBindingCollection != null)
{
KeyBindingCollection.CollectionChanged += KeyBindingCollection_CollectionChanged;
}
}
private static void KeyBindingCollection_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
AddKeyBindings(KeyBindingCollection);
KeyboardHook.Start();
}
public static void SetHost(DependencyObject element, bool value)
=> element.SetValue(HostProperty, ValueBoxes.BooleanBox(value));
public static bool GetHost(DependencyObject element)
=> (bool) element.GetValue(HostProperty);
public static readonly DependencyProperty KeyBindingsProperty = DependencyProperty.RegisterAttached(
"KeyBindings", typeof(ObservableCollection<KeyBinding>), typeof(GlobalShortcut), new PropertyMetadata(new ObservableCollection<KeyBinding>()));
public static void SetKeyBindings(DependencyObject element, ObservableCollection<KeyBinding> value)
=> element.SetValue(KeyBindingsProperty, value);
public static ObservableCollection<KeyBinding> GetKeyBindings(DependencyObject element)
=> (ObservableCollection<KeyBinding>) element.GetValue(KeyBindingsProperty);
}
@@ -6,89 +6,88 @@ using System.Windows.Media;
using System.Windows.Media.Animation;
using HandyControl.Tools.Extension;
namespace HandyControl.Tools
namespace HandyControl.Tools;
/// <summary>
/// 包含一些常用的动画辅助方法
/// </summary>
public class AnimationHelper
{
/// <summary>
/// 包含一些常用的动画辅助方法
/// 创建一个Thickness动画
/// </summary>
public class AnimationHelper
/// <param name="thickness"></param>
/// <param name="milliseconds"></param>
/// <returns></returns>
public static ThicknessAnimation CreateAnimation(Thickness thickness = default, double milliseconds = 200)
{
/// <summary>
/// 创建一个Thickness动画
/// </summary>
/// <param name="thickness"></param>
/// <param name="milliseconds"></param>
/// <returns></returns>
public static ThicknessAnimation CreateAnimation(Thickness thickness = default, double milliseconds = 200)
return new(thickness, new Duration(TimeSpan.FromMilliseconds(milliseconds)))
{
return new(thickness, new Duration(TimeSpan.FromMilliseconds(milliseconds)))
{
EasingFunction = new PowerEase { EasingMode = EasingMode.EaseInOut }
};
}
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)
/// <summary>
/// 创建一个Double动画
/// </summary>
/// <param name="toValue"></param>
/// <param name="milliseconds"></param>
/// <returns></returns>
public static DoubleAnimation CreateAnimation(double toValue, double milliseconds = 200)
{
return new(toValue, new Duration(TimeSpan.FromMilliseconds(milliseconds)))
{
return new(toValue, new Duration(TimeSpan.FromMilliseconds(milliseconds)))
{
EasingFunction = new PowerEase { EasingMode = EasingMode.EaseInOut }
};
}
EasingFunction = new PowerEase { EasingMode = EasingMode.EaseInOut }
};
}
internal static void DecomposeGeometryStr(string geometryStr, out double[] arr)
internal static void DecomposeGeometryStr(string geometryStr, out double[] arr)
{
var collection = Regex.Matches(geometryStr, RegexPatterns.DigitsPattern);
arr = new double[collection.Count];
for (var i = 0; i < collection.Count; i++)
{
var collection = Regex.Matches(geometryStr, RegexPatterns.DigitsPattern);
arr = new double[collection.Count];
for (var i = 0; i < collection.Count; i++)
{
arr[i] = collection[i].Value.Value<double>();
}
}
internal static Geometry ComposeGeometry(string[] strings, double[] arr)
{
var builder = new StringBuilder(strings[0]);
for (var i = 0; i < arr.Length; i++)
{
var s = strings[i + 1];
var n = arr[i];
if (!double.IsNaN(n))
{
builder.Append(n).Append(s);
}
}
return Geometry.Parse(builder.ToString());
}
internal static Geometry InterpolateGeometry(double[] from, double[] to, double progress, string[] strings)
{
var accumulated = new double[to.Length];
for (var i = 0; i < to.Length; i++)
{
var fromValue = from[i];
accumulated[i] = fromValue + (to[i] - fromValue) * progress;
}
return ComposeGeometry(strings, accumulated);
}
internal static double[] InterpolateGeometryValue(double[] from, double[] to, double progress)
{
var accumulated = new double[to.Length];
for (var i = 0; i < to.Length; i++)
{
var fromValue = from[i];
accumulated[i] = fromValue + (to[i] - fromValue) * progress;
}
return accumulated;
arr[i] = collection[i].Value.Value<double>();
}
}
}
internal static Geometry ComposeGeometry(string[] strings, double[] arr)
{
var builder = new StringBuilder(strings[0]);
for (var i = 0; i < arr.Length; i++)
{
var s = strings[i + 1];
var n = arr[i];
if (!double.IsNaN(n))
{
builder.Append(n).Append(s);
}
}
return Geometry.Parse(builder.ToString());
}
internal static Geometry InterpolateGeometry(double[] from, double[] to, double progress, string[] strings)
{
var accumulated = new double[to.Length];
for (var i = 0; i < to.Length; i++)
{
var fromValue = from[i];
accumulated[i] = fromValue + (to[i] - fromValue) * progress;
}
return ComposeGeometry(strings, accumulated);
}
internal static double[] InterpolateGeometryValue(double[] from, double[] to, double progress)
{
var accumulated = new double[to.Length];
for (var i = 0; i < to.Length; i++)
{
var fromValue = from[i];
accumulated[i] = fromValue + (to[i] - fromValue) * progress;
}
return accumulated;
}
}
@@ -4,124 +4,123 @@ using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Media.Media3D;
namespace HandyControl.Tools
namespace HandyControl.Tools;
/// <summary>
/// 包含内部使用的一些简单算法
/// </summary>
internal class ArithmeticHelper
{
/// <summary>
/// 包含内部使用的一些简单算法
/// 平分一个整数到一个数组中
/// </summary>
internal class ArithmeticHelper
/// <param name="num"></param>
/// <param name="count"></param>
/// <returns></returns>
public static int[] DivideInt2Arr(int num, int count)
{
/// <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 (var i = 0; i < count; i++)
{
var arr = new int[count];
var div = num / count;
var rest = num % count;
for (var i = 0; i < count; i++)
{
arr[i] = div;
}
for (var i = 0; i < rest; i++)
{
arr[i] += 1;
}
return arr;
arr[i] = div;
}
/// <summary>
/// 计算控件在窗口中的可见坐标
/// </summary>
public static Point CalSafePoint(FrameworkElement element, FrameworkElement showElement, Thickness thickness = default)
for (var i = 0; i < rest; i++)
{
if (element == null || showElement == null) return default;
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;
x = element.HorizontalAlignment switch
{
HorizontalAlignment.Left => layoutSlot.Left + margin.Left,
HorizontalAlignment.Center => (layoutSlot.Left + margin.Left + layoutSlot.Right - margin.Right) / 2.0 -
width / 2.0,
HorizontalAlignment.Right => layoutSlot.Right - margin.Right - width,
HorizontalAlignment.Stretch => Math.Max(layoutSlot.Left + margin.Left,
(layoutSlot.Left + margin.Left + layoutSlot.Right - margin.Right) / 2.0 - width / 2.0),
_ => x
};
y = element.VerticalAlignment switch
{
VerticalAlignment.Top => layoutSlot.Top + margin.Top,
VerticalAlignment.Center => (layoutSlot.Top + margin.Top + layoutSlot.Bottom - margin.Bottom) / 2.0 -
height / 2.0,
VerticalAlignment.Bottom => layoutSlot.Bottom - margin.Bottom - height,
VerticalAlignment.Stretch => Math.Max(layoutSlot.Top + margin.Top,
(layoutSlot.Top + margin.Top + layoutSlot.Bottom - margin.Bottom) / 2.0 - height / 2.0),
_ => y
};
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="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);
arr[i] += 1;
}
return arr;
}
}
/// <summary>
/// 计算控件在窗口中的可见坐标
/// </summary>
public static Point CalSafePoint(FrameworkElement element, FrameworkElement showElement, Thickness thickness = default)
{
if (element == null || showElement == null) return default;
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;
x = element.HorizontalAlignment switch
{
HorizontalAlignment.Left => layoutSlot.Left + margin.Left,
HorizontalAlignment.Center => (layoutSlot.Left + margin.Left + layoutSlot.Right - margin.Right) / 2.0 -
width / 2.0,
HorizontalAlignment.Right => layoutSlot.Right - margin.Right - width,
HorizontalAlignment.Stretch => Math.Max(layoutSlot.Left + margin.Left,
(layoutSlot.Left + margin.Left + layoutSlot.Right - margin.Right) / 2.0 - width / 2.0),
_ => x
};
y = element.VerticalAlignment switch
{
VerticalAlignment.Top => layoutSlot.Top + margin.Top,
VerticalAlignment.Center => (layoutSlot.Top + margin.Top + layoutSlot.Bottom - margin.Bottom) / 2.0 -
height / 2.0,
VerticalAlignment.Bottom => layoutSlot.Bottom - margin.Bottom - height,
VerticalAlignment.Stretch => Math.Max(layoutSlot.Top + margin.Top,
(layoutSlot.Top + margin.Top + layoutSlot.Bottom - margin.Bottom) / 2.0 - height / 2.0),
_ => y
};
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="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);
}
}
@@ -2,30 +2,29 @@
using System.Windows.Controls;
using System.Windows.Data;
namespace HandyControl.Tools.Helper
namespace HandyControl.Tools.Helper;
public class BindingHelper
{
public class BindingHelper
public static string GetString(object source, string path)
{
public static string GetString(object source, string path)
if (string.IsNullOrEmpty(path))
{
if (string.IsNullOrEmpty(path))
{
return source == null ? string.Empty : source.ToString();
}
var tempObj = new DependencyObject();
var binding = new Binding(path)
{
Mode = BindingMode.OneTime,
Source = source
};
BindingOperations.SetBinding(tempObj, TextSearch.TextProperty, binding);
var result = (string) tempObj.GetValue(TextSearch.TextProperty);
BindingOperations.ClearBinding(tempObj, TextSearch.TextProperty);
return result;
return source == null ? string.Empty : source.ToString();
}
var tempObj = new DependencyObject();
var binding = new Binding(path)
{
Mode = BindingMode.OneTime,
Source = source
};
BindingOperations.SetBinding(tempObj, TextSearch.TextProperty, binding);
var result = (string) tempObj.GetValue(TextSearch.TextProperty);
BindingOperations.ClearBinding(tempObj, TextSearch.TextProperty);
return result;
}
}
}
@@ -1,20 +1,19 @@
using System;
using System.Windows.Media;
namespace HandyControl.Tools
namespace HandyControl.Tools;
public class ColorHelper
{
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)
{
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]);
}
var bytes = BitConverter.GetBytes(c);
return Color.FromArgb(bytes[3], bytes[2], bytes[1], bytes[0]);
}
}
}
@@ -11,78 +11,77 @@ using System.Windows.Navigation;
using HandyControl.Data;
using HandyControl.Properties.Langs;
namespace HandyControl.Tools
namespace HandyControl.Tools;
public class ConfigHelper : INotifyPropertyChanged
{
public class ConfigHelper : INotifyPropertyChanged
private ConfigHelper()
{
private ConfigHelper()
}
public static ConfigHelper Instance = new Lazy<ConfigHelper>(() => new ConfigHelper()).Value;
private XmlLanguage _lang = XmlLanguage.GetLanguage("zh-cn");
public XmlLanguage Lang
{
get => _lang;
set
{
}
public static ConfigHelper Instance = new Lazy<ConfigHelper>(() => new ConfigHelper()).Value;
private XmlLanguage _lang = XmlLanguage.GetLanguage("zh-cn");
public XmlLanguage Lang
{
get => _lang;
set
if (!_lang.IetfLanguageTag.Equals(value.IetfLanguageTag))
{
if (!_lang.IetfLanguageTag.Equals(value.IetfLanguageTag))
{
_lang = value;
OnPropertyChanged(nameof(Lang));
}
_lang = value;
OnPropertyChanged(nameof(Lang));
}
}
}
public void SetLang(string lang)
{
LangProvider.Culture = new CultureInfo(lang);
Application.Current.Dispatcher.Thread.CurrentUICulture = new CultureInfo(lang);
Lang = XmlLanguage.GetLanguage(lang);
}
public void SetLang(string lang)
{
LangProvider.Culture = new CultureInfo(lang);
Application.Current.Dispatcher.Thread.CurrentUICulture = new CultureInfo(lang);
Lang = XmlLanguage.GetLanguage(lang);
}
public void SetConfig(HandyControlConfig config)
{
SetLang(config.Lang);
SetTimelineFrameRate(config.TimelineFrameRate);
}
public void SetConfig(HandyControlConfig config)
{
SetLang(config.Lang);
SetTimelineFrameRate(config.TimelineFrameRate);
}
public void SetTimelineFrameRate(int rate) =>
Timeline.DesiredFrameRateProperty.OverrideMetadata(typeof(Timeline), new FrameworkPropertyMetadata(rate));
public void SetTimelineFrameRate(int rate) =>
Timeline.DesiredFrameRateProperty.OverrideMetadata(typeof(Timeline), new FrameworkPropertyMetadata(rate));
public void SetWindowDefaultStyle(object resourceKey = null)
{
var metadata = resourceKey == null
? new FrameworkPropertyMetadata(Application.Current.FindResource(typeof(Window)))
: new FrameworkPropertyMetadata(Application.Current.FindResource(resourceKey));
public void SetWindowDefaultStyle(object resourceKey = null)
{
var metadata = resourceKey == null
? new FrameworkPropertyMetadata(Application.Current.FindResource(typeof(Window)))
: new FrameworkPropertyMetadata(Application.Current.FindResource(resourceKey));
FrameworkElement.StyleProperty.OverrideMetadata(typeof(Window), metadata);
}
FrameworkElement.StyleProperty.OverrideMetadata(typeof(Window), metadata);
}
public void SetNavigationWindowDefaultStyle(object resourceKey = null)
{
var metadata = resourceKey == null
? new FrameworkPropertyMetadata(Application.Current.FindResource(typeof(NavigationWindow)))
: new FrameworkPropertyMetadata(Application.Current.FindResource(resourceKey));
public void SetNavigationWindowDefaultStyle(object resourceKey = null)
{
var metadata = resourceKey == null
? new FrameworkPropertyMetadata(Application.Current.FindResource(typeof(NavigationWindow)))
: new FrameworkPropertyMetadata(Application.Current.FindResource(resourceKey));
FrameworkElement.StyleProperty.OverrideMetadata(typeof(NavigationWindow), metadata);
}
FrameworkElement.StyleProperty.OverrideMetadata(typeof(NavigationWindow), metadata);
}
public event PropertyChangedEventHandler PropertyChanged;
public event PropertyChangedEventHandler PropertyChanged;
#if NET40
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
#else
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
#endif
}
}
}
@@ -1,22 +1,21 @@
using System.ComponentModel;
using System.Windows;
namespace HandyControl.Tools
{
public class DesignerHelper
{
private static bool? _isInDesignMode;
namespace HandyControl.Tools;
public static bool IsInDesignMode
public class DesignerHelper
{
private static bool? _isInDesignMode;
public static bool IsInDesignMode
{
get
{
get
if (!_isInDesignMode.HasValue)
{
if (!_isInDesignMode.HasValue)
{
_isInDesignMode = (bool) DependencyPropertyDescriptor.FromProperty(DesignerProperties.IsInDesignModeProperty, typeof(FrameworkElement)).Metadata.DefaultValue;
}
return _isInDesignMode.Value;
_isInDesignMode = (bool) DependencyPropertyDescriptor.FromProperty(DesignerProperties.IsInDesignModeProperty, typeof(FrameworkElement)).Metadata.DefaultValue;
}
return _isInDesignMode.Value;
}
}
}
}
@@ -4,101 +4,100 @@ using System.Windows.Media;
using HandyControl.Expression.Drawing;
using HandyControl.Tools.Interop;
namespace HandyControl.Tools
namespace HandyControl.Tools;
internal static class DpiHelper
{
internal static class DpiHelper
private const double LogicalDpi = 96.0;
[ThreadStatic]
private static Matrix _transformToDip;
static DpiHelper()
{
private const double LogicalDpi = 96.0;
[ThreadStatic]
private static Matrix _transformToDip;
static DpiHelper()
var dC = InteropMethods.GetDC(IntPtr.Zero);
if (dC != IntPtr.Zero)
{
var dC = InteropMethods.GetDC(IntPtr.Zero);
if (dC != IntPtr.Zero)
{
// 沿着屏幕宽度每逻辑英寸的像素数。在具有多个显示器的系统中,这个值对所有显示器都是相同的
const int logicPixelsX = 88;
// 沿着屏幕高度每逻辑英寸的像素数
const int logicPixelsY = 90;
DeviceDpiX = InteropMethods.GetDeviceCaps(dC, logicPixelsX);
DeviceDpiY = InteropMethods.GetDeviceCaps(dC, logicPixelsY);
InteropMethods.ReleaseDC(IntPtr.Zero, dC);
}
else
{
DeviceDpiX = LogicalDpi;
DeviceDpiY = LogicalDpi;
}
var identity = Matrix.Identity;
var identity2 = Matrix.Identity;
identity.Scale(DeviceDpiX / LogicalDpi, DeviceDpiY / LogicalDpi);
identity2.Scale(LogicalDpi / DeviceDpiX, LogicalDpi / DeviceDpiY);
TransformFromDevice = new MatrixTransform(identity2);
TransformFromDevice.Freeze();
TransformToDevice = new MatrixTransform(identity);
TransformToDevice.Freeze();
// 沿着屏幕宽度每逻辑英寸的像素数。在具有多个显示器的系统中,这个值对所有显示器都是相同的
const int logicPixelsX = 88;
// 沿着屏幕高度每逻辑英寸的像素数
const int logicPixelsY = 90;
DeviceDpiX = InteropMethods.GetDeviceCaps(dC, logicPixelsX);
DeviceDpiY = InteropMethods.GetDeviceCaps(dC, logicPixelsY);
InteropMethods.ReleaseDC(IntPtr.Zero, dC);
}
else
{
DeviceDpiX = LogicalDpi;
DeviceDpiY = LogicalDpi;
}
public static MatrixTransform TransformFromDevice { get; }
public static MatrixTransform TransformToDevice { get; }
public static double DeviceDpiX { get; }
public static double DeviceDpiY { get; }
public static double LogicalToDeviceUnitsScalingFactorX => TransformToDevice.Matrix.M11;
public static double LogicalToDeviceUnitsScalingFactorY => TransformToDevice.Matrix.M22;
public static Point DevicePixelsToLogical(Point devicePoint, double dpiScaleX, double dpiScaleY)
{
_transformToDip = Matrix.Identity;
_transformToDip.Scale(1d / dpiScaleX, 1d / dpiScaleY);
return _transformToDip.Transform(devicePoint);
}
public static Size DeviceSizeToLogical(Size deviceSize, double dpiScaleX, double dpiScaleY)
{
var pt = DevicePixelsToLogical(new Point(deviceSize.Width, deviceSize.Height), dpiScaleX, dpiScaleY);
return new Size(pt.X, pt.Y);
}
public static Rect LogicalToDeviceUnits(this Rect logicalRect)
{
var result = logicalRect;
result.Transform(TransformToDevice.Matrix);
return result;
}
public static Rect DeviceToLogicalUnits(this Rect deviceRect)
{
var result = deviceRect;
result.Transform(TransformFromDevice.Matrix);
return result;
}
public static double RoundLayoutValue(double value, double dpiScale)
{
double newValue;
if (!MathHelper.AreClose(dpiScale, 1.0))
{
newValue = Math.Round(value * dpiScale) / dpiScale;
if (double.IsNaN(newValue) || double.IsInfinity(newValue) || MathHelper.AreClose(newValue, double.MaxValue))
{
newValue = value;
}
}
else
{
newValue = Math.Round(value);
}
return newValue;
}
var identity = Matrix.Identity;
var identity2 = Matrix.Identity;
identity.Scale(DeviceDpiX / LogicalDpi, DeviceDpiY / LogicalDpi);
identity2.Scale(LogicalDpi / DeviceDpiX, LogicalDpi / DeviceDpiY);
TransformFromDevice = new MatrixTransform(identity2);
TransformFromDevice.Freeze();
TransformToDevice = new MatrixTransform(identity);
TransformToDevice.Freeze();
}
}
public static MatrixTransform TransformFromDevice { get; }
public static MatrixTransform TransformToDevice { get; }
public static double DeviceDpiX { get; }
public static double DeviceDpiY { get; }
public static double LogicalToDeviceUnitsScalingFactorX => TransformToDevice.Matrix.M11;
public static double LogicalToDeviceUnitsScalingFactorY => TransformToDevice.Matrix.M22;
public static Point DevicePixelsToLogical(Point devicePoint, double dpiScaleX, double dpiScaleY)
{
_transformToDip = Matrix.Identity;
_transformToDip.Scale(1d / dpiScaleX, 1d / dpiScaleY);
return _transformToDip.Transform(devicePoint);
}
public static Size DeviceSizeToLogical(Size deviceSize, double dpiScaleX, double dpiScaleY)
{
var pt = DevicePixelsToLogical(new Point(deviceSize.Width, deviceSize.Height), dpiScaleX, dpiScaleY);
return new Size(pt.X, pt.Y);
}
public static Rect LogicalToDeviceUnits(this Rect logicalRect)
{
var result = logicalRect;
result.Transform(TransformToDevice.Matrix);
return result;
}
public static Rect DeviceToLogicalUnits(this Rect deviceRect)
{
var result = deviceRect;
result.Transform(TransformFromDevice.Matrix);
return result;
}
public static double RoundLayoutValue(double value, double dpiScale)
{
double newValue;
if (!MathHelper.AreClose(dpiScale, 1.0))
{
newValue = Math.Round(value * dpiScale) / dpiScale;
if (double.IsNaN(newValue) || double.IsInfinity(newValue) || MathHelper.AreClose(newValue, double.MaxValue))
{
newValue = value;
}
}
else
{
newValue = Math.Round(value);
}
return newValue;
}
}
@@ -5,273 +5,272 @@ using System.Windows;
using System.Windows.Interop;
using HandyControl.Tools.Interop;
namespace HandyControl.Controls
namespace HandyControl.Controls;
/// <summary>
/// 用来使窗口变得全屏的辅助类
/// 采用设置窗口位置和尺寸,确保盖住整个屏幕的方式来实现全屏
/// 目前已知需要满足的条件是:窗口盖住整个屏幕、窗口没有WS_THICKFRAME样式、窗口不能有标题栏且最大化
/// </summary>
internal static class FullScreenHelper
{
/// <summary>
/// 用来使窗口变得全屏的辅助类
/// 采用设置窗口位置和尺寸,确保盖住整个屏幕的方式来实现全屏
/// 目前已知需要满足的条件是:窗口盖住整个屏幕、窗口没有WS_THICKFRAME样式、窗口不能有标题栏且最大化
/// 用于记录窗口全屏前位置的附加属性
/// </summary>
internal static class FullScreenHelper
private static readonly DependencyProperty BeforeFullScreenWindowPlacementProperty =
DependencyProperty.RegisterAttached("BeforeFullScreenWindowPlacement",
typeof(InteropValues.WINDOWPLACEMENT?), typeof(FullScreenHelper));
/// <summary>
/// 用于记录窗口全屏前样式的附加属性
/// </summary>
private static readonly DependencyProperty BeforeFullScreenWindowStyleProperty =
DependencyProperty.RegisterAttached("BeforeFullScreenWindowStyle",
typeof(InteropValues.WindowStyles?), typeof(FullScreenHelper));
/// <summary>
/// 开始进入全屏模式
/// 进入全屏模式后,窗口可通过 API 方式(也可以用 Win + Shift + Left/Right)移动,调整大小,但会根据目标矩形寻找显示器重新调整到全屏状态。
/// 进入全屏后,不要修改样式等窗口属性,在退出时,会恢复到进入前的状态
/// 进入全屏模式后会禁用 DWM 过渡动画
/// </summary>
public static void StartFullScreen(System.Windows.Window window)
{
/// <summary>
/// 用于记录窗口全屏前位置的附加属性
/// </summary>
private static readonly DependencyProperty BeforeFullScreenWindowPlacementProperty =
DependencyProperty.RegisterAttached("BeforeFullScreenWindowPlacement",
typeof(InteropValues.WINDOWPLACEMENT?), typeof(FullScreenHelper));
/// <summary>
/// 用于记录窗口全屏前样式的附加属性
/// </summary>
private static readonly DependencyProperty BeforeFullScreenWindowStyleProperty =
DependencyProperty.RegisterAttached("BeforeFullScreenWindowStyle",
typeof(InteropValues.WindowStyles?), typeof(FullScreenHelper));
/// <summary>
/// 开始进入全屏模式
/// 进入全屏模式后,窗口可通过 API 方式(也可以用 Win + Shift + Left/Right)移动,调整大小,但会根据目标矩形寻找显示器重新调整到全屏状态。
/// 进入全屏后,不要修改样式等窗口属性,在退出时,会恢复到进入前的状态
/// 进入全屏模式后会禁用 DWM 过渡动画
/// </summary>
public static void StartFullScreen(System.Windows.Window window)
if (window == null)
{
if (window == null)
throw new ArgumentNullException(nameof(window), $"{nameof(window)} 不能为 null");
}
//确保不在全屏模式
if (window.GetValue(BeforeFullScreenWindowPlacementProperty) == null &&
window.GetValue(BeforeFullScreenWindowStyleProperty) == null)
{
var hwnd = new WindowInteropHelper(window).EnsureHandle();
var hwndSource = HwndSource.FromHwnd(hwnd);
//获取当前窗口的位置大小状态并保存
var placement = InteropMethods.GetWindowPlacement(hwnd);
window.SetValue(BeforeFullScreenWindowPlacementProperty, placement);
//修改窗口样式
var style = (InteropValues.WindowStyles) InteropMethods.GetWindowLongPtr(hwnd, InteropValues.GWL_STYLE);
window.SetValue(BeforeFullScreenWindowStyleProperty, style);
//将窗口恢复到还原模式,在有标题栏的情况下最大化模式下无法全屏,
//这里采用还原,不修改标题栏的方式
//在退出全屏时,窗口原有的状态会恢复
//去掉WS_THICKFRAME,在有该样式的情况下不能全屏
//去掉WS_MAXIMIZEBOX,禁用最大化,如果最大化会退出全屏
//去掉WS_MAXIMIZE,使窗口变成还原状态,不使用ShowWindow(hwnd, ShowWindowCommands.SW_RESTORE),避免看到窗口变成还原状态这一过程(也避免影响窗口的Visible状态)
style &= ~(InteropValues.WindowStyles.WS_THICKFRAME | InteropValues.WindowStyles.WS_MAXIMIZEBOX | InteropValues.WindowStyles.WS_MAXIMIZE);
InteropMethods.SetWindowLong(hwnd, InteropValues.GWL_STYLE, (IntPtr) style);
//禁用 DWM 过渡动画 忽略返回值,若DWM关闭不做处理
InteropMethods.DwmSetWindowAttribute(hwnd, InteropValues.DwmWindowAttribute.DWMWA_TRANSITIONS_FORCEDISABLED, 1,
sizeof(int));
//添加Hook,在窗口尺寸位置等要发生变化时,确保全屏
hwndSource.AddHook(KeepFullScreenHook);
if (InteropMethods.GetWindowRect(hwnd, out var rect))
{
throw new ArgumentNullException(nameof(window), $"{nameof(window)} 不能为 null");
//不能用 placement 的坐标,placement是工作区坐标,不是屏幕坐标。
//使用窗口当前的矩形调用下设置窗口位置和尺寸的方法,让Hook来进行调整窗口位置和尺寸到全屏模式
InteropMethods.SetWindowPos(hwnd, (IntPtr) InteropValues.HWND_TOP, rect.Left, rect.Top, rect.Width,
rect.Height, (int) InteropValues.WindowPositionFlags.SWP_NOZORDER);
}
}
}
/// <summary>
/// 退出全屏模式
/// 窗口会回到进入全屏模式时保存的状态
/// 退出全屏模式后会重新启用 DWM 过渡动画
/// </summary>
public static void EndFullScreen(System.Windows.Window window)
{
if (window == null)
{
throw new ArgumentNullException(nameof(window), $"{nameof(window)} 不能为 null");
}
//确保在全屏模式并获取之前保存的状态
if (window.GetValue(BeforeFullScreenWindowPlacementProperty) is InteropValues.WINDOWPLACEMENT placement
&& window.GetValue(BeforeFullScreenWindowStyleProperty) is InteropValues.WindowStyles style)
{
var hwnd = new WindowInteropHelper(window).Handle;
if (hwnd == IntPtr.Zero)
{
// 句柄为 0 只有两种情况:
// 1. 虽然窗口已进入全屏,但窗口已被关闭;
// 2. 窗口初始化前,在还没有调用 StartFullScreen 的前提下就调用了此方法。
// 所以,直接 return 就好。
return;
}
//确保不在全屏模式
if (window.GetValue(BeforeFullScreenWindowPlacementProperty) == null &&
window.GetValue(BeforeFullScreenWindowStyleProperty) == null)
var hwndSource = HwndSource.FromHwnd(hwnd);
//去除hook
hwndSource.RemoveHook(KeepFullScreenHook);
//恢复保存的状态
//不要改变Style里的WS_MAXIMIZE,否则会使窗口变成最大化状态,但是尺寸不对
//也不要设置回Style里的WS_MINIMIZE,否则会导致窗口最小化按钮显示成还原按钮
InteropMethods.SetWindowLong(hwnd, InteropValues.GWL_STYLE,
(IntPtr) (style & ~(InteropValues.WindowStyles.WS_MAXIMIZE | InteropValues.WindowStyles.WS_MINIMIZE)));
if ((style & InteropValues.WindowStyles.WS_MINIMIZE) != 0)
{
var hwnd = new WindowInteropHelper(window).EnsureHandle();
var hwndSource = HwndSource.FromHwnd(hwnd);
//如果窗口进入全屏前是最小化的,这里不让窗口恢复到之前的最小化状态,而是到还原的状态。
//大多数情况下,都不期望在退出全屏的时候,恢复到最小化。
placement.showCmd = InteropValues.SW.RESTORE;
}
//获取当前窗口的位置大小状态并保存
var placement = InteropMethods.GetWindowPlacement(hwnd);
window.SetValue(BeforeFullScreenWindowPlacementProperty, placement);
if ((style & InteropValues.WindowStyles.WS_MAXIMIZE) != 0)
{
//提前调用 ShowWindow 使窗口恢复最大化,若通过 SetWindowPlacement 最大化会导致闪烁,只靠其恢复 RestoreBounds.
InteropMethods.ShowWindow(hwnd, InteropValues.SW.MAXIMIZE);
}
//修改窗口样式
var style = (InteropValues.WindowStyles) InteropMethods.GetWindowLongPtr(hwnd, InteropValues.GWL_STYLE);
window.SetValue(BeforeFullScreenWindowStyleProperty, style);
//将窗口恢复到还原模式,在有标题栏的情况下最大化模式下无法全屏,
//这里采用还原,不修改标题栏的方式
//在退出全屏时,窗口原有的状态会恢复
//去掉WS_THICKFRAME,在有该样式的情况下不能全屏
//去掉WS_MAXIMIZEBOX,禁用最大化,如果最大化会退出全屏
//去掉WS_MAXIMIZE,使窗口变成还原状态,不使用ShowWindow(hwnd, ShowWindowCommands.SW_RESTORE),避免看到窗口变成还原状态这一过程(也避免影响窗口的Visible状态)
style &= ~(InteropValues.WindowStyles.WS_THICKFRAME | InteropValues.WindowStyles.WS_MAXIMIZEBOX | InteropValues.WindowStyles.WS_MAXIMIZE);
InteropMethods.SetWindowLong(hwnd, InteropValues.GWL_STYLE, (IntPtr) style);
//禁用 DWM 过渡动画 忽略返回值,若DWM关闭不做处理
InteropMethods.DwmSetWindowAttribute(hwnd, InteropValues.DwmWindowAttribute.DWMWA_TRANSITIONS_FORCEDISABLED, 1,
sizeof(int));
//添加Hook,在窗口尺寸位置等要发生变化时,确保全屏
hwndSource.AddHook(KeepFullScreenHook);
InteropMethods.SetWindowPlacement(hwnd, ref placement);
if ((style & InteropValues.WindowStyles.WS_MAXIMIZE) ==
0) //如果窗口是最大化就不要修改WPF属性,否则会破坏RestoreBounds,且WPF窗口自身在最大化时,不会修改 Left Top Width Height 属性
{
if (InteropMethods.GetWindowRect(hwnd, out var rect))
{
//不能用 placement 的坐标,placement是工作区坐标,不是屏幕坐标。
//使用窗口当前的矩形调用下设置窗口位置和尺寸的方法,让Hook来进行调整窗口位置和尺寸到全屏模式
InteropMethods.SetWindowPos(hwnd, (IntPtr) InteropValues.HWND_TOP, rect.Left, rect.Top, rect.Width,
rect.Height, (int) InteropValues.WindowPositionFlags.SWP_NOZORDER);
}
}
}
/// <summary>
/// 退出全屏模式
/// 窗口会回到进入全屏模式时保存的状态
/// 退出全屏模式后会重新启用 DWM 过渡动画
/// </summary>
public static void EndFullScreen(System.Windows.Window window)
{
if (window == null)
{
throw new ArgumentNullException(nameof(window), $"{nameof(window)} 不能为 null");
}
//确保在全屏模式并获取之前保存的状态
if (window.GetValue(BeforeFullScreenWindowPlacementProperty) is InteropValues.WINDOWPLACEMENT placement
&& window.GetValue(BeforeFullScreenWindowStyleProperty) is InteropValues.WindowStyles style)
{
var hwnd = new WindowInteropHelper(window).Handle;
if (hwnd == IntPtr.Zero)
{
// 句柄为 0 只有两种情况:
// 1. 虽然窗口已进入全屏,但窗口已被关闭;
// 2. 窗口初始化前,在还没有调用 StartFullScreen 的前提下就调用了此方法。
// 所以,直接 return 就好。
return;
}
var hwndSource = HwndSource.FromHwnd(hwnd);
//去除hook
hwndSource.RemoveHook(KeepFullScreenHook);
//恢复保存的状态
//不要改变Style里的WS_MAXIMIZE,否则会使窗口变成最大化状态,但是尺寸不对
//也不要设置回Style里的WS_MINIMIZE,否则会导致窗口最小化按钮显示成还原按钮
InteropMethods.SetWindowLong(hwnd, InteropValues.GWL_STYLE,
(IntPtr) (style & ~(InteropValues.WindowStyles.WS_MAXIMIZE | InteropValues.WindowStyles.WS_MINIMIZE)));
if ((style & InteropValues.WindowStyles.WS_MINIMIZE) != 0)
{
//如果窗口进入全屏前是最小化的,这里不让窗口恢复到之前的最小化状态,而是到还原的状态。
//大多数情况下,都不期望在退出全屏的时候,恢复到最小化。
placement.showCmd = InteropValues.SW.RESTORE;
}
if ((style & InteropValues.WindowStyles.WS_MAXIMIZE) != 0)
{
//提前调用 ShowWindow 使窗口恢复最大化,若通过 SetWindowPlacement 最大化会导致闪烁,只靠其恢复 RestoreBounds.
InteropMethods.ShowWindow(hwnd, InteropValues.SW.MAXIMIZE);
}
InteropMethods.SetWindowPlacement(hwnd, ref placement);
if ((style & InteropValues.WindowStyles.WS_MAXIMIZE) ==
0) //如果窗口是最大化就不要修改WPF属性,否则会破坏RestoreBounds,且WPF窗口自身在最大化时,不会修改 Left Top Width Height 属性
{
if (InteropMethods.GetWindowRect(hwnd, out var rect))
{
//不能用 placement 的坐标,placement是工作区坐标,不是屏幕坐标。
//确保窗口的 WPF 属性与 Win32 位置一致
var logicalPos =
hwndSource.CompositionTarget.TransformFromDevice.Transform(
new Point(rect.Left, rect.Top));
var logicalSize =
hwndSource.CompositionTarget.TransformFromDevice.Transform(
new Point(rect.Width, rect.Height));
window.Left = logicalPos.X;
window.Top = logicalPos.Y;
window.Width = logicalSize.X;
window.Height = logicalSize.Y;
}
}
//重新启用 DWM 过渡动画 忽略返回值,若DWM关闭不做处理
InteropMethods.DwmSetWindowAttribute(hwnd, InteropValues.DwmWindowAttribute.DWMWA_TRANSITIONS_FORCEDISABLED, 0,
sizeof(int));
//删除保存的状态
window.ClearValue(BeforeFullScreenWindowPlacementProperty);
window.ClearValue(BeforeFullScreenWindowStyleProperty);
}
}
/// <summary>
/// 确保窗口全屏的Hook
/// 使用HandleProcessCorruptedStateExceptions,防止访问内存过程中因为一些致命异常导致程序崩溃
/// </summary>
[HandleProcessCorruptedStateExceptions]
private static IntPtr KeepFullScreenHook(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
//处理WM_WINDOWPOSCHANGING消息
const int WINDOWPOSCHANGING = 0x0046;
if (msg == WINDOWPOSCHANGING)
{
try
{
//得到WINDOWPOS结构体
var pos = (InteropValues.WindowPosition) Marshal.PtrToStructure(lParam, typeof(InteropValues.WindowPosition));
if ((pos.Flags & InteropValues.WindowPositionFlags.SWP_NOMOVE) != 0 &&
(pos.Flags & InteropValues.WindowPositionFlags.SWP_NOSIZE) != 0)
{
//既然你既不改变位置,也不改变尺寸,我就不管了...
return IntPtr.Zero;
}
if (InteropMethods.IsIconic(hwnd))
{
// 如果在全屏期间最小化了窗口,那么忽略后续的位置调整。
// 否则按后续逻辑,会根据窗口在 -32000 的位置,计算出错误的目标位置,然后就跳到主屏了。
return IntPtr.Zero;
}
//获取窗口现在的矩形,下面用来参考计算目标矩形
if (InteropMethods.GetWindowRect(hwnd, out var rect))
{
var targetRect = rect; //窗口想要变化的目标矩形
if ((pos.Flags & InteropValues.WindowPositionFlags.SWP_NOMOVE) == 0)
{
//需要移动
targetRect.Left = pos.X;
targetRect.Top = pos.Y;
}
if ((pos.Flags & InteropValues.WindowPositionFlags.SWP_NOSIZE) == 0)
{
//要改变尺寸
targetRect.Right = targetRect.Left + pos.Width;
targetRect.Bottom = targetRect.Top + pos.Height;
}
else
{
//不改变尺寸
targetRect.Right = targetRect.Left + rect.Width;
targetRect.Bottom = targetRect.Top + rect.Height;
}
//使用目标矩形获取显示器信息
var monitor = InteropMethods.MonitorFromRect(ref targetRect, InteropValues.MONITOR_DEFAULTTOPRIMARY);
var info = new InteropValues.MONITORINFO();
info.cbSize = (uint) Marshal.SizeOf(info);
if (InteropMethods.GetMonitorInfo(monitor, ref info))
{
//基于显示器信息设置窗口尺寸位置
pos.X = info.rcMonitor.Left;
pos.Y = info.rcMonitor.Top;
pos.Width = info.rcMonitor.Right - info.rcMonitor.Left;
pos.Height = info.rcMonitor.Bottom - info.rcMonitor.Top;
pos.Flags &= ~(InteropValues.WindowPositionFlags.SWP_NOSIZE | InteropValues.WindowPositionFlags.SWP_NOMOVE |
InteropValues.WindowPositionFlags.SWP_NOREDRAW);
pos.Flags |= InteropValues.WindowPositionFlags.SWP_NOCOPYBITS;
if (rect == info.rcMonitor)
{
var hwndSource = HwndSource.FromHwnd(hwnd);
if (hwndSource?.RootVisual is Window window)
{
//确保窗口的 WPF 属性与 Win32 位置一致,防止有逗比全屏后改 WPF 的属性,发生一些诡异的行为
//下面这样做其实不太好,会再次触发 WM_WINDOWPOSCHANGING 来着.....但是又没有其他时机了
// WM_WINDOWPOSCHANGED 不能用
//(例如:在进入全屏后,修改 Left 属性,会进入 WM_WINDOWPOSCHANGING,然后在这里将消息里的结构体中的 Left 改回,
// 使对 Left 的修改无效,那么将不会进入 WM_WINDOWPOSCHANGED,窗口尺寸正常,但窗口的 Left 属性值错误。)
var logicalPos =
hwndSource.CompositionTarget.TransformFromDevice.Transform(
new Point(pos.X, pos.Y));
var logicalSize =
hwndSource.CompositionTarget.TransformFromDevice.Transform(
new Point(pos.Width, pos.Height));
window.Left = logicalPos.X;
window.Top = logicalPos.Y;
window.Width = logicalSize.X;
window.Height = logicalSize.Y;
}
}
//将修改后的结构体拷贝回去
Marshal.StructureToPtr(pos, lParam, false);
}
}
}
catch
{
// 这里也不需要日志啥的,只是为了防止上面有逗比逻辑,在消息循环里面炸了
//确保窗口的 WPF 属性与 Win32 位置一致
var logicalPos =
hwndSource.CompositionTarget.TransformFromDevice.Transform(
new Point(rect.Left, rect.Top));
var logicalSize =
hwndSource.CompositionTarget.TransformFromDevice.Transform(
new Point(rect.Width, rect.Height));
window.Left = logicalPos.X;
window.Top = logicalPos.Y;
window.Width = logicalSize.X;
window.Height = logicalSize.Y;
}
}
return IntPtr.Zero;
//重新启用 DWM 过渡动画 忽略返回值,若DWM关闭不做处理
InteropMethods.DwmSetWindowAttribute(hwnd, InteropValues.DwmWindowAttribute.DWMWA_TRANSITIONS_FORCEDISABLED, 0,
sizeof(int));
//删除保存的状态
window.ClearValue(BeforeFullScreenWindowPlacementProperty);
window.ClearValue(BeforeFullScreenWindowStyleProperty);
}
}
}
/// <summary>
/// 确保窗口全屏的Hook
/// 使用HandleProcessCorruptedStateExceptions,防止访问内存过程中因为一些致命异常导致程序崩溃
/// </summary>
[HandleProcessCorruptedStateExceptions]
private static IntPtr KeepFullScreenHook(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
//处理WM_WINDOWPOSCHANGING消息
const int WINDOWPOSCHANGING = 0x0046;
if (msg == WINDOWPOSCHANGING)
{
try
{
//得到WINDOWPOS结构体
var pos = (InteropValues.WindowPosition) Marshal.PtrToStructure(lParam, typeof(InteropValues.WindowPosition));
if ((pos.Flags & InteropValues.WindowPositionFlags.SWP_NOMOVE) != 0 &&
(pos.Flags & InteropValues.WindowPositionFlags.SWP_NOSIZE) != 0)
{
//既然你既不改变位置,也不改变尺寸,我就不管了...
return IntPtr.Zero;
}
if (InteropMethods.IsIconic(hwnd))
{
// 如果在全屏期间最小化了窗口,那么忽略后续的位置调整。
// 否则按后续逻辑,会根据窗口在 -32000 的位置,计算出错误的目标位置,然后就跳到主屏了。
return IntPtr.Zero;
}
//获取窗口现在的矩形,下面用来参考计算目标矩形
if (InteropMethods.GetWindowRect(hwnd, out var rect))
{
var targetRect = rect; //窗口想要变化的目标矩形
if ((pos.Flags & InteropValues.WindowPositionFlags.SWP_NOMOVE) == 0)
{
//需要移动
targetRect.Left = pos.X;
targetRect.Top = pos.Y;
}
if ((pos.Flags & InteropValues.WindowPositionFlags.SWP_NOSIZE) == 0)
{
//要改变尺寸
targetRect.Right = targetRect.Left + pos.Width;
targetRect.Bottom = targetRect.Top + pos.Height;
}
else
{
//不改变尺寸
targetRect.Right = targetRect.Left + rect.Width;
targetRect.Bottom = targetRect.Top + rect.Height;
}
//使用目标矩形获取显示器信息
var monitor = InteropMethods.MonitorFromRect(ref targetRect, InteropValues.MONITOR_DEFAULTTOPRIMARY);
var info = new InteropValues.MONITORINFO();
info.cbSize = (uint) Marshal.SizeOf(info);
if (InteropMethods.GetMonitorInfo(monitor, ref info))
{
//基于显示器信息设置窗口尺寸位置
pos.X = info.rcMonitor.Left;
pos.Y = info.rcMonitor.Top;
pos.Width = info.rcMonitor.Right - info.rcMonitor.Left;
pos.Height = info.rcMonitor.Bottom - info.rcMonitor.Top;
pos.Flags &= ~(InteropValues.WindowPositionFlags.SWP_NOSIZE | InteropValues.WindowPositionFlags.SWP_NOMOVE |
InteropValues.WindowPositionFlags.SWP_NOREDRAW);
pos.Flags |= InteropValues.WindowPositionFlags.SWP_NOCOPYBITS;
if (rect == info.rcMonitor)
{
var hwndSource = HwndSource.FromHwnd(hwnd);
if (hwndSource?.RootVisual is Window window)
{
//确保窗口的 WPF 属性与 Win32 位置一致,防止有逗比全屏后改 WPF 的属性,发生一些诡异的行为
//下面这样做其实不太好,会再次触发 WM_WINDOWPOSCHANGING 来着.....但是又没有其他时机了
// WM_WINDOWPOSCHANGED 不能用
//(例如:在进入全屏后,修改 Left 属性,会进入 WM_WINDOWPOSCHANGING,然后在这里将消息里的结构体中的 Left 改回,
// 使对 Left 的修改无效,那么将不会进入 WM_WINDOWPOSCHANGED,窗口尺寸正常,但窗口的 Left 属性值错误。)
var logicalPos =
hwndSource.CompositionTarget.TransformFromDevice.Transform(
new Point(pos.X, pos.Y));
var logicalSize =
hwndSource.CompositionTarget.TransformFromDevice.Transform(
new Point(pos.Width, pos.Height));
window.Left = logicalPos.X;
window.Top = logicalPos.Y;
window.Width = logicalSize.X;
window.Height = logicalSize.Y;
}
}
//将修改后的结构体拷贝回去
Marshal.StructureToPtr(pos, lParam, false);
}
}
}
catch
{
// 这里也不需要日志啥的,只是为了防止上面有逗比逻辑,在消息循环里面炸了
}
}
return IntPtr.Zero;
}
}
@@ -8,290 +8,289 @@ using System.Windows.Media;
using System.Windows.Media.Imaging;
using HandyControl.Tools.Interop;
namespace HandyControl.Tools
namespace HandyControl.Tools;
[SuppressMessage("ReSharper", "ArrangeRedundantParentheses")]
[SuppressMessage("ReSharper", "IntVariableOverflowInUncheckedContext")]
internal static class IconHelper
{
[SuppressMessage("ReSharper", "ArrangeRedundantParentheses")]
[SuppressMessage("ReSharper", "IntVariableOverflowInUncheckedContext")]
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)
{
private static Size SmallIconSize;
EnsureSystemMetrics();
largeIconHandle = CreateIconHandleFromImageSource(image, IconSize);
smallIconHandle = CreateIconHandleFromImageSource(image, SmallIconSize);
}
private static Size IconSize;
[SecurityCritical]
public static IconHandle CreateIconHandleFromImageSource(ImageSource image, Size size)
{
EnsureSystemMetrics();
private static int SystemBitDepth;
var asGoodAsItGets = false;
[SecurityCritical, SecuritySafeCritical]
public static void GetIconHandlesFromImageSource(ImageSource image, out IconHandle largeIconHandle, out IconHandle smallIconHandle)
var bf = image as BitmapFrame;
if (bf?.Decoder?.Frames != null)
{
EnsureSystemMetrics();
largeIconHandle = CreateIconHandleFromImageSource(image, IconSize);
smallIconHandle = CreateIconHandleFromImageSource(image, SmallIconSize);
bf = GetBestMatch(bf.Decoder.Frames, size);
asGoodAsItGets = bf.Decoder is IconBitmapDecoder || bf.PixelWidth == (int) size.Width && bf.PixelHeight == (int) size.Height;
image = bf;
}
[SecurityCritical]
public static IconHandle CreateIconHandleFromImageSource(ImageSource image, Size size)
if (!asGoodAsItGets)
{
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);
bf = BitmapFrame.Create(GenerateBitmapSource(image, size));
}
[SecurityCritical]
private static IconHandle CreateIconHandleFromBitmapFrame(BitmapFrame sourceBitmapFrame)
return CreateIconHandleFromBitmapFrame(bf);
}
[SecurityCritical]
private static IconHandle CreateIconHandleFromBitmapFrame(BitmapFrame sourceBitmapFrame)
{
BitmapSource bitmapSource = sourceBitmapFrame;
if (bitmapSource.Format != PixelFormats.Bgra32 && bitmapSource.Format != PixelFormats.Pbgra32)
{
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);
bitmapSource = new FormatConvertedBitmap(bitmapSource, PixelFormats.Bgra32, null, 0.0);
}
[SecurityCritical]
internal static IconHandle CreateIconCursor(byte[] colorArray, int width, int height, int xHotspot, int yHotspot, bool isIcon)
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
{
BitmapHandle colorBitmap = null;
BitmapHandle maskBitmap = null;
try
var bi = new InteropValues.BITMAPINFO(width, -height, 32)
{
var bi = new InteropValues.BITMAPINFO(width, -height, 32)
{
biCompression = InteropValues.BI_RGB
};
biCompression = InteropValues.BI_RGB
};
var bits = IntPtr.Zero;
colorBitmap = InteropMethods.CreateDIBSection(new HandleRef(null, IntPtr.Zero), ref bi, InteropValues.DIB_RGB_COLORS, ref bits, null, 0);
var bits = IntPtr.Zero;
colorBitmap = InteropMethods.CreateDIBSection(new HandleRef(null, IntPtr.Zero), ref bi, InteropValues.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 = InteropMethods.CreateBitmap(width, height, 1, 1, maskArray);
if (maskBitmap.IsInvalid)
{
return IconHandle.GetInvalidIcon();
}
var iconInfo = new InteropValues.ICONINFO
{
fIcon = isIcon,
xHotspot = xHotspot,
yHotspot = yHotspot,
hbmMask = maskBitmap,
hbmColor = colorBitmap
};
return InteropMethods.CreateIconIndirect(iconInfo);
if (colorBitmap.IsInvalid || bits == IntPtr.Zero)
{
return IconHandle.GetInvalidIcon();
}
finally
Marshal.Copy(colorArray, 0, bits, colorArray.Length);
var maskArray = GenerateMaskArray(width, height, colorArray);
maskBitmap = InteropMethods.CreateBitmap(width, height, 1, 1, maskArray);
if (maskBitmap.IsInvalid)
{
colorBitmap?.Dispose();
maskBitmap?.Dispose();
return IconHandle.GetInvalidIcon();
}
var iconInfo = new InteropValues.ICONINFO
{
fIcon = isIcon,
xHotspot = xHotspot,
yHotspot = yHotspot,
hbmMask = maskBitmap,
hbmColor = colorBitmap
};
return InteropMethods.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;
}
}
private static byte[] GenerateMaskArray(int width, int height, byte[] colorArray)
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)
{
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;
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);
}
internal static int AlignToBytes(double original, int nBytesCount)
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 (var i = 0; i < frames.Count && bestScore != 0; ++i)
{
var nBitsCount = 8 << (nBytesCount - 1);
return ((int) Math.Ceiling(original) + (nBitsCount - 1)) / nBitsCount * nBitsCount;
}
var currentIconBitDepth = isBitmapIconDecoder ? frames[i].Thumbnail.Format.BitsPerPixel : frames[i].Format.BitsPerPixel;
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)
if (currentIconBitDepth == 0)
{
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);
currentIconBitDepth = 8;
}
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 (var i = 0; i < frames.Count && bestScore != 0; ++i)
var score = MatchImage(frames[i], size, currentIconBitDepth);
if (score < bestScore)
{
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;
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 frames[bestIndex];
}
return score;
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;
}
private static int MyAbs(int valueHave, int valueWant, bool fPunish)
{
var diff = (valueHave - valueWant);
return diff;
}
if (diff < 0)
[SecurityCritical, SecuritySafeCritical]
private static void EnsureSystemMetrics()
{
if (SystemBitDepth == 0)
{
var hdcDesktop = new HandleRef(null, InteropMethods.GetDC(new HandleRef()));
try
{
diff = (fPunish ? -2 : -1) * diff;
var sysBitDepth = InteropMethods.GetDeviceCaps(hdcDesktop, InteropValues.BITSPIXEL);
sysBitDepth *= InteropMethods.GetDeviceCaps(hdcDesktop, InteropValues.PLANES);
if (sysBitDepth == 8)
{
sysBitDepth = 4;
}
var cxSmallIcon = InteropMethods.GetSystemMetrics(InteropValues.SM.CXSMICON);
var cySmallIcon = InteropMethods.GetSystemMetrics(InteropValues.SM.CYSMICON);
var cxIcon = InteropMethods.GetSystemMetrics(InteropValues.SM.CXICON);
var cyIcon = InteropMethods.GetSystemMetrics(InteropValues.SM.CYICON);
SmallIconSize = new Size(cxSmallIcon, cySmallIcon);
IconSize = new Size(cxIcon, cyIcon);
SystemBitDepth = sysBitDepth;
}
return diff;
}
[SecurityCritical, SecuritySafeCritical]
private static void EnsureSystemMetrics()
{
if (SystemBitDepth == 0)
finally
{
var hdcDesktop = new HandleRef(null, InteropMethods.GetDC(new HandleRef()));
try
{
var sysBitDepth = InteropMethods.GetDeviceCaps(hdcDesktop, InteropValues.BITSPIXEL);
sysBitDepth *= InteropMethods.GetDeviceCaps(hdcDesktop, InteropValues.PLANES);
if (sysBitDepth == 8)
{
sysBitDepth = 4;
}
var cxSmallIcon = InteropMethods.GetSystemMetrics(InteropValues.SM.CXSMICON);
var cySmallIcon = InteropMethods.GetSystemMetrics(InteropValues.SM.CYSMICON);
var cxIcon = InteropMethods.GetSystemMetrics(InteropValues.SM.CXICON);
var cyIcon = InteropMethods.GetSystemMetrics(InteropValues.SM.CYICON);
SmallIconSize = new Size(cxSmallIcon, cySmallIcon);
IconSize = new Size(cxIcon, cyIcon);
SystemBitDepth = sysBitDepth;
}
finally
{
InteropMethods.ReleaseDC(new HandleRef(), hdcDesktop);
}
InteropMethods.ReleaseDC(new HandleRef(), hdcDesktop);
}
}
[SecurityCritical, SecuritySafeCritical]
public static void GetDefaultIconHandles(out IconHandle largeIconHandle, out IconHandle smallIconHandle)
{
largeIconHandle = null;
smallIconHandle = null;
SecurityHelper.DemandUIWindowPermission();
var iconModuleFile = InteropMethods.GetModuleFileName(new HandleRef());
InteropMethods.ExtractIconEx(iconModuleFile, 0, out largeIconHandle, out smallIconHandle, 1);
}
}
}
[SecurityCritical, SecuritySafeCritical]
public static void GetDefaultIconHandles(out IconHandle largeIconHandle, out IconHandle smallIconHandle)
{
largeIconHandle = null;
smallIconHandle = null;
SecurityHelper.DemandUIWindowPermission();
var iconModuleFile = InteropMethods.GetModuleFileName(new HandleRef());
InteropMethods.ExtractIconEx(iconModuleFile, 0, out largeIconHandle, out smallIconHandle, 1);
}
}
@@ -2,148 +2,147 @@
using System.Windows;
using System.Windows.Input;
namespace HandyControl.Tools
namespace HandyControl.Tools;
/// <summary>
/// 输入层点击帮助类
/// </summary>
public static class InputClickHelper
{
private static readonly DependencyProperty InputInfoProperty = DependencyProperty.RegisterAttached(
"InputInfo", typeof(InputInfo), typeof(InputClickHelper), new PropertyMetadata(default(InputInfo)));
private static void SetInputInfo(DependencyObject element, InputInfo value) => element.SetValue(InputInfoProperty, value);
private static InputInfo GetInputInfo(DependencyObject element) => (InputInfo) element.GetValue(InputInfoProperty);
/// <summary>
/// 输入层点击帮助类
/// 将 MouseDown MouseMove MouseUp 封装为点击事件
/// </summary>
public static class InputClickHelper
/// <param name="element">要被附加的元素</param>
/// <param name="clickEventHandler">点击的事件</param>
/// <param name="dragStarted">因为拖动而结束点击时触发</param>
public static void AttachMouseDownMoveUpToClick(UIElement element, EventHandler clickEventHandler,
EventHandler dragStarted = null)
{
private static readonly DependencyProperty InputInfoProperty = DependencyProperty.RegisterAttached(
"InputInfo", typeof(InputInfo), typeof(InputClickHelper), new PropertyMetadata(default(InputInfo)));
var inputInfo = GetOrCreateInputInfo(element);
inputInfo.ClickEventHandler += clickEventHandler;
private static void SetInputInfo(DependencyObject element, InputInfo value) => element.SetValue(InputInfoProperty, value);
inputInfo.DragStarted += dragStarted;
private static InputInfo GetInputInfo(DependencyObject element) => (InputInfo) element.GetValue(InputInfoProperty);
element.MouseDown -= Element_MouseDown;
element.MouseDown += Element_MouseDown;
element.MouseMove -= Element_MouseMove;
element.MouseMove += Element_MouseMove;
element.MouseUp -= Element_MouseUp;
element.MouseUp += Element_MouseUp;
element.LostMouseCapture -= Element_LostMouseCapture;
element.LostMouseCapture += Element_LostMouseCapture;
}
/// <summary>
/// 将 MouseDown MouseMove MouseUp 封装为点击事件
/// </summary>
/// <param name="element">要被附加的元素</param>
/// <param name="clickEventHandler">点击的事件</param>
/// <param name="dragStarted">因为拖动而结束点击时触发</param>
public static void AttachMouseDownMoveUpToClick(UIElement element, EventHandler clickEventHandler,
EventHandler dragStarted = null)
/// <summary>
/// 去掉对 <paramref name="element" /> 的点击时间的监听
/// </summary>
/// <param name="element"></param>
/// <param name="clickEventHandler">点击的事件</param>
/// <param name="dragStarted">因为拖动而结束点击时触发的事件</param>
public static void DetachMouseDownMoveUpToClick(UIElement element, EventHandler clickEventHandler,
EventHandler dragStarted = null)
{
var inputInfo = GetInputInfo(element);
if (inputInfo == null) return;
inputInfo.ClickEventHandler -= clickEventHandler;
inputInfo.DragStarted -= dragStarted;
if (inputInfo.IsEmpty())
{
var inputInfo = GetOrCreateInputInfo(element);
inputInfo.ClickEventHandler += clickEventHandler;
inputInfo.DragStarted += dragStarted;
element.ClearValue(InputInfoProperty);
element.MouseDown -= Element_MouseDown;
element.MouseDown += Element_MouseDown;
element.MouseMove -= Element_MouseMove;
element.MouseMove += Element_MouseMove;
element.MouseUp -= Element_MouseUp;
element.MouseUp += Element_MouseUp;
element.LostMouseCapture -= Element_LostMouseCapture;
element.LostMouseCapture += Element_LostMouseCapture;
}
/// <summary>
/// 去掉对 <paramref name="element" /> 的点击时间的监听
/// </summary>
/// <param name="element"></param>
/// <param name="clickEventHandler">点击的事件</param>
/// <param name="dragStarted">因为拖动而结束点击时触发的事件</param>
public static void DetachMouseDownMoveUpToClick(UIElement element, EventHandler clickEventHandler,
EventHandler dragStarted = null)
{
var inputInfo = GetInputInfo(element);
if (inputInfo == null) return;
inputInfo.ClickEventHandler -= clickEventHandler;
inputInfo.DragStarted -= dragStarted;
if (inputInfo.IsEmpty())
{
element.ClearValue(InputInfoProperty);
element.MouseDown -= Element_MouseDown;
element.MouseMove -= Element_MouseMove;
element.MouseUp -= Element_MouseUp;
element.LostMouseCapture -= Element_LostMouseCapture;
}
}
private static void Element_LostMouseCapture(object sender, MouseEventArgs e)
{
var element = (UIElement) sender;
GetInputInfo(element)?.LostCapture();
}
private static void Element_MouseUp(object sender, MouseButtonEventArgs e)
{
var element = (UIElement) sender;
GetInputInfo(element)?.Up(e.GetPosition(element));
}
private static void Element_MouseMove(object sender, MouseEventArgs e)
{
var element = (UIElement) sender;
GetInputInfo(element)?.Move(e.GetPosition(element));
}
private static void Element_MouseDown(object sender, MouseButtonEventArgs e)
{
var element = (UIElement) sender;
GetInputInfo(element)?.Down(e.GetPosition(element));
}
private static InputInfo GetOrCreateInputInfo(UIElement element)
{
var inputInfo = GetInputInfo(element);
if (inputInfo == null)
{
inputInfo = new InputInfo();
SetInputInfo(element, inputInfo);
}
return inputInfo;
}
private class InputInfo
{
private const double ToleranceSquared = 0.01;
private Point _downedPosition;
private bool _isClick;
public event EventHandler ClickEventHandler;
public event EventHandler DragStarted;
public void Down(Point position)
{
_downedPosition = position;
_isClick = true;
}
public void Move(Point position)
{
if (!_isClick) return;
if ((position - _downedPosition).LengthSquared > ToleranceSquared)
{
_isClick = false;
DragStarted?.Invoke(null, EventArgs.Empty);
}
}
public void Up(Point position)
{
_isClick = _isClick && (position - _downedPosition).LengthSquared <= ToleranceSquared;
if (!_isClick) return;
ClickEventHandler?.Invoke(null, EventArgs.Empty);
_isClick = false;
}
public void LostCapture() => _isClick = false;
public bool IsEmpty() => ClickEventHandler is null && DragStarted is null;
}
}
}
private static void Element_LostMouseCapture(object sender, MouseEventArgs e)
{
var element = (UIElement) sender;
GetInputInfo(element)?.LostCapture();
}
private static void Element_MouseUp(object sender, MouseButtonEventArgs e)
{
var element = (UIElement) sender;
GetInputInfo(element)?.Up(e.GetPosition(element));
}
private static void Element_MouseMove(object sender, MouseEventArgs e)
{
var element = (UIElement) sender;
GetInputInfo(element)?.Move(e.GetPosition(element));
}
private static void Element_MouseDown(object sender, MouseButtonEventArgs e)
{
var element = (UIElement) sender;
GetInputInfo(element)?.Down(e.GetPosition(element));
}
private static InputInfo GetOrCreateInputInfo(UIElement element)
{
var inputInfo = GetInputInfo(element);
if (inputInfo == null)
{
inputInfo = new InputInfo();
SetInputInfo(element, inputInfo);
}
return inputInfo;
}
private class InputInfo
{
private const double ToleranceSquared = 0.01;
private Point _downedPosition;
private bool _isClick;
public event EventHandler ClickEventHandler;
public event EventHandler DragStarted;
public void Down(Point position)
{
_downedPosition = position;
_isClick = true;
}
public void Move(Point position)
{
if (!_isClick) return;
if ((position - _downedPosition).LengthSquared > ToleranceSquared)
{
_isClick = false;
DragStarted?.Invoke(null, EventArgs.Empty);
}
}
public void Up(Point position)
{
_isClick = _isClick && (position - _downedPosition).LengthSquared <= ToleranceSquared;
if (!_isClick) return;
ClickEventHandler?.Invoke(null, EventArgs.Empty);
_isClick = false;
}
public void LostCapture() => _isClick = false;
public bool IsEmpty() => ClickEventHandler is null && DragStarted is null;
}
}
@@ -3,83 +3,82 @@ using System.Reflection;
using System.Windows;
using HandyControl.Data;
namespace HandyControl.Tools
namespace HandyControl.Tools;
/// <summary>
/// 资源帮助类
/// </summary>
public class ResourceHelper
{
private static ResourceDictionary _theme;
/// <summary>
/// 资源帮助类
/// 获取资源
/// </summary>
public class ResourceHelper
/// <param name="key"></param>
/// <returns></returns>
public static T GetResource<T>(string key)
{
private static ResourceDictionary _theme;
/// <summary>
/// 获取资源
/// </summary>
/// <param name="key"></param>
/// <returns></returns>
public static T GetResource<T>(string key)
if (Application.Current.TryFindResource(key) is T resource)
{
if (Application.Current.TryFindResource(key) is T resource)
{
return resource;
}
return default;
return resource;
}
internal static T GetResourceInternal<T>(string key)
{
if (GetTheme()[key] is T resource)
{
return resource;
}
return default;
}
return default;
internal static T GetResourceInternal<T>(string key)
{
if (GetTheme()[key] is T resource)
{
return resource;
}
/// <summary>
/// 获取皮肤
/// </summary>
public static ResourceDictionary GetSkin(Assembly assembly, string themePath, SkinType skin)
return default;
}
/// <summary>
/// 获取皮肤
/// </summary>
public static ResourceDictionary GetSkin(Assembly assembly, string themePath, SkinType skin)
{
try
{
try
var uri = new Uri($"pack://application:,,,/{assembly.GetName().Name};component/{themePath}/Skin{skin}.xaml");
return new ResourceDictionary
{
var uri = new Uri($"pack://application:,,,/{assembly.GetName().Name};component/{themePath}/Skin{skin}.xaml");
return new ResourceDictionary
{
Source = uri
};
}
catch
{
return new ResourceDictionary
{
Source = new Uri($"pack://application:,,,/{assembly.GetName().Name};component/{themePath}/Skin{SkinType.Default}.xaml")
};
}
Source = uri
};
}
/// <summary>
/// get HandyControl skin
/// </summary>
/// <param name="skin"></param>
/// <returns></returns>
public static ResourceDictionary GetSkin(SkinType skin) => new()
catch
{
Source = new Uri($"pack://application:,,,/HandyControl;component/Themes/Skin{skin}.xaml")
};
/// <summary>
/// get HandyControl theme
/// </summary>
public static ResourceDictionary GetTheme() => _theme ??= GetStandaloneTheme();
public static ResourceDictionary GetStandaloneTheme()
{
return new()
return new ResourceDictionary
{
Source = new Uri("pack://application:,,,/HandyControl;component/Themes/Theme.xaml")
Source = new Uri($"pack://application:,,,/{assembly.GetName().Name};component/{themePath}/Skin{SkinType.Default}.xaml")
};
}
}
}
/// <summary>
/// get HandyControl skin
/// </summary>
/// <param name="skin"></param>
/// <returns></returns>
public static ResourceDictionary GetSkin(SkinType skin) => new()
{
Source = new Uri($"pack://application:,,,/HandyControl;component/Themes/Skin{skin}.xaml")
};
/// <summary>
/// get HandyControl theme
/// </summary>
public static ResourceDictionary GetTheme() => _theme ??= GetStandaloneTheme();
public static ResourceDictionary GetStandaloneTheme()
{
return new()
{
Source = new Uri("pack://application:,,,/HandyControl;component/Themes/Theme.xaml")
};
}
}
@@ -4,82 +4,81 @@ using System.Runtime.InteropServices;
using System.Windows;
using HandyControl.Tools.Interop;
namespace HandyControl.Tools
namespace HandyControl.Tools;
internal class ScreenHelper
{
internal class ScreenHelper
internal static void FindMaximumSingleMonitorRectangle(Rect windowRect, out Rect screenSubRect, out Rect monitorRect)
{
internal static void FindMaximumSingleMonitorRectangle(Rect windowRect, out Rect screenSubRect, out Rect monitorRect)
{
var windowRect2 = new InteropValues.RECT(windowRect);
FindMaximumSingleMonitorRectangle(windowRect2, out var rect, out var rect2);
screenSubRect = new Rect(rect.Position, rect.Size);
monitorRect = new Rect(rect2.Position, rect2.Size);
}
var windowRect2 = new InteropValues.RECT(windowRect);
FindMaximumSingleMonitorRectangle(windowRect2, out var rect, out var rect2);
screenSubRect = new Rect(rect.Position, rect.Size);
monitorRect = new Rect(rect2.Position, rect2.Size);
}
private static void FindMaximumSingleMonitorRectangle(InteropValues.RECT windowRect, out InteropValues.RECT screenSubRect, out InteropValues.RECT monitorRect)
{
var rects = new List<InteropValues.RECT>();
InteropMethods.EnumDisplayMonitors(IntPtr.Zero, IntPtr.Zero,
delegate (IntPtr hMonitor, IntPtr hdcMonitor, ref InteropValues.RECT rect, IntPtr lpData)
{
var monitorInfo = default(InteropValues.MONITORINFO);
monitorInfo.cbSize = (uint) Marshal.SizeOf(typeof(InteropValues.MONITORINFO));
InteropMethods.GetMonitorInfo(hMonitor, ref monitorInfo);
rects.Add(monitorInfo.rcWork);
return true;
}, IntPtr.Zero);
var num = 0L;
screenSubRect = new InteropValues.RECT
private static void FindMaximumSingleMonitorRectangle(InteropValues.RECT windowRect, out InteropValues.RECT screenSubRect, out InteropValues.RECT monitorRect)
{
var rects = new List<InteropValues.RECT>();
InteropMethods.EnumDisplayMonitors(IntPtr.Zero, IntPtr.Zero,
delegate (IntPtr hMonitor, IntPtr hdcMonitor, ref InteropValues.RECT rect, IntPtr lpData)
{
Left = 0,
Right = 0,
Top = 0,
Bottom = 0
};
monitorRect = new InteropValues.RECT
{
Left = 0,
Right = 0,
Top = 0,
Bottom = 0
};
foreach (var current in rects)
{
var rect = current;
InteropMethods.IntersectRect(out var rECT2, ref rect, ref windowRect);
var num2 = (long) (rECT2.Width * rECT2.Height);
if (num2 > num)
{
screenSubRect = rECT2;
monitorRect = current;
num = num2;
}
}
}
internal static void FindMonitorRectsFromPoint(Point point, out Rect monitorRect, out Rect workAreaRect)
{
var intPtr = InteropMethods.MonitorFromPoint(new InteropValues.POINT
{
X = (int) point.X,
Y = (int) point.Y
}, 2);
monitorRect = new Rect(0.0, 0.0, 0.0, 0.0);
workAreaRect = new Rect(0.0, 0.0, 0.0, 0.0);
if (intPtr != IntPtr.Zero)
{
InteropValues.MONITORINFO monitorInfo = default;
var monitorInfo = default(InteropValues.MONITORINFO);
monitorInfo.cbSize = (uint) Marshal.SizeOf(typeof(InteropValues.MONITORINFO));
InteropMethods.GetMonitorInfo(intPtr, ref monitorInfo);
monitorRect = new Rect(monitorInfo.rcMonitor.Position, monitorInfo.rcMonitor.Size);
workAreaRect = new Rect(monitorInfo.rcWork.Position, monitorInfo.rcWork.Size);
InteropMethods.GetMonitorInfo(hMonitor, ref monitorInfo);
rects.Add(monitorInfo.rcWork);
return true;
}, IntPtr.Zero);
var num = 0L;
screenSubRect = new InteropValues.RECT
{
Left = 0,
Right = 0,
Top = 0,
Bottom = 0
};
monitorRect = new InteropValues.RECT
{
Left = 0,
Right = 0,
Top = 0,
Bottom = 0
};
foreach (var current in rects)
{
var rect = current;
InteropMethods.IntersectRect(out var rECT2, ref rect, ref windowRect);
var num2 = (long) (rECT2.Width * rECT2.Height);
if (num2 > num)
{
screenSubRect = rECT2;
monitorRect = current;
num = num2;
}
}
}
}
internal static void FindMonitorRectsFromPoint(Point point, out Rect monitorRect, out Rect workAreaRect)
{
var intPtr = InteropMethods.MonitorFromPoint(new InteropValues.POINT
{
X = (int) point.X,
Y = (int) point.Y
}, 2);
monitorRect = new Rect(0.0, 0.0, 0.0, 0.0);
workAreaRect = new Rect(0.0, 0.0, 0.0, 0.0);
if (intPtr != IntPtr.Zero)
{
InteropValues.MONITORINFO monitorInfo = default;
monitorInfo.cbSize = (uint) Marshal.SizeOf(typeof(InteropValues.MONITORINFO));
InteropMethods.GetMonitorInfo(intPtr, ref monitorInfo);
monitorRect = new Rect(monitorInfo.rcMonitor.Position, monitorInfo.rcMonitor.Size);
workAreaRect = new Rect(monitorInfo.rcWork.Position, monitorInfo.rcWork.Size);
}
}
}
@@ -2,18 +2,17 @@
using System.Security;
using System.Security.Permissions;
namespace HandyControl.Tools
{
[SuppressMessage("ReSharper", "InconsistentNaming")]
internal class SecurityHelper
{
private static UIPermission _allWindowsUIPermission;
namespace HandyControl.Tools;
[SecurityCritical]
internal static void DemandUIWindowPermission()
{
_allWindowsUIPermission ??= new UIPermission(UIPermissionWindow.AllWindows);
_allWindowsUIPermission.Demand();
}
[SuppressMessage("ReSharper", "InconsistentNaming")]
internal class SecurityHelper
{
private static UIPermission _allWindowsUIPermission;
[SecurityCritical]
internal static void DemandUIWindowPermission()
{
_allWindowsUIPermission ??= new UIPermission(UIPermissionWindow.AllWindows);
_allWindowsUIPermission.Demand();
}
}
}
@@ -3,40 +3,39 @@ using System.Linq;
using System.Windows.Media;
using HandyControl.Controls;
namespace HandyControl.Tools
namespace HandyControl.Tools;
/// <summary>
/// 该类可以为可视化元素提供单开的功能
/// </summary>
public class SingleOpenHelper
{
private static readonly Dictionary<string, ISingleOpen> OpenDic = new();
/// <summary>
/// 该类可以为可视化元素提供单开的功能
/// 根据指定的类型创建实例
/// </summary>
public class SingleOpenHelper
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public static T CreateControl<T>() where T : Visual, ISingleOpen, new()
{
private static readonly Dictionary<string, ISingleOpen> OpenDic = new();
var typeStr = typeof(T).FullName;
/// <summary>
/// 根据指定的类型创建实例
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public static T CreateControl<T>() where T : Visual, ISingleOpen, new()
if (string.IsNullOrEmpty(typeStr)) return default;
var temp = new T();
if (!OpenDic.Keys.Contains(typeStr))
{
var typeStr = typeof(T).FullName;
if (string.IsNullOrEmpty(typeStr)) return default;
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;
OpenDic.Add(typeStr, temp);
return temp;
}
var currentCtl = OpenDic[typeStr];
if (currentCtl.CanDispose)
{
currentCtl.Dispose();
OpenDic[typeStr] = temp;
return temp;
}
return default;
}
}
}
@@ -3,27 +3,26 @@ using HandyControl.Data;
using HandyControl.Tools.Interop;
using Microsoft.Win32;
namespace HandyControl.Tools.Helper
namespace HandyControl.Tools.Helper;
internal class SystemHelper
{
internal class SystemHelper
public static SystemVersionInfo GetSystemVersionInfo()
{
public static SystemVersionInfo GetSystemVersionInfo()
{
var osv = new InteropValues.RTL_OSVERSIONINFOEX();
osv.dwOSVersionInfoSize = (uint) Marshal.SizeOf(osv);
InteropMethods.Gdip.RtlGetVersion(out osv);
return new SystemVersionInfo((int) osv.dwMajorVersion, (int) osv.dwMinorVersion, (int) osv.dwBuildNumber);
}
private const string SkinTypeRegistryKeyName = @"HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Themes\Personalize";
private const string SkinTypeRegistryValueName = "AppsUseLightTheme";
public static bool DetermineIfInLightThemeMode()
{
var value = Registry.GetValue(SkinTypeRegistryKeyName, SkinTypeRegistryValueName, "0");
return value != null && value.ToString() != "0";
}
var osv = new InteropValues.RTL_OSVERSIONINFOEX();
osv.dwOSVersionInfoSize = (uint) Marshal.SizeOf(osv);
InteropMethods.Gdip.RtlGetVersion(out osv);
return new SystemVersionInfo((int) osv.dwMajorVersion, (int) osv.dwMinorVersion, (int) osv.dwBuildNumber);
}
}
private const string SkinTypeRegistryKeyName = @"HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Themes\Personalize";
private const string SkinTypeRegistryValueName = "AppsUseLightTheme";
public static bool DetermineIfInLightThemeMode()
{
var value = Registry.GetValue(SkinTypeRegistryKeyName, SkinTypeRegistryValueName, "0");
return value != null && value.ToString() != "0";
}
}
@@ -2,19 +2,19 @@
using System.Windows;
using System.Windows.Media;
namespace HandyControl.Tools.Helper
namespace HandyControl.Tools.Helper;
internal class TextHelper
{
internal class TextHelper
public static FormattedText CreateFormattedText(string text, FlowDirection flowDirection, Typeface typeface, double fontSize)
{
public static FormattedText CreateFormattedText(string text, FlowDirection flowDirection, Typeface typeface, double fontSize)
{
#if NET40 || NET45 || NET451 || NET452 || NET46 || NET461
var formattedText = new FormattedText(
text,
CultureInfo.CurrentUICulture,
flowDirection,
typeface,
fontSize, Brushes.Black);
var formattedText = new FormattedText(
text,
CultureInfo.CurrentUICulture,
flowDirection,
typeface,
fontSize, Brushes.Black);
#else
var formattedText = new FormattedText(
text,
@@ -24,7 +24,6 @@ namespace HandyControl.Tools.Helper
fontSize, Brushes.Black, DpiHelper.DeviceDpiX);
#endif
return formattedText;
}
return formattedText;
}
}
}
@@ -16,218 +16,217 @@
using System;
using System.Globalization;
namespace HandyControl.Tools
namespace HandyControl.Tools;
internal class TokenizerHelper
{
internal class TokenizerHelper
private char _quoteChar;
private char _argSeparator;
private string _str;
private int _strLen;
private int _charIndex;
private int _currentTokenIndex;
private int _currentTokenLength;
public bool FoundSeparator { get; private set; }
public TokenizerHelper(string str, IFormatProvider formatProvider)
{
private char _quoteChar;
var numberSeparator = GetNumericListSeparator(formatProvider);
Initialize(str, '\'', numberSeparator);
}
private char _argSeparator;
private void Initialize(string str, char quoteChar, char separator)
{
_str = str;
_strLen = str?.Length ?? 0;
_currentTokenIndex = -1;
_quoteChar = quoteChar;
_argSeparator = separator;
private string _str;
private int _strLen;
private int _charIndex;
private int _currentTokenIndex;
private int _currentTokenLength;
public bool FoundSeparator { get; private set; }
public TokenizerHelper(string str, IFormatProvider formatProvider)
// immediately forward past any whitespace so
// NextToken() logic always starts on the first
// character of the next token.
while (_charIndex < _strLen)
{
var numberSeparator = GetNumericListSeparator(formatProvider);
Initialize(str, '\'', numberSeparator);
if (!char.IsWhiteSpace(_str, _charIndex))
{
break;
}
++_charIndex;
}
}
public string GetCurrentToken() =>
_currentTokenIndex < 0 ? null : _str.Substring(_currentTokenIndex, _currentTokenLength);
internal bool NextToken() => NextToken(false);
public bool NextToken(bool allowQuotedToken) => NextToken(allowQuotedToken, _argSeparator);
public bool NextToken(bool allowQuotedToken, char separator)
{
_currentTokenIndex = -1; // reset the currentTokenIndex
FoundSeparator = false; // reset
// If we're at end of the string, just return false.
if (_charIndex >= _strLen)
{
return false;
}
private void Initialize(string str, char quoteChar, char separator)
var currentChar = _str[_charIndex];
// setup the quoteCount
var quoteCount = 0;
// If we are allowing a quoted token and this token begins with a quote,
// set up the quote count and skip the initial quote
if (allowQuotedToken &&
currentChar == _quoteChar)
{
_str = str;
_strLen = str?.Length ?? 0;
_currentTokenIndex = -1;
_quoteChar = quoteChar;
_argSeparator = separator;
// immediately forward past any whitespace so
// NextToken() logic always starts on the first
// character of the next token.
while (_charIndex < _strLen)
{
if (!char.IsWhiteSpace(_str, _charIndex))
{
break;
}
++_charIndex;
}
quoteCount++; // increment quote count
++_charIndex; // move to next character
}
public string GetCurrentToken() =>
_currentTokenIndex < 0 ? null : _str.Substring(_currentTokenIndex, _currentTokenLength);
var newTokenIndex = _charIndex;
var newTokenLength = 0;
internal bool NextToken() => NextToken(false);
public bool NextToken(bool allowQuotedToken) => NextToken(allowQuotedToken, _argSeparator);
public bool NextToken(bool allowQuotedToken, char separator)
// loop until hit end of string or hit a , or whitespace
// if at end of string ust return false.
while (_charIndex < _strLen)
{
_currentTokenIndex = -1; // reset the currentTokenIndex
FoundSeparator = false; // reset
currentChar = _str[_charIndex];
// If we're at end of the string, just return false.
if (_charIndex >= _strLen)
{
return false;
}
var currentChar = _str[_charIndex];
// setup the quoteCount
var quoteCount = 0;
// If we are allowing a quoted token and this token begins with a quote,
// set up the quote count and skip the initial quote
if (allowQuotedToken &&
currentChar == _quoteChar)
{
quoteCount++; // increment quote count
++_charIndex; // move to next character
}
var newTokenIndex = _charIndex;
var newTokenLength = 0;
// loop until hit end of string or hit a , or whitespace
// if at end of string ust return false.
while (_charIndex < _strLen)
{
currentChar = _str[_charIndex];
// if have a QuoteCount and this is a quote
// decrement the quoteCount
if (quoteCount > 0)
{
// if anything but a quoteChar we move on
if (currentChar == _quoteChar)
{
--quoteCount;
// if at zero which it always should for now
// break out of the loop
if (0 == quoteCount)
{
++_charIndex; // move past the quote
break;
}
}
}
else if (char.IsWhiteSpace(currentChar) || currentChar == separator)
{
if (currentChar == separator)
{
FoundSeparator = true;
}
break;
}
++_charIndex;
++newTokenLength;
}
// if quoteCount isn't zero we hit the end of the string
// before the ending quote
// if have a QuoteCount and this is a quote
// decrement the quoteCount
if (quoteCount > 0)
{
throw new InvalidOperationException("TokenizerHelperMissingEndQuote");
// if anything but a quoteChar we move on
if (currentChar == _quoteChar)
{
--quoteCount;
// if at zero which it always should for now
// break out of the loop
if (0 == quoteCount)
{
++_charIndex; // move past the quote
break;
}
}
}
ScanToNextToken(separator); // move so at the start of the nextToken for next call
// finally made it, update the _currentToken values
_currentTokenIndex = newTokenIndex;
_currentTokenLength = newTokenLength;
if (_currentTokenLength < 1)
else if (char.IsWhiteSpace(currentChar) || currentChar == separator)
{
throw new InvalidOperationException("TokenizerHelperEmptyToken");
}
return true;
}
private void ScanToNextToken(char separator)
{
// if already at end of the string don't bother
if (_charIndex >= _strLen) return;
var currentChar = _str[_charIndex];
// check that the currentChar is a space or the separator. If not
// we have an error. this can happen in the quote case
// that the char after the quotes string isn't a char.
if (currentChar != separator && !char.IsWhiteSpace(currentChar))
{
throw new InvalidOperationException("TokenizerHelperExtraDataEncountered");
}
// loop until hit a character that isn't
// an argument separator or whitespace.
// !!!Todo: if more than one argSet throw an exception
var argSepCount = 0;
while (_charIndex < _strLen)
{
currentChar = _str[_charIndex];
if (currentChar == separator)
{
FoundSeparator = true;
++argSepCount;
_charIndex++;
if (argSepCount > 1)
{
throw new InvalidOperationException("TokenizerHelperEmptyToken");
}
}
else if (char.IsWhiteSpace(currentChar))
{
++_charIndex;
}
else
{
break;
}
break;
}
// if there was a separatorChar then we shouldn't be
// at the end of string or means there was a separator
// but there isn't an arg
++_charIndex;
++newTokenLength;
}
if (argSepCount > 0 && _charIndex >= _strLen)
// if quoteCount isn't zero we hit the end of the string
// before the ending quote
if (quoteCount > 0)
{
throw new InvalidOperationException("TokenizerHelperMissingEndQuote");
}
ScanToNextToken(separator); // move so at the start of the nextToken for next call
// finally made it, update the _currentToken values
_currentTokenIndex = newTokenIndex;
_currentTokenLength = newTokenLength;
if (_currentTokenLength < 1)
{
throw new InvalidOperationException("TokenizerHelperEmptyToken");
}
return true;
}
private void ScanToNextToken(char separator)
{
// if already at end of the string don't bother
if (_charIndex >= _strLen) return;
var currentChar = _str[_charIndex];
// check that the currentChar is a space or the separator. If not
// we have an error. this can happen in the quote case
// that the char after the quotes string isn't a char.
if (currentChar != separator && !char.IsWhiteSpace(currentChar))
{
throw new InvalidOperationException("TokenizerHelperExtraDataEncountered");
}
// loop until hit a character that isn't
// an argument separator or whitespace.
// !!!Todo: if more than one argSet throw an exception
var argSepCount = 0;
while (_charIndex < _strLen)
{
currentChar = _str[_charIndex];
if (currentChar == separator)
{
throw new InvalidOperationException("TokenizerHelperEmptyToken");
FoundSeparator = true;
++argSepCount;
_charIndex++;
if (argSepCount > 1)
{
throw new InvalidOperationException("TokenizerHelperEmptyToken");
}
}
else if (char.IsWhiteSpace(currentChar))
{
++_charIndex;
}
else
{
break;
}
}
internal static char GetNumericListSeparator(IFormatProvider provider)
// if there was a separatorChar then we shouldn't be
// at the end of string or means there was a separator
// but there isn't an arg
if (argSepCount > 0 && _charIndex >= _strLen)
{
var numericSeparator = ',';
// Get the NumberFormatInfo out of the provider, if possible
// If the IFormatProvider doesn't not contain a NumberFormatInfo, then
// this method returns the current culture's NumberFormatInfo.
var numberFormat = NumberFormatInfo.GetInstance(provider);
// Is the decimal separator is the same as the list separator?
// If so, we use the ";".
if (numberFormat.NumberDecimalSeparator.Length > 0 && numericSeparator == numberFormat.NumberDecimalSeparator[0])
{
numericSeparator = ';';
}
return numericSeparator;
throw new InvalidOperationException("TokenizerHelperEmptyToken");
}
}
}
internal static char GetNumericListSeparator(IFormatProvider provider)
{
var numericSeparator = ',';
// Get the NumberFormatInfo out of the provider, if possible
// If the IFormatProvider doesn't not contain a NumberFormatInfo, then
// this method returns the current culture's NumberFormatInfo.
var numberFormat = NumberFormatInfo.GetInstance(provider);
// Is the decimal separator is the same as the list separator?
// If so, we use the ";".
if (numberFormat.NumberDecimalSeparator.Length > 0 && numericSeparator == numberFormat.NumberDecimalSeparator[0])
{
numericSeparator = ';';
}
return numericSeparator;
}
}
@@ -6,68 +6,67 @@ using System.Windows.Input;
using System.Windows.Interop;
using HandyControl.Tools.Interop;
namespace HandyControl.Tools.Helper
namespace HandyControl.Tools.Helper;
internal class TouchDragMoveWindowHelper
{
internal class TouchDragMoveWindowHelper
private const int MaxMoveSpeed = 60;
public TouchDragMoveWindowHelper(Window window)
{
private const int MaxMoveSpeed = 60;
public TouchDragMoveWindowHelper(Window window)
{
_window = window;
}
public void Start()
{
var window = _window;
window.PreviewMouseMove += Window_PreviewMouseMove;
window.PreviewMouseUp += Window_PreviewMouseUp;
window.LostMouseCapture += Window_LostMouseCapture;
}
public void Stop()
{
var window = _window;
window.PreviewMouseMove -= Window_PreviewMouseMove;
window.PreviewMouseUp -= Window_PreviewMouseUp;
window.LostMouseCapture -= Window_LostMouseCapture;
}
private readonly Window _window;
private InteropValues.POINT? _lastPoint;
private void Window_LostMouseCapture(object sender, MouseEventArgs e) => Stop();
private void Window_PreviewMouseUp(object sender, MouseButtonEventArgs e) => Stop();
private void Window_PreviewMouseMove(object sender, MouseEventArgs e)
{
InteropMethods.GetCursorPos(out var lpPoint);
if (_lastPoint == null)
{
_lastPoint = lpPoint;
_window.CaptureMouse();
}
var dx = lpPoint.X - _lastPoint.Value.X;
var dy = lpPoint.Y - _lastPoint.Value.Y;
if (Math.Abs(dx) < MaxMoveSpeed && Math.Abs(dy) < MaxMoveSpeed)
{
var handle = new WindowInteropHelper(_window).Handle;
InteropMethods.GetWindowRect(handle, out var lpRect);
InteropMethods.SetWindowPos(handle, IntPtr.Zero, lpRect.Left + dx, lpRect.Top + dy, 0, 0,
(int) (InteropValues.WindowPositionFlags.SWP_NOSIZE |
InteropValues.WindowPositionFlags.SWP_NOZORDER));
}
_lastPoint = lpPoint;
}
_window = window;
}
}
public void Start()
{
var window = _window;
window.PreviewMouseMove += Window_PreviewMouseMove;
window.PreviewMouseUp += Window_PreviewMouseUp;
window.LostMouseCapture += Window_LostMouseCapture;
}
public void Stop()
{
var window = _window;
window.PreviewMouseMove -= Window_PreviewMouseMove;
window.PreviewMouseUp -= Window_PreviewMouseUp;
window.LostMouseCapture -= Window_LostMouseCapture;
}
private readonly Window _window;
private InteropValues.POINT? _lastPoint;
private void Window_LostMouseCapture(object sender, MouseEventArgs e) => Stop();
private void Window_PreviewMouseUp(object sender, MouseButtonEventArgs e) => Stop();
private void Window_PreviewMouseMove(object sender, MouseEventArgs e)
{
InteropMethods.GetCursorPos(out var lpPoint);
if (_lastPoint == null)
{
_lastPoint = lpPoint;
_window.CaptureMouse();
}
var dx = lpPoint.X - _lastPoint.Value.X;
var dy = lpPoint.Y - _lastPoint.Value.Y;
if (Math.Abs(dx) < MaxMoveSpeed && Math.Abs(dy) < MaxMoveSpeed)
{
var handle = new WindowInteropHelper(_window).Handle;
InteropMethods.GetWindowRect(handle, out var lpRect);
InteropMethods.SetWindowPos(handle, IntPtr.Zero, lpRect.Left + dx, lpRect.Top + dy, 0, 0,
(int) (InteropValues.WindowPositionFlags.SWP_NOSIZE |
InteropValues.WindowPositionFlags.SWP_NOZORDER));
}
_lastPoint = lpPoint;
}
}
@@ -1,107 +1,106 @@
namespace HandyControl.Tools
namespace HandyControl.Tools;
/// <summary>
/// 验证帮助类
/// </summary>
public class ValidateHelper
{
/// <summary>
/// 验证帮助类
/// 是否在浮点数范围内
/// </summary>
public class ValidateHelper
/// <param name="value"></param>
/// <returns></returns>
public static bool IsInRangeOfDouble(object value)
{
/// <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>
/// <returns></returns>
public static bool IsInRangeOfPosDouble(object value)
{
var v = (double) value;
return !(double.IsNaN(v) || double.IsInfinity(v)) && v > 0;
}
/// <summary>
/// 是否在正浮点数范围内(包括0)
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
public static bool IsInRangeOfPosDoubleIncludeZero(object value)
{
var v = (double) value;
return !(double.IsNaN(v) || double.IsInfinity(v)) && v >= 0;
}
/// <summary>
/// 是否在负浮点数范围内
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
public static bool IsInRangeOfNegDouble(object value)
{
var v = (double) value;
return !(double.IsNaN(v) || double.IsInfinity(v)) && v < 0;
}
/// <summary>
/// 是否在负浮点数范围内(包括0)
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
public static bool IsInRangeOfNegDoubleIncludeZero(object value)
{
var v = (double) value;
return !(double.IsNaN(v) || double.IsInfinity(v)) && v <= 0;
}
/// <summary>
/// 是否在正整数范围内
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
public static bool IsInRangeOfPosInt(object value)
{
var v = (int) value;
return v > 0;
}
/// <summary>
/// 是否在正整数范围内(包括0)
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
public static bool IsInRangeOfPosIntIncludeZero(object value)
{
var v = (int) value;
return v >= 0;
}
/// <summary>
/// 是否在负整数范围内
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
public static bool IsInRangeOfNegInt(object value)
{
var v = (int) value;
return v < 0;
}
/// <summary>
/// 是否在负整数范围内(包括0)
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
public static bool IsInRangeOfNegIntIncludeZero(object value)
{
var v = (int) value;
return v <= 0;
}
var v = (double) value;
return !(double.IsNaN(v) || double.IsInfinity(v));
}
}
/// <summary>
/// 是否在正浮点数范围内
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
public static bool IsInRangeOfPosDouble(object value)
{
var v = (double) value;
return !(double.IsNaN(v) || double.IsInfinity(v)) && v > 0;
}
/// <summary>
/// 是否在正浮点数范围内(包括0)
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
public static bool IsInRangeOfPosDoubleIncludeZero(object value)
{
var v = (double) value;
return !(double.IsNaN(v) || double.IsInfinity(v)) && v >= 0;
}
/// <summary>
/// 是否在负浮点数范围内
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
public static bool IsInRangeOfNegDouble(object value)
{
var v = (double) value;
return !(double.IsNaN(v) || double.IsInfinity(v)) && v < 0;
}
/// <summary>
/// 是否在负浮点数范围内(包括0)
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
public static bool IsInRangeOfNegDoubleIncludeZero(object value)
{
var v = (double) value;
return !(double.IsNaN(v) || double.IsInfinity(v)) && v <= 0;
}
/// <summary>
/// 是否在正整数范围内
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
public static bool IsInRangeOfPosInt(object value)
{
var v = (int) value;
return v > 0;
}
/// <summary>
/// 是否在正整数范围内(包括0)
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
public static bool IsInRangeOfPosIntIncludeZero(object value)
{
var v = (int) value;
return v >= 0;
}
/// <summary>
/// 是否在负整数范围内
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
public static bool IsInRangeOfNegInt(object value)
{
var v = (int) value;
return v < 0;
}
/// <summary>
/// 是否在负整数范围内(包括0)
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
public static bool IsInRangeOfNegIntIncludeZero(object value)
{
var v = (int) value;
return v <= 0;
}
}
@@ -5,73 +5,72 @@ using System.Windows.Interop;
using System.Windows.Media;
using HandyControl.Tools.Interop;
namespace HandyControl.Tools
namespace HandyControl.Tools;
public static class VisualHelper
{
public static class VisualHelper
internal static VisualStateGroup TryGetVisualStateGroup(DependencyObject d, string groupName)
{
internal static VisualStateGroup TryGetVisualStateGroup(DependencyObject d, string groupName)
{
var root = GetImplementationRoot(d);
if (root == null) return null;
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) =>
1 == VisualTreeHelper.GetChildrenCount(d)
? VisualTreeHelper.GetChild(d, 0) as FrameworkElement
: null;
public static T GetChild<T>(DependencyObject d) where T : DependencyObject
{
if (d == null) return default;
if (d is T t) return t;
for (var 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;
}
public static T GetParent<T>(DependencyObject d) where T : DependencyObject =>
d switch
{
null => default,
T t => t,
Window _ => null,
_ => GetParent<T>(VisualTreeHelper.GetParent(d))
};
public static IntPtr GetHandle(this Visual visual) => (PresentationSource.FromVisual(visual) as HwndSource)?.Handle ?? IntPtr.Zero;
internal static void HitTestVisibleElements(Visual visual, HitTestResultCallback resultCallback, HitTestParameters parameters) =>
VisualTreeHelper.HitTest(visual, ExcludeNonVisualElements, resultCallback, parameters);
private static HitTestFilterBehavior ExcludeNonVisualElements(DependencyObject potentialHitTestTarget)
{
if (!(potentialHitTestTarget is Visual)) return HitTestFilterBehavior.ContinueSkipSelfAndChildren;
if (!(potentialHitTestTarget is UIElement uIElement) || uIElement.IsVisible && uIElement.IsEnabled)
return HitTestFilterBehavior.Continue;
return HitTestFilterBehavior.ContinueSkipSelfAndChildren;
}
internal static bool ModifyStyle(IntPtr hWnd, int styleToRemove, int styleToAdd)
{
var windowLong = InteropMethods.GetWindowLong(hWnd, InteropValues.GWL.STYLE);
var num = (windowLong & ~styleToRemove) | styleToAdd;
if (num == windowLong) return false;
InteropMethods.SetWindowLong(hWnd, InteropValues.GWL.STYLE, num);
return true;
}
return VisualStateManager
.GetVisualStateGroups(root)?
.OfType<VisualStateGroup>()
.FirstOrDefault(group => string.CompareOrdinal(groupName, group.Name) == 0);
}
}
internal static FrameworkElement GetImplementationRoot(DependencyObject d) =>
1 == VisualTreeHelper.GetChildrenCount(d)
? VisualTreeHelper.GetChild(d, 0) as FrameworkElement
: null;
public static T GetChild<T>(DependencyObject d) where T : DependencyObject
{
if (d == null) return default;
if (d is T t) return t;
for (var 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;
}
public static T GetParent<T>(DependencyObject d) where T : DependencyObject =>
d switch
{
null => default,
T t => t,
Window _ => null,
_ => GetParent<T>(VisualTreeHelper.GetParent(d))
};
public static IntPtr GetHandle(this Visual visual) => (PresentationSource.FromVisual(visual) as HwndSource)?.Handle ?? IntPtr.Zero;
internal static void HitTestVisibleElements(Visual visual, HitTestResultCallback resultCallback, HitTestParameters parameters) =>
VisualTreeHelper.HitTest(visual, ExcludeNonVisualElements, resultCallback, parameters);
private static HitTestFilterBehavior ExcludeNonVisualElements(DependencyObject potentialHitTestTarget)
{
if (!(potentialHitTestTarget is Visual)) return HitTestFilterBehavior.ContinueSkipSelfAndChildren;
if (!(potentialHitTestTarget is UIElement uIElement) || uIElement.IsVisible && uIElement.IsEnabled)
return HitTestFilterBehavior.Continue;
return HitTestFilterBehavior.ContinueSkipSelfAndChildren;
}
internal static bool ModifyStyle(IntPtr hWnd, int styleToRemove, int styleToAdd)
{
var windowLong = InteropMethods.GetWindowLong(hWnd, InteropValues.GWL.STYLE);
var num = (windowLong & ~styleToRemove) | styleToAdd;
if (num == windowLong) return false;
InteropMethods.SetWindowLong(hWnd, InteropValues.GWL.STYLE, num);
return true;
}
}
@@ -12,133 +12,133 @@ using HandyControl.Tools.Helper;
using HandyControl.Tools.Interop;
using Window = System.Windows.Window;
namespace HandyControl.Tools
namespace HandyControl.Tools;
public static class WindowHelper
{
public static class WindowHelper
/// <summary>
/// 获取当前应用中处于激活的一个窗口
/// </summary>
/// <returns></returns>
public static Window GetActiveWindow() => Application.Current.Windows.OfType<Window>().SingleOrDefault(x => x.IsActive);
private static readonly BitArray _cacheValid = new((int) InteropValues.CacheSlot.NumSlots);
private static bool _setDpiX = true;
private static bool _dpiInitialized;
private static readonly object _dpiLock = new();
private static int _dpi;
internal static int Dpi
{
/// <summary>
/// 获取当前应用中处于激活的一个窗口
/// </summary>
/// <returns></returns>
public static Window GetActiveWindow() => Application.Current.Windows.OfType<Window>().SingleOrDefault(x => x.IsActive);
private static readonly BitArray _cacheValid = new((int) InteropValues.CacheSlot.NumSlots);
private static bool _setDpiX = true;
private static bool _dpiInitialized;
private static readonly object _dpiLock = new();
private static int _dpi;
internal static int Dpi
[SecurityCritical, SecuritySafeCritical]
get
{
[SecurityCritical, SecuritySafeCritical]
get
if (!_dpiInitialized)
{
if (!_dpiInitialized)
lock (_dpiLock)
{
lock (_dpiLock)
if (!_dpiInitialized)
{
if (!_dpiInitialized)
var desktopWnd = new HandleRef(null, IntPtr.Zero);
// Win32Exception will get the Win32 error code so we don't have to
var dc = InteropMethods.GetDC(desktopWnd);
// Detecting error case from unmanaged call, required by PREsharp to throw a Win32Exception
if (dc == IntPtr.Zero)
{
var desktopWnd = new HandleRef(null, IntPtr.Zero);
throw new Win32Exception();
}
// Win32Exception will get the Win32 error code so we don't have to
var dc = InteropMethods.GetDC(desktopWnd);
// Detecting error case from unmanaged call, required by PREsharp to throw a Win32Exception
if (dc == IntPtr.Zero)
{
throw new Win32Exception();
}
try
{
_dpi = InteropMethods.GetDeviceCaps(new HandleRef(null, dc), InteropValues.LOGPIXELSY);
_dpiInitialized = true;
}
finally
{
InteropMethods.ReleaseDC(desktopWnd, new HandleRef(null, dc));
}
try
{
_dpi = InteropMethods.GetDeviceCaps(new HandleRef(null, dc), InteropValues.LOGPIXELSY);
_dpiInitialized = true;
}
finally
{
InteropMethods.ReleaseDC(desktopWnd, new HandleRef(null, dc));
}
}
}
return _dpi;
}
return _dpi;
}
}
private static int _dpiX;
private static int _dpiX;
internal static int DpiX
internal static int DpiX
{
[SecurityCritical, SecuritySafeCritical]
get
{
[SecurityCritical, SecuritySafeCritical]
get
{
if (_setDpiX)
{
lock (_cacheValid)
{
if (_setDpiX)
{
_setDpiX = false;
var desktopWnd = new HandleRef(null, IntPtr.Zero);
var dc = InteropMethods.GetDC(desktopWnd);
if (dc == IntPtr.Zero)
{
throw new Win32Exception();
}
try
{
_dpiX = InteropMethods.GetDeviceCaps(new HandleRef(null, dc), InteropValues.LOGPIXELSX);
_cacheValid[(int) InteropValues.CacheSlot.DpiX] = true;
}
finally
{
InteropMethods.ReleaseDC(desktopWnd, new HandleRef(null, dc));
}
}
}
}
return _dpiX;
}
}
private static Thickness _windowResizeBorderThickness;
internal static Thickness WindowResizeBorderThickness
{
[SecurityCritical]
get
if (_setDpiX)
{
lock (_cacheValid)
{
while (!_cacheValid[(int) InteropValues.CacheSlot.WindowResizeBorderThickness])
if (_setDpiX)
{
_cacheValid[(int) InteropValues.CacheSlot.WindowResizeBorderThickness] = true;
var frameSize = new Size(InteropMethods.GetSystemMetrics(InteropValues.SM.CXSIZEFRAME), InteropMethods.GetSystemMetrics(InteropValues.SM.CYSIZEFRAME));
var frameSizeInDips = DpiHelper.DeviceSizeToLogical(frameSize, DpiX / 96.0, Dpi / 96.0);
_windowResizeBorderThickness = new Thickness(frameSizeInDips.Width, frameSizeInDips.Height, frameSizeInDips.Width, frameSizeInDips.Height);
_setDpiX = false;
var desktopWnd = new HandleRef(null, IntPtr.Zero);
var dc = InteropMethods.GetDC(desktopWnd);
if (dc == IntPtr.Zero)
{
throw new Win32Exception();
}
try
{
_dpiX = InteropMethods.GetDeviceCaps(new HandleRef(null, dc), InteropValues.LOGPIXELSX);
_cacheValid[(int) InteropValues.CacheSlot.DpiX] = true;
}
finally
{
InteropMethods.ReleaseDC(desktopWnd, new HandleRef(null, dc));
}
}
}
return _windowResizeBorderThickness;
}
}
public static Thickness WindowMaximizedPadding
return _dpiX;
}
}
private static Thickness _windowResizeBorderThickness;
internal static Thickness WindowResizeBorderThickness
{
[SecurityCritical]
get
{
get
lock (_cacheValid)
{
InteropValues.APPBARDATA appBarData = default;
var autoHide = InteropMethods.SHAppBarMessage(4, ref appBarData) != 0;
while (!_cacheValid[(int) InteropValues.CacheSlot.WindowResizeBorderThickness])
{
_cacheValid[(int) InteropValues.CacheSlot.WindowResizeBorderThickness] = true;
var frameSize = new Size(InteropMethods.GetSystemMetrics(InteropValues.SM.CXSIZEFRAME), InteropMethods.GetSystemMetrics(InteropValues.SM.CYSIZEFRAME));
var frameSizeInDips = DpiHelper.DeviceSizeToLogical(frameSize, DpiX / 96.0, Dpi / 96.0);
_windowResizeBorderThickness = new Thickness(frameSizeInDips.Width, frameSizeInDips.Height, frameSizeInDips.Width, frameSizeInDips.Height);
}
}
return _windowResizeBorderThickness;
}
}
public static Thickness WindowMaximizedPadding
{
get
{
InteropValues.APPBARDATA appBarData = default;
var autoHide = InteropMethods.SHAppBarMessage(4, ref appBarData) != 0;
#if NET40
return WindowResizeBorderThickness.Add(new Thickness(autoHide ? -8 : 0));
return WindowResizeBorderThickness.Add(new Thickness(autoHide ? -8 : 0));
#elif NETCOREAPP
var hdc = InteropMethods.GetDC(IntPtr.Zero);
var scale = InteropMethods.GetDeviceCaps(hdc, InteropValues.DESKTOPVERTRES) / (float) InteropMethods.GetDeviceCaps(hdc, InteropValues.VERTRES);
@@ -147,59 +147,58 @@ namespace HandyControl.Tools
#else
return WindowResizeBorderThickness.Add(new Thickness(autoHide ? -4 : 4));
#endif
}
}
public static IntPtr CreateHandle() => new WindowInteropHelper(new Window()).EnsureHandle();
public static IntPtr GetHandle(this Window window) => new WindowInteropHelper(window).EnsureHandle();
public static HwndSource GetHwndSource(this Window window) => HwndSource.FromHwnd(window.GetHandle());
/// <summary>
/// 让窗口激活作为前台最上层窗口
/// </summary>
/// <param name="window"></param>
public static void SetWindowToForeground(Window window)
{
// [WPF 让窗口激活作为前台最上层窗口的方法 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/12749671.html)
var interopHelper = new WindowInteropHelper(window);
var thisWindowThreadId = InteropMethods.GetWindowThreadProcessId(interopHelper.Handle, out _);
var currentForegroundWindow = InteropMethods.GetForegroundWindow();
var currentForegroundWindowThreadId = InteropMethods.GetWindowThreadProcessId(currentForegroundWindow, out _);
// [c# - Bring a window to the front in WPF - Stack Overflow](https://stackoverflow.com/questions/257587/bring-a-window-to-the-front-in-wpf )
// [SetForegroundWindow的正确用法 - 子坞 - 博客园](https://www.cnblogs.com/ziwuge/archive/2012/01/06/2315342.html )
/*
  1.得到窗口句柄FindWindow
    2.切换键盘输入焦点AttachThreadInput
    3.显示窗口ShowWindow(有些窗口被最小化/隐藏了)
    4.更改窗口的Z Order,SetWindowPos使之最上,为了不影响后续窗口的Z Order,改完之后,再还原
    5.最后SetForegroundWindow
*/
InteropMethods.AttachThreadInput(currentForegroundWindowThreadId, thisWindowThreadId, true);
window.Show();
window.Activate();
// 去掉和其他线程的输入链接
InteropMethods.AttachThreadInput(currentForegroundWindowThreadId, thisWindowThreadId, false);
// 用于踢掉其他的在上层的窗口
if (window.Topmost != true)
{
window.Topmost = true;
window.Topmost = false;
}
}
/// <summary>
/// 开始使用触摸拖动窗口,在触摸抬起后自动结束
/// </summary>
public static void TouchDragMove(this Window window) => new TouchDragMoveWindowHelper(window).Start();
public static void StartFullScreen(this Window window) => FullScreenHelper.StartFullScreen(window);
public static void EndFullScreen(this Window window) => FullScreenHelper.EndFullScreen(window);
}
}
public static IntPtr CreateHandle() => new WindowInteropHelper(new Window()).EnsureHandle();
public static IntPtr GetHandle(this Window window) => new WindowInteropHelper(window).EnsureHandle();
public static HwndSource GetHwndSource(this Window window) => HwndSource.FromHwnd(window.GetHandle());
/// <summary>
/// 让窗口激活作为前台最上层窗口
/// </summary>
/// <param name="window"></param>
public static void SetWindowToForeground(Window window)
{
// [WPF 让窗口激活作为前台最上层窗口的方法 - lindexi - 博客园](https://www.cnblogs.com/lindexi/p/12749671.html)
var interopHelper = new WindowInteropHelper(window);
var thisWindowThreadId = InteropMethods.GetWindowThreadProcessId(interopHelper.Handle, out _);
var currentForegroundWindow = InteropMethods.GetForegroundWindow();
var currentForegroundWindowThreadId = InteropMethods.GetWindowThreadProcessId(currentForegroundWindow, out _);
// [c# - Bring a window to the front in WPF - Stack Overflow](https://stackoverflow.com/questions/257587/bring-a-window-to-the-front-in-wpf )
// [SetForegroundWindow的正确用法 - 子坞 - 博客园](https://www.cnblogs.com/ziwuge/archive/2012/01/06/2315342.html )
/*
1.得到窗口句柄FindWindow
2.切换键盘输入焦点AttachThreadInput
3.显示窗口ShowWindow(有些窗口被最小化/隐藏了)
4.更改窗口的Z Order,SetWindowPos使之最上,为了不影响后续窗口的Z Order,改完之后,再还原
5.最后SetForegroundWindow
*/
InteropMethods.AttachThreadInput(currentForegroundWindowThreadId, thisWindowThreadId, true);
window.Show();
window.Activate();
// 去掉和其他线程的输入链接
InteropMethods.AttachThreadInput(currentForegroundWindowThreadId, thisWindowThreadId, false);
// 用于踢掉其他的在上层的窗口
if (window.Topmost != true)
{
window.Topmost = true;
window.Topmost = false;
}
}
/// <summary>
/// 开始使用触摸拖动窗口,在触摸抬起后自动结束
/// </summary>
public static void TouchDragMove(this Window window) => new TouchDragMoveWindowHelper(window).Start();
public static void StartFullScreen(this Window window) => FullScreenHelper.StartFullScreen(window);
public static void EndFullScreen(this Window window) => FullScreenHelper.EndFullScreen(window);
}
@@ -2,56 +2,55 @@
using System.Windows.Interop;
using HandyControl.Tools.Interop;
namespace HandyControl.Tools
namespace HandyControl.Tools;
public class ClipboardHook
{
public class ClipboardHook
public static event Action ContentChanged;
private static HwndSource HWndSource;
private static IntPtr HookId = IntPtr.Zero;
private static int Count;
public static void Start()
{
public static event Action ContentChanged;
private static HwndSource HWndSource;
private static IntPtr HookId = IntPtr.Zero;
private static int Count;
public static void Start()
if (HookId == IntPtr.Zero)
{
if (HookId == IntPtr.Zero)
HookId = WindowHelper.CreateHandle();
HWndSource = HwndSource.FromHwnd(HookId);
if (HWndSource != null)
{
HookId = WindowHelper.CreateHandle();
HWndSource = HwndSource.FromHwnd(HookId);
if (HWndSource != null)
{
HWndSource.AddHook(WinProc);
InteropMethods.AddClipboardFormatListener(HookId);
}
}
if (HookId != IntPtr.Zero)
{
Count++;
HWndSource.AddHook(WinProc);
InteropMethods.AddClipboardFormatListener(HookId);
}
}
public static void Stop()
if (HookId != IntPtr.Zero)
{
Count--;
if (Count < 1)
{
HWndSource.RemoveHook(WinProc);
InteropMethods.RemoveClipboardFormatListener(HookId);
HookId = IntPtr.Zero;
}
}
private static IntPtr WinProc(IntPtr hwnd, int msg, IntPtr wparam, IntPtr lparam, ref bool handled)
{
if (msg == InteropValues.WM_CLIPBOARDUPDATE)
{
ContentChanged?.Invoke();
}
return IntPtr.Zero;
Count++;
}
}
}
public static void Stop()
{
Count--;
if (Count < 1)
{
HWndSource.RemoveHook(WinProc);
InteropMethods.RemoveClipboardFormatListener(HookId);
HookId = IntPtr.Zero;
}
}
private static IntPtr WinProc(IntPtr hwnd, int msg, IntPtr wparam, IntPtr lparam, ref bool handled)
{
if (msg == InteropValues.WM_CLIPBOARDUPDATE)
{
ContentChanged?.Invoke();
}
return IntPtr.Zero;
}
}
@@ -4,102 +4,101 @@ using System.Runtime.InteropServices;
using HandyControl.Data;
using HandyControl.Tools.Interop;
namespace HandyControl.Tools
namespace HandyControl.Tools;
public class KeyboardHook
{
public class KeyboardHook
public static event EventHandler<KeyboardHookEventArgs> KeyDown;
public static event EventHandler<KeyboardHookEventArgs> KeyUp;
private static IntPtr HookId = IntPtr.Zero;
private static readonly InteropValues.HookProc Proc = HookCallback;
private static int VirtualKey;
private static readonly IntPtr KeyDownIntPtr = (IntPtr) InteropValues.WM_KEYDOWN;
private static readonly IntPtr KeyUpIntPtr = (IntPtr) InteropValues.WM_KEYUP;
private static readonly IntPtr SyskeyDownIntPtr = (IntPtr) InteropValues.WM_SYSKEYDOWN;
private static readonly IntPtr SyskeyUpIntPtr = (IntPtr) InteropValues.WM_SYSKEYUP;
private static int Count;
public static void Start()
{
public static event EventHandler<KeyboardHookEventArgs> KeyDown;
public static event EventHandler<KeyboardHookEventArgs> KeyUp;
private static IntPtr HookId = IntPtr.Zero;
private static readonly InteropValues.HookProc Proc = HookCallback;
private static int VirtualKey;
private static readonly IntPtr KeyDownIntPtr = (IntPtr) InteropValues.WM_KEYDOWN;
private static readonly IntPtr KeyUpIntPtr = (IntPtr) InteropValues.WM_KEYUP;
private static readonly IntPtr SyskeyDownIntPtr = (IntPtr) InteropValues.WM_SYSKEYDOWN;
private static readonly IntPtr SyskeyUpIntPtr = (IntPtr) InteropValues.WM_SYSKEYUP;
private static int Count;
public static void Start()
if (HookId == IntPtr.Zero)
{
if (HookId == IntPtr.Zero)
{
HookId = SetHook(Proc);
}
if (HookId != IntPtr.Zero)
{
Count++;
}
HookId = SetHook(Proc);
}
public static void Stop()
if (HookId != IntPtr.Zero)
{
Count--;
if (Count < 1)
{
InteropMethods.UnhookWindowsHookEx(HookId);
HookId = IntPtr.Zero;
}
}
private static IntPtr SetHook(InteropValues.HookProc proc)
{
using var curProcess = Process.GetCurrentProcess();
using var curModule = curProcess.MainModule;
if (curModule != null)
{
return InteropMethods.SetWindowsHookEx((int) InteropValues.HookType.WH_KEYBOARD_LL, proc,
InteropMethods.GetModuleHandle(curModule.ModuleName), 0);
}
return IntPtr.Zero;
}
private static IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam)
{
if (nCode >= 0)
{
if (wParam == KeyDownIntPtr)
{
var virtualKey = Marshal.ReadInt32(lParam);
if (VirtualKey != virtualKey)
{
VirtualKey = virtualKey;
KeyDown?.Invoke(null, new KeyboardHookEventArgs(virtualKey, false));
}
}
else if (wParam == SyskeyDownIntPtr)
{
var virtualKey = Marshal.ReadInt32(lParam);
if (VirtualKey != virtualKey)
{
VirtualKey = virtualKey;
KeyDown?.Invoke(null, new KeyboardHookEventArgs(virtualKey, true));
}
}
else if (wParam == KeyUpIntPtr)
{
var virtualKey = Marshal.ReadInt32(lParam);
VirtualKey = -1;
KeyUp?.Invoke(null, new KeyboardHookEventArgs(virtualKey, false));
}
else if (wParam == SyskeyUpIntPtr)
{
var virtualKey = Marshal.ReadInt32(lParam);
VirtualKey = -1;
KeyUp?.Invoke(null, new KeyboardHookEventArgs(virtualKey, true));
}
}
return InteropMethods.CallNextHookEx(HookId, nCode, wParam, lParam);
Count++;
}
}
}
public static void Stop()
{
Count--;
if (Count < 1)
{
InteropMethods.UnhookWindowsHookEx(HookId);
HookId = IntPtr.Zero;
}
}
private static IntPtr SetHook(InteropValues.HookProc proc)
{
using var curProcess = Process.GetCurrentProcess();
using var curModule = curProcess.MainModule;
if (curModule != null)
{
return InteropMethods.SetWindowsHookEx((int) InteropValues.HookType.WH_KEYBOARD_LL, proc,
InteropMethods.GetModuleHandle(curModule.ModuleName), 0);
}
return IntPtr.Zero;
}
private static IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam)
{
if (nCode >= 0)
{
if (wParam == KeyDownIntPtr)
{
var virtualKey = Marshal.ReadInt32(lParam);
if (VirtualKey != virtualKey)
{
VirtualKey = virtualKey;
KeyDown?.Invoke(null, new KeyboardHookEventArgs(virtualKey, false));
}
}
else if (wParam == SyskeyDownIntPtr)
{
var virtualKey = Marshal.ReadInt32(lParam);
if (VirtualKey != virtualKey)
{
VirtualKey = virtualKey;
KeyDown?.Invoke(null, new KeyboardHookEventArgs(virtualKey, true));
}
}
else if (wParam == KeyUpIntPtr)
{
var virtualKey = Marshal.ReadInt32(lParam);
VirtualKey = -1;
KeyUp?.Invoke(null, new KeyboardHookEventArgs(virtualKey, false));
}
else if (wParam == SyskeyUpIntPtr)
{
var virtualKey = Marshal.ReadInt32(lParam);
VirtualKey = -1;
KeyUp?.Invoke(null, new KeyboardHookEventArgs(virtualKey, true));
}
}
return InteropMethods.CallNextHookEx(HookId, nCode, wParam, lParam);
}
}
@@ -4,65 +4,64 @@ using System.Runtime.InteropServices;
using HandyControl.Data;
using HandyControl.Tools.Interop;
namespace HandyControl.Tools
namespace HandyControl.Tools;
internal class MouseHook
{
internal class MouseHook
public static event EventHandler<MouseHookEventArgs> StatusChanged;
private static IntPtr HookId = IntPtr.Zero;
private static readonly InteropValues.HookProc Proc = HookCallback;
private static int Count;
public static void Start()
{
public static event EventHandler<MouseHookEventArgs> StatusChanged;
private static IntPtr HookId = IntPtr.Zero;
private static readonly InteropValues.HookProc Proc = HookCallback;
private static int Count;
public static void Start()
if (HookId == IntPtr.Zero)
{
if (HookId == IntPtr.Zero)
{
HookId = SetHook(Proc);
}
if (HookId != IntPtr.Zero)
{
Count++;
}
HookId = SetHook(Proc);
}
public static void Stop()
if (HookId != IntPtr.Zero)
{
Count--;
if (Count < 1)
{
InteropMethods.UnhookWindowsHookEx(HookId);
HookId = IntPtr.Zero;
}
}
private static IntPtr SetHook(InteropValues.HookProc proc)
{
using var curProcess = Process.GetCurrentProcess();
using var curModule = curProcess.MainModule;
if (curModule != null)
{
return InteropMethods.SetWindowsHookEx((int) InteropValues.HookType.WH_MOUSE_LL, proc,
InteropMethods.GetModuleHandle(curModule.ModuleName), 0);
}
return IntPtr.Zero;
}
private static IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam)
{
if (nCode < 0) return InteropMethods.CallNextHookEx(HookId, nCode, wParam, lParam);
var hookStruct = (InteropValues.MOUSEHOOKSTRUCT) Marshal.PtrToStructure(lParam, typeof(InteropValues.MOUSEHOOKSTRUCT));
StatusChanged?.Invoke(null, new MouseHookEventArgs
{
MessageType = (MouseHookMessageType) wParam,
Point = new InteropValues.POINT(hookStruct.pt.X, hookStruct.pt.Y)
});
return InteropMethods.CallNextHookEx(HookId, nCode, wParam, lParam);
Count++;
}
}
}
public static void Stop()
{
Count--;
if (Count < 1)
{
InteropMethods.UnhookWindowsHookEx(HookId);
HookId = IntPtr.Zero;
}
}
private static IntPtr SetHook(InteropValues.HookProc proc)
{
using var curProcess = Process.GetCurrentProcess();
using var curModule = curProcess.MainModule;
if (curModule != null)
{
return InteropMethods.SetWindowsHookEx((int) InteropValues.HookType.WH_MOUSE_LL, proc,
InteropMethods.GetModuleHandle(curModule.ModuleName), 0);
}
return IntPtr.Zero;
}
private static IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam)
{
if (nCode < 0) return InteropMethods.CallNextHookEx(HookId, nCode, wParam, lParam);
var hookStruct = (InteropValues.MOUSEHOOKSTRUCT) Marshal.PtrToStructure(lParam, typeof(InteropValues.MOUSEHOOKSTRUCT));
StatusChanged?.Invoke(null, new MouseHookEventArgs
{
MessageType = (MouseHookMessageType) wParam,
Point = new InteropValues.POINT(hookStruct.pt.X, hookStruct.pt.Y)
});
return InteropMethods.CallNextHookEx(HookId, nCode, wParam, lParam);
}
}
@@ -4,50 +4,49 @@ using System.Windows;
using System.Windows.Interop;
using HandyControl.Tools.Interop;
namespace HandyControl.Tools
namespace HandyControl.Tools;
public class SystemMenuHook
{
public class SystemMenuHook
private static readonly Dictionary<int, HwndSource> DataDic = new();
public static event Action<int> Click;
public static void Insert(int index, int id, string text, Window window)
{
private static readonly Dictionary<int, HwndSource> DataDic = new();
public static event Action<int> Click;
public static void Insert(int index, int id, string text, Window window)
var hookId = window.GetHandle();
var source = HwndSource.FromHwnd(hookId);
if (source != null)
{
var hookId = window.GetHandle();
var source = HwndSource.FromHwnd(hookId);
if (source != null)
{
DataDic[id] = source;
InteropMethods.InsertMenu(InteropMethods.GetSystemMenu(hookId, false), index, InteropValues.MF_BYPOSITION, id, text);
source.AddHook(WinProc);
}
}
public static void InsertSeperator(int index, Window window) => InteropMethods.InsertMenu(
InteropMethods.GetSystemMenu(window.GetHandle(), false), index,
InteropValues.MF_BYPOSITION | InteropValues.MF_SEPARATOR, 0, "");
public static void Remove(int id)
{
if (DataDic.TryGetValue(id, out var data))
{
data.RemoveHook(WinProc);
DataDic.Remove(id);
}
}
private static IntPtr WinProc(IntPtr hwnd, int msg, IntPtr wparam, IntPtr lparam, ref bool handled)
{
if (msg == InteropValues.WM_SYSCOMMAND)
{
var id = wparam.ToInt32();
if (DataDic.ContainsKey(id))
{
Click?.Invoke(id);
}
}
return IntPtr.Zero;
DataDic[id] = source;
InteropMethods.InsertMenu(InteropMethods.GetSystemMenu(hookId, false), index, InteropValues.MF_BYPOSITION, id, text);
source.AddHook(WinProc);
}
}
}
public static void InsertSeperator(int index, Window window) => InteropMethods.InsertMenu(
InteropMethods.GetSystemMenu(window.GetHandle(), false), index,
InteropValues.MF_BYPOSITION | InteropValues.MF_SEPARATOR, 0, "");
public static void Remove(int id)
{
if (DataDic.TryGetValue(id, out var data))
{
data.RemoveHook(WinProc);
DataDic.Remove(id);
}
}
private static IntPtr WinProc(IntPtr hwnd, int msg, IntPtr wparam, IntPtr lparam, ref bool handled)
{
if (msg == InteropValues.WM_SYSCOMMAND)
{
var id = wparam.ToInt32();
if (DataDic.ContainsKey(id))
{
Click?.Invoke(id);
}
}
return IntPtr.Zero;
}
}
@@ -4,42 +4,41 @@ using System.Runtime.ConstrainedExecution;
using System.Runtime.InteropServices;
using System.Security;
namespace HandyControl.Tools.Interop
namespace HandyControl.Tools.Interop;
[SuppressMessage("ReSharper", "UnusedMember.Local")]
internal sealed class BitmapHandle : WpfSafeHandle
{
[SuppressMessage("ReSharper", "UnusedMember.Local")]
internal sealed class BitmapHandle : WpfSafeHandle
[SecurityCritical]
private BitmapHandle() : this(true)
{
[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 InteropMethods.DeleteObject(handle);
}
[SecurityCritical]
internal HandleRef MakeHandleRef(object obj)
{
return new(obj, handle);
}
[SecurityCritical]
internal static BitmapHandle CreateFromHandle(IntPtr hbitmap, bool ownsHandle = true)
{
return new(ownsHandle)
{
handle = hbitmap,
};
}
//请不要删除此构造函数,否则当使用自定义ico文件时会报错
}
}
[SecurityCritical]
private BitmapHandle(bool ownsHandle) : base(ownsHandle, CommonHandles.GDI)
{
}
[SecurityCritical]
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
protected override bool ReleaseHandle()
{
return InteropMethods.DeleteObject(handle);
}
[SecurityCritical]
internal HandleRef MakeHandleRef(object obj)
{
return new(obj, handle);
}
[SecurityCritical]
internal static BitmapHandle CreateFromHandle(IntPtr hbitmap, bool ownsHandle = true)
{
return new(ownsHandle)
{
handle = hbitmap,
};
}
}
@@ -1,16 +1,15 @@
using System.Diagnostics.CodeAnalysis;
namespace HandyControl.Tools.Interop
namespace HandyControl.Tools.Interop;
[SuppressMessage("ReSharper", "InconsistentNaming")]
internal static class CommonHandles
{
[SuppressMessage("ReSharper", "InconsistentNaming")]
internal static class CommonHandles
{
public static readonly int Icon = HandleCollector.RegisterType(nameof(Icon), 20, 500);
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 HDC = HandleCollector.RegisterType(nameof(HDC), 100, 2);
public static readonly int GDI = HandleCollector.RegisterType(nameof(GDI), 50, 500);
public static readonly int GDI = HandleCollector.RegisterType(nameof(GDI), 50, 500);
public static readonly int Kernel = HandleCollector.RegisterType(nameof(Kernel), 0, 1000);
}
}
public static readonly int Kernel = HandleCollector.RegisterType(nameof(Kernel), 0, 1000);
}
@@ -3,130 +3,129 @@
using System;
using System.Runtime.InteropServices;
namespace HandyControl.Tools.Interop
namespace HandyControl.Tools.Interop;
internal static class HandleCollector
{
internal static class HandleCollector
private static HandleType[] HandleTypes;
private static int HandleTypeCount;
private static readonly object HandleMutex = new();
internal static IntPtr Add(IntPtr handle, int type)
{
private static HandleType[] HandleTypes;
private static int HandleTypeCount;
HandleTypes[type - 1].Add();
return handle;
}
private static readonly object HandleMutex = new();
[System.Security.SecuritySafeCritical]
internal static SafeHandle Add(SafeHandle handle, int type)
{
HandleTypes[type - 1].Add();
return handle;
}
internal static IntPtr Add(IntPtr handle, int type)
internal static void Add(int type)
{
HandleTypes[type - 1].Add();
}
internal static int RegisterType(string typeName, int expense, int initialThreshold)
{
lock (HandleMutex)
{
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)
{
if (HandleTypeCount == 0 || HandleTypeCount == HandleTypes.Length)
HandleType[] newTypes = new HandleType[HandleTypeCount + 10];
if (HandleTypes != null)
{
HandleType[] newTypes = new HandleType[HandleTypeCount + 10];
if (HandleTypes != null)
{
Array.Copy(HandleTypes, 0, newTypes, 0, HandleTypeCount);
}
HandleTypes = newTypes;
Array.Copy(HandleTypes, 0, newTypes, 0, HandleTypeCount);
}
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;
HandleTypes = newTypes;
}
internal void Add()
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)
{
lock (this)
_handleCount++;
var performCollect = NeedCollection();
if (!performCollect)
{
_handleCount++;
var performCollect = NeedCollection();
if (!performCollect)
{
return;
}
return;
}
GC.Collect();
var sleep = (100 - _deltaPercent) / 4;
System.Threading.Thread.Sleep(sleep);
}
private bool NeedCollection()
GC.Collect();
var sleep = (100 - _deltaPercent) / 4;
System.Threading.Thread.Sleep(sleep);
}
private bool NeedCollection()
{
if (_handleCount > _threshHold)
{
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;
_threshHold = _handleCount + _handleCount * _deltaPercent / 100;
return true;
}
internal void Remove()
var oldThreshHold = 100 * _threshHold / (100 + _deltaPercent);
if (oldThreshHold >= _initialThreshHold && _handleCount < (int) (oldThreshHold * .9F))
{
lock (this)
{
_handleCount--;
_threshHold = oldThreshHold;
}
_handleCount = Math.Max(0, _handleCount);
}
return false;
}
internal void Remove()
{
lock (this)
{
_handleCount--;
_handleCount = Math.Max(0, _handleCount);
}
}
}
}
}
@@ -2,32 +2,31 @@
using System.Runtime.ConstrainedExecution;
using System.Security;
namespace HandyControl.Tools.Interop
namespace HandyControl.Tools.Interop;
internal sealed class IconHandle : WpfSafeHandle
{
internal sealed class IconHandle : WpfSafeHandle
[SecurityCritical]
private IconHandle() : base(true, CommonHandles.Icon)
{
[SecurityCritical]
private IconHandle() : base(true, CommonHandles.Icon)
{
}
[SecurityCritical]
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
protected override bool ReleaseHandle()
{
return InteropMethods.DestroyIcon(handle);
}
[SecurityCritical, SecuritySafeCritical]
internal static IconHandle GetInvalidIcon()
{
return new();
}
[SecurityCritical]
internal IntPtr CriticalGetHandle()
{
return handle;
}
}
}
[SecurityCritical]
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
protected override bool ReleaseHandle()
{
return InteropMethods.DestroyIcon(handle);
}
[SecurityCritical, SecuritySafeCritical]
internal static IconHandle GetInvalidIcon()
{
return new();
}
[SecurityCritical]
internal IntPtr CriticalGetHandle()
{
return handle;
}
}
@@ -4,47 +4,46 @@ using System.Security;
using System.Security.Permissions;
using Microsoft.Win32.SafeHandles;
namespace HandyControl.Tools.Interop
namespace HandyControl.Tools.Interop;
internal sealed class SafeFileMappingHandle : SafeHandleZeroOrMinusOneIsInvalid
{
internal sealed class SafeFileMappingHandle : SafeHandleZeroOrMinusOneIsInvalid
[SecurityCritical]
internal SafeFileMappingHandle(IntPtr handle) : base(false)
{
[SecurityCritical]
internal SafeFileMappingHandle(IntPtr handle) : base(false)
{
SetHandle(handle);
}
SetHandle(handle);
}
[SecurityCritical, SecuritySafeCritical]
internal SafeFileMappingHandle() : base(true)
{
}
public override bool IsInvalid
{
[SecurityCritical, SecuritySafeCritical]
internal SafeFileMappingHandle() : base(true)
{
}
get => handle == IntPtr.Zero;
}
public override bool IsInvalid
[SecurityCritical, SecuritySafeCritical]
protected override bool ReleaseHandle()
{
new SecurityPermission(SecurityPermissionFlag.UnmanagedCode).Assert();
try
{
[SecurityCritical, SecuritySafeCritical]
get => handle == IntPtr.Zero;
return CloseHandleNoThrow(new HandleRef(null, handle));
}
[SecurityCritical, SecuritySafeCritical]
protected override bool ReleaseHandle()
finally
{
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 = InteropMethods.IntCloseHandle(handle);
return result;
CodeAccessPermission.RevertAssert();
}
}
}
[SecurityCritical]
public static bool CloseHandleNoThrow(HandleRef handle)
{
HandleCollector.Remove((IntPtr) handle, CommonHandles.Kernel);
var result = InteropMethods.IntCloseHandle(handle);
return result;
}
}
@@ -1,24 +1,23 @@
using System.Security;
using Microsoft.Win32.SafeHandles;
namespace HandyControl.Tools.Interop
namespace HandyControl.Tools.Interop;
internal abstract class WpfSafeHandle : SafeHandleZeroOrMinusOneIsInvalid
{
internal abstract class WpfSafeHandle : SafeHandleZeroOrMinusOneIsInvalid
private readonly int _collectorId;
[SecurityCritical]
protected WpfSafeHandle(bool ownsHandle, int collectorId) : base(ownsHandle)
{
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);
}
HandleCollector.Add(collectorId);
_collectorId = collectorId;
}
}
[SecurityCritical, SecuritySafeCritical]
protected override void Dispose(bool disposing)
{
HandleCollector.Remove(_collectorId);
base.Dispose(disposing);
}
}
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
@@ -2,96 +2,95 @@
using System.Text.RegularExpressions;
using HandyControl.Data;
namespace HandyControl.Tools
namespace HandyControl.Tools;
/// <summary>
/// 包含一些正则验证操作
/// </summary>
public static class RegexJudgment
{
private static readonly RegexPatterns RegexPatterns = new();
/// <summary>
/// 包含一些正则验证操作
/// 判断字符串格式是否符合某种要求
/// </summary>
public static class RegexJudgment
/// <param name="str">需要判断的字符串</param>
/// <param name="pattern">正则表达式</param>
/// <returns></returns>
public static bool IsKindOf(this string str, string pattern)
{
private static readonly RegexPatterns RegexPatterns = new();
/// <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,
RegexPatterns.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, RegexPatterns.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)
{
return ipType switch
{
IpType.A => Regex.IsMatch(ip, RegexPatterns.IpAPattern),
IpType.B => Regex.IsMatch(ip, RegexPatterns.IpBPattern),
IpType.C => Regex.IsMatch(ip, RegexPatterns.IpCPattern),
IpType.D => Regex.IsMatch(ip, RegexPatterns.IpDPattern),
IpType.E => Regex.IsMatch(ip, RegexPatterns.IpEPattern),
_ => false
};
}
/// <summary>
/// 判断字符串格式是否为IP地址
/// </summary>
/// <param name="ip">需要判断的IP字符串</param>
/// <returns>方法返回布尔值</returns>
public static bool IsIp(this string ip)
{
return Regex.IsMatch(ip, RegexPatterns.IpPattern);
}
/// <summary>
/// 判断字符串格式是否为单个汉字
/// </summary>
/// <param name="str">需要判断的单个汉字字符串</param>
/// <returns>方法返回布尔值</returns>
public static bool IsChinese(this string str)
{
return Regex.IsMatch(str, RegexPatterns.ChinesePattern);
}
/// <summary>
/// 判断字符串格式是否为url
/// </summary>
/// <param name="str">需要判断的url字符串</param>
/// <returns>方法返回布尔值</returns>
public static bool IsUrl(this string str)
{
return Regex.IsMatch(str, RegexPatterns.UrlPattern);
}
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,
RegexPatterns.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, RegexPatterns.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)
{
return ipType switch
{
IpType.A => Regex.IsMatch(ip, RegexPatterns.IpAPattern),
IpType.B => Regex.IsMatch(ip, RegexPatterns.IpBPattern),
IpType.C => Regex.IsMatch(ip, RegexPatterns.IpCPattern),
IpType.D => Regex.IsMatch(ip, RegexPatterns.IpDPattern),
IpType.E => Regex.IsMatch(ip, RegexPatterns.IpEPattern),
_ => false
};
}
/// <summary>
/// 判断字符串格式是否为IP地址
/// </summary>
/// <param name="ip">需要判断的IP字符串</param>
/// <returns>方法返回布尔值</returns>
public static bool IsIp(this string ip)
{
return Regex.IsMatch(ip, RegexPatterns.IpPattern);
}
/// <summary>
/// 判断字符串格式是否为单个汉字
/// </summary>
/// <param name="str">需要判断的单个汉字字符串</param>
/// <returns>方法返回布尔值</returns>
public static bool IsChinese(this string str)
{
return Regex.IsMatch(str, RegexPatterns.ChinesePattern);
}
/// <summary>
/// 判断字符串格式是否为url
/// </summary>
/// <param name="str">需要判断的url字符串</param>
/// <returns>方法返回布尔值</returns>
public static bool IsUrl(this string str)
{
return Regex.IsMatch(str, RegexPatterns.UrlPattern);
}
}
@@ -1,151 +1,150 @@
namespace HandyControl.Tools
namespace HandyControl.Tools;
/// <summary>
/// 包含一些正则验证所需要的字符串
/// </summary>
public sealed class RegexPatterns
{
/// <summary>
/// 包含一些正则验证所需要的字符串
/// 邮件正则匹配表达式
/// </summary>
public sealed class RegexPatterns
{
/// <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})(\]?)$";
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>
/// 手机号正则匹配表达式
/// </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>
/// 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>
/// 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>
/// 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>
/// 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>
/// 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>
/// 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>
/// 汉字正则匹配
/// </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>
/// 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 NumberPattern = @"^\d+$";
/// <summary>
/// 计算性质数字正则匹配
/// </summary>
public const string DigitsPattern = @"[+-]?\d*\.?\d+(?:\.\d+)?(?:[eE][+-]?\d+)?";
/// <summary>
/// 计算性质数字正则匹配
/// </summary>
public const string DigitsPattern = @"[+-]?\d*\.?\d+(?:\.\d+)?(?:[eE][+-]?\d+)?";
/// <summary>
/// 正整数正则匹配
/// </summary>
public const string PIntPattern = @"^[1-9]\d*$";
/// <summary>
/// 正整数正则匹配
/// </summary>
public const string PIntPattern = @"^[1-9]\d*$";
/// <summary>
/// 负整数正则匹配
/// </summary>
public const string NIntPattern = @"^-[1-9]\d*$ ";
/// <summary>
/// 负整数正则匹配
/// </summary>
public const string NIntPattern = @"^-[1-9]\d*$ ";
/// <summary>
/// 整数正则匹配
/// </summary>
public const string IntPattern = @"^-?[1-9]\d*|0$";
/// <summary>
/// 整数正则匹配
/// </summary>
public const string IntPattern = @"^-?[1-9]\d*|0$";
/// <summary>
/// 非负整数正则匹配
/// </summary>
public const string NnIntPattern = @"^[1-9]\d*|0$";
/// <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 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 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 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 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 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>
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);
}
}
/// <summary>
/// 根据属性名称使用反射来获取值
/// </summary>
/// <param name="propertyName"></param>
/// <returns></returns>
public object GetValue(string propertyName) => GetType().GetField(propertyName).GetValue(null);
}
@@ -6,117 +6,116 @@ using System.Windows.Controls.Primitives;
using HandyControl.Controls;
using HandyControl.Data;
namespace HandyControl.Tools
namespace HandyControl.Tools;
public class ButtonGroupItemStyleSelector : StyleSelector
{
public class ButtonGroupItemStyleSelector : StyleSelector
private static readonly Dictionary<string, Style> StyleDict = new()
{
private static readonly Dictionary<string, Style> StyleDict = new()
[ResourceToken.RadioGroupItemSingle] = ResourceHelper.GetResourceInternal<Style>(ResourceToken.RadioGroupItemSingle),
[ResourceToken.RadioGroupItemHorizontalFirst] = ResourceHelper.GetResourceInternal<Style>(ResourceToken.RadioGroupItemHorizontalFirst),
[ResourceToken.RadioGroupItemHorizontalLast] = ResourceHelper.GetResourceInternal<Style>(ResourceToken.RadioGroupItemHorizontalLast),
[ResourceToken.RadioGroupItemVerticalFirst] = ResourceHelper.GetResourceInternal<Style>(ResourceToken.RadioGroupItemVerticalFirst),
[ResourceToken.RadioGroupItemVerticalLast] = ResourceHelper.GetResourceInternal<Style>(ResourceToken.RadioGroupItemVerticalLast),
[ResourceToken.RadioGroupItemDefault] = ResourceHelper.GetResourceInternal<Style>(ResourceToken.RadioGroupItemDefault),
[ResourceToken.ButtonGroupItemSingle] = ResourceHelper.GetResourceInternal<Style>(ResourceToken.ButtonGroupItemSingle),
[ResourceToken.ButtonGroupItemHorizontalFirst] = ResourceHelper.GetResourceInternal<Style>(ResourceToken.ButtonGroupItemHorizontalFirst),
[ResourceToken.ButtonGroupItemHorizontalLast] = ResourceHelper.GetResourceInternal<Style>(ResourceToken.ButtonGroupItemHorizontalLast),
[ResourceToken.ButtonGroupItemVerticalFirst] = ResourceHelper.GetResourceInternal<Style>(ResourceToken.ButtonGroupItemVerticalFirst),
[ResourceToken.ButtonGroupItemVerticalLast] = ResourceHelper.GetResourceInternal<Style>(ResourceToken.ButtonGroupItemVerticalLast),
[ResourceToken.ButtonGroupItemDefault] = ResourceHelper.GetResourceInternal<Style>(ResourceToken.ButtonGroupItemDefault),
[ResourceToken.ToggleButtonGroupItemSingle] = ResourceHelper.GetResourceInternal<Style>(ResourceToken.ToggleButtonGroupItemSingle),
[ResourceToken.ToggleButtonGroupItemHorizontalFirst] = ResourceHelper.GetResourceInternal<Style>(ResourceToken.ToggleButtonGroupItemHorizontalFirst),
[ResourceToken.ToggleButtonGroupItemHorizontalLast] = ResourceHelper.GetResourceInternal<Style>(ResourceToken.ToggleButtonGroupItemHorizontalLast),
[ResourceToken.ToggleButtonGroupItemVerticalFirst] = ResourceHelper.GetResourceInternal<Style>(ResourceToken.ToggleButtonGroupItemVerticalFirst),
[ResourceToken.ToggleButtonGroupItemVerticalLast] = ResourceHelper.GetResourceInternal<Style>(ResourceToken.ToggleButtonGroupItemVerticalLast),
[ResourceToken.ToggleButtonGroupItemDefault] = ResourceHelper.GetResourceInternal<Style>(ResourceToken.ToggleButtonGroupItemDefault)
};
public override Style SelectStyle(object item, DependencyObject container)
{
if (container is ButtonGroup buttonGroup && item is ButtonBase buttonBase)
{
[ResourceToken.RadioGroupItemSingle] = ResourceHelper.GetResourceInternal<Style>(ResourceToken.RadioGroupItemSingle),
[ResourceToken.RadioGroupItemHorizontalFirst] = ResourceHelper.GetResourceInternal<Style>(ResourceToken.RadioGroupItemHorizontalFirst),
[ResourceToken.RadioGroupItemHorizontalLast] = ResourceHelper.GetResourceInternal<Style>(ResourceToken.RadioGroupItemHorizontalLast),
[ResourceToken.RadioGroupItemVerticalFirst] = ResourceHelper.GetResourceInternal<Style>(ResourceToken.RadioGroupItemVerticalFirst),
[ResourceToken.RadioGroupItemVerticalLast] = ResourceHelper.GetResourceInternal<Style>(ResourceToken.RadioGroupItemVerticalLast),
[ResourceToken.RadioGroupItemDefault] = ResourceHelper.GetResourceInternal<Style>(ResourceToken.RadioGroupItemDefault),
var count = GetVisibleButtonsCount(buttonGroup);
[ResourceToken.ButtonGroupItemSingle] = ResourceHelper.GetResourceInternal<Style>(ResourceToken.ButtonGroupItemSingle),
[ResourceToken.ButtonGroupItemHorizontalFirst] = ResourceHelper.GetResourceInternal<Style>(ResourceToken.ButtonGroupItemHorizontalFirst),
[ResourceToken.ButtonGroupItemHorizontalLast] = ResourceHelper.GetResourceInternal<Style>(ResourceToken.ButtonGroupItemHorizontalLast),
[ResourceToken.ButtonGroupItemVerticalFirst] = ResourceHelper.GetResourceInternal<Style>(ResourceToken.ButtonGroupItemVerticalFirst),
[ResourceToken.ButtonGroupItemVerticalLast] = ResourceHelper.GetResourceInternal<Style>(ResourceToken.ButtonGroupItemVerticalLast),
[ResourceToken.ButtonGroupItemDefault] = ResourceHelper.GetResourceInternal<Style>(ResourceToken.ButtonGroupItemDefault),
[ResourceToken.ToggleButtonGroupItemSingle] = ResourceHelper.GetResourceInternal<Style>(ResourceToken.ToggleButtonGroupItemSingle),
[ResourceToken.ToggleButtonGroupItemHorizontalFirst] = ResourceHelper.GetResourceInternal<Style>(ResourceToken.ToggleButtonGroupItemHorizontalFirst),
[ResourceToken.ToggleButtonGroupItemHorizontalLast] = ResourceHelper.GetResourceInternal<Style>(ResourceToken.ToggleButtonGroupItemHorizontalLast),
[ResourceToken.ToggleButtonGroupItemVerticalFirst] = ResourceHelper.GetResourceInternal<Style>(ResourceToken.ToggleButtonGroupItemVerticalFirst),
[ResourceToken.ToggleButtonGroupItemVerticalLast] = ResourceHelper.GetResourceInternal<Style>(ResourceToken.ToggleButtonGroupItemVerticalLast),
[ResourceToken.ToggleButtonGroupItemDefault] = ResourceHelper.GetResourceInternal<Style>(ResourceToken.ToggleButtonGroupItemDefault)
};
public override Style SelectStyle(object item, DependencyObject container)
{
if (container is ButtonGroup buttonGroup && item is ButtonBase buttonBase)
switch (buttonBase)
{
var count = GetVisibleButtonsCount(buttonGroup);
switch (buttonBase)
{
case RadioButton: return GetRadioButtonStyle(count, buttonGroup, buttonBase);
case Button: return GetButtonStyle(count, buttonGroup, buttonBase);
case ToggleButton: return GetToggleButtonStyle(count, buttonGroup, buttonBase);
}
case RadioButton: return GetRadioButtonStyle(count, buttonGroup, buttonBase);
case Button: return GetButtonStyle(count, buttonGroup, buttonBase);
case ToggleButton: return GetToggleButtonStyle(count, buttonGroup, buttonBase);
}
return null;
}
private static int GetVisibleButtonsCount(ButtonGroup buttonGroup)
{
return buttonGroup.Items.OfType<ButtonBase>().Count(button => button.IsVisible);
}
private static Style GetToggleButtonStyle(int count, ButtonGroup buttonGroup, ButtonBase button)
{
if (count == 1)
{
return StyleDict[ResourceToken.ToggleButtonGroupItemSingle];
}
var index = buttonGroup.Items.IndexOf(button);
return buttonGroup.Orientation == Orientation.Horizontal
? index == 0
? StyleDict[ResourceToken.ToggleButtonGroupItemHorizontalFirst]
: StyleDict[index == count - 1
? ResourceToken.ToggleButtonGroupItemHorizontalLast
: ResourceToken.ToggleButtonGroupItemDefault]
: index == 0
? StyleDict[ResourceToken.ToggleButtonGroupItemVerticalFirst]
: StyleDict[index == count - 1
? ResourceToken.ToggleButtonGroupItemVerticalLast
: ResourceToken.ToggleButtonGroupItemDefault];
}
private static Style GetButtonStyle(int count, ButtonGroup buttonGroup, ButtonBase button)
{
if (count == 1)
{
return StyleDict[ResourceToken.ButtonGroupItemSingle];
}
var index = buttonGroup.Items.IndexOf(button);
return buttonGroup.Orientation == Orientation.Horizontal
? index == 0
? StyleDict[ResourceToken.ButtonGroupItemHorizontalFirst]
: StyleDict[index == count - 1
? ResourceToken.ButtonGroupItemHorizontalLast
: ResourceToken.ButtonGroupItemDefault]
: index == 0
? StyleDict[ResourceToken.ButtonGroupItemVerticalFirst]
: StyleDict[index == count - 1
? ResourceToken.ButtonGroupItemVerticalLast
: ResourceToken.ButtonGroupItemDefault];
}
private static Style GetRadioButtonStyle(int count, ButtonGroup buttonGroup, ButtonBase button)
{
if (count == 1)
{
return StyleDict[ResourceToken.RadioGroupItemSingle];
}
var index = buttonGroup.Items.IndexOf(button);
return buttonGroup.Orientation == Orientation.Horizontal
? index == 0
? StyleDict[ResourceToken.RadioGroupItemHorizontalFirst]
: StyleDict[index == count - 1
? ResourceToken.RadioGroupItemHorizontalLast
: ResourceToken.RadioGroupItemDefault]
: index == 0
? StyleDict[ResourceToken.RadioGroupItemVerticalFirst]
: StyleDict[index == count - 1
? ResourceToken.RadioGroupItemVerticalLast
: ResourceToken.RadioGroupItemDefault];
}
return null;
}
}
private static int GetVisibleButtonsCount(ButtonGroup buttonGroup)
{
return buttonGroup.Items.OfType<ButtonBase>().Count(button => button.IsVisible);
}
private static Style GetToggleButtonStyle(int count, ButtonGroup buttonGroup, ButtonBase button)
{
if (count == 1)
{
return StyleDict[ResourceToken.ToggleButtonGroupItemSingle];
}
var index = buttonGroup.Items.IndexOf(button);
return buttonGroup.Orientation == Orientation.Horizontal
? index == 0
? StyleDict[ResourceToken.ToggleButtonGroupItemHorizontalFirst]
: StyleDict[index == count - 1
? ResourceToken.ToggleButtonGroupItemHorizontalLast
: ResourceToken.ToggleButtonGroupItemDefault]
: index == 0
? StyleDict[ResourceToken.ToggleButtonGroupItemVerticalFirst]
: StyleDict[index == count - 1
? ResourceToken.ToggleButtonGroupItemVerticalLast
: ResourceToken.ToggleButtonGroupItemDefault];
}
private static Style GetButtonStyle(int count, ButtonGroup buttonGroup, ButtonBase button)
{
if (count == 1)
{
return StyleDict[ResourceToken.ButtonGroupItemSingle];
}
var index = buttonGroup.Items.IndexOf(button);
return buttonGroup.Orientation == Orientation.Horizontal
? index == 0
? StyleDict[ResourceToken.ButtonGroupItemHorizontalFirst]
: StyleDict[index == count - 1
? ResourceToken.ButtonGroupItemHorizontalLast
: ResourceToken.ButtonGroupItemDefault]
: index == 0
? StyleDict[ResourceToken.ButtonGroupItemVerticalFirst]
: StyleDict[index == count - 1
? ResourceToken.ButtonGroupItemVerticalLast
: ResourceToken.ButtonGroupItemDefault];
}
private static Style GetRadioButtonStyle(int count, ButtonGroup buttonGroup, ButtonBase button)
{
if (count == 1)
{
return StyleDict[ResourceToken.RadioGroupItemSingle];
}
var index = buttonGroup.Items.IndexOf(button);
return buttonGroup.Orientation == Orientation.Horizontal
? index == 0
? StyleDict[ResourceToken.RadioGroupItemHorizontalFirst]
: StyleDict[index == count - 1
? ResourceToken.RadioGroupItemHorizontalLast
: ResourceToken.RadioGroupItemDefault]
: index == 0
? StyleDict[ResourceToken.RadioGroupItemVerticalFirst]
: StyleDict[index == count - 1
? ResourceToken.RadioGroupItemVerticalLast
: ResourceToken.RadioGroupItemDefault];
}
}
@@ -2,29 +2,28 @@
using System.Windows.Controls;
using HandyControl.Data;
namespace HandyControl.Tools
{
public class ComboBoxItemCapsuleStyleSelector : StyleSelector
{
public override Style SelectStyle(object item, DependencyObject container)
{
if (container is ComboBoxItem comboBoxItem && VisualHelper.GetParent<ComboBox>(comboBoxItem) is { } comboBox)
{
var count = comboBox.Items.Count;
if (count == 1)
{
return ResourceHelper.GetResourceInternal<Style>(ResourceToken.ComboBoxItemCapsuleSingle);
}
namespace HandyControl.Tools;
var index = comboBox.ItemContainerGenerator.IndexFromContainer(comboBoxItem);
return index == 0
? ResourceHelper.GetResourceInternal<Style>(ResourceToken.ComboBoxItemCapsuleHorizontalFirst)
: ResourceHelper.GetResourceInternal<Style>(index == count - 1
? ResourceToken.ComboBoxItemCapsuleHorizontalLast
: ResourceToken.ComboBoxItemCapsuleDefault);
public class ComboBoxItemCapsuleStyleSelector : StyleSelector
{
public override Style SelectStyle(object item, DependencyObject container)
{
if (container is ComboBoxItem comboBoxItem && VisualHelper.GetParent<ComboBox>(comboBoxItem) is { } comboBox)
{
var count = comboBox.Items.Count;
if (count == 1)
{
return ResourceHelper.GetResourceInternal<Style>(ResourceToken.ComboBoxItemCapsuleSingle);
}
return null;
var index = comboBox.ItemContainerGenerator.IndexFromContainer(comboBoxItem);
return index == 0
? ResourceHelper.GetResourceInternal<Style>(ResourceToken.ComboBoxItemCapsuleHorizontalFirst)
: ResourceHelper.GetResourceInternal<Style>(index == count - 1
? ResourceToken.ComboBoxItemCapsuleHorizontalLast
: ResourceToken.ComboBoxItemCapsuleDefault);
}
return null;
}
}
}
@@ -2,34 +2,33 @@
using System.Windows.Controls;
using HandyControl.Data;
namespace HandyControl.Tools
{
public class TabItemCapsuleStyleSelector : StyleSelector
{
public override Style SelectStyle(object item, DependencyObject container)
{
if (container is TabItem tabItem && VisualHelper.GetParent<TabControl>(tabItem) is { } tabControl)
{
var count = tabControl.Items.Count;
if (count == 1)
{
return ResourceHelper.GetResourceInternal<Style>(ResourceToken.TabItemCapsuleSingle);
}
namespace HandyControl.Tools;
var index = tabControl.ItemContainerGenerator.IndexFromContainer(tabItem);
return index == 0
? ResourceHelper.GetResourceInternal<Style>(
tabControl.TabStripPlacement == Dock.Top || tabControl.TabStripPlacement == Dock.Bottom
? ResourceToken.TabItemCapsuleHorizontalFirst
: ResourceToken.TabItemCapsuleVerticalFirst)
: ResourceHelper.GetResourceInternal<Style>(index == count - 1
? tabControl.TabStripPlacement == Dock.Top || tabControl.TabStripPlacement == Dock.Bottom
? ResourceToken.TabItemCapsuleHorizontalLast
: ResourceToken.TabItemCapsuleVerticalLast
: ResourceToken.TabItemCapsuleDefault);
public class TabItemCapsuleStyleSelector : StyleSelector
{
public override Style SelectStyle(object item, DependencyObject container)
{
if (container is TabItem tabItem && VisualHelper.GetParent<TabControl>(tabItem) is { } tabControl)
{
var count = tabControl.Items.Count;
if (count == 1)
{
return ResourceHelper.GetResourceInternal<Style>(ResourceToken.TabItemCapsuleSingle);
}
return null;
var index = tabControl.ItemContainerGenerator.IndexFromContainer(tabItem);
return index == 0
? ResourceHelper.GetResourceInternal<Style>(
tabControl.TabStripPlacement == Dock.Top || tabControl.TabStripPlacement == Dock.Bottom
? ResourceToken.TabItemCapsuleHorizontalFirst
: ResourceToken.TabItemCapsuleVerticalFirst)
: ResourceHelper.GetResourceInternal<Style>(index == count - 1
? tabControl.TabStripPlacement == Dock.Top || tabControl.TabStripPlacement == Dock.Bottom
? ResourceToken.TabItemCapsuleHorizontalLast
: ResourceToken.TabItemCapsuleVerticalLast
: ResourceToken.TabItemCapsuleDefault);
}
return null;
}
}
}