chore: use file-scoped namespace
This commit is contained in:
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();
|
||||
}
|
||||
}
|
||||
+32
-33
@@ -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);
|
||||
}
|
||||
+104
-105
@@ -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];
|
||||
}
|
||||
}
|
||||
+20
-21
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user