support for .NET framework 3.5

Still have bugs
This commit is contained in:
NaBian committed 2019-01-21 00:43:10 +08:00
1 parent 5878039e21
commit 7d6b491f25
704 files changed
+48273 -33

No files matched your search

@@ -0,0 +1,27 @@
using System;
using System.Globalization;
using System.Windows.Data;
namespace HandyControl.Tools.Converter
{
public class Boolean2BooleanReConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is bool boolValue)
{
return !boolValue;
}
return value;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is bool boolValue)
{
return !boolValue;
}
return value;
}
}
}
@@ -0,0 +1,32 @@
using System;
using System.Globalization;
using System.Windows.Data;
namespace HandyControl.Tools.Converter
{
public class Boolean2StrConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is bool boolValue)
{
if (parameter is string str)
{
var arr = str.Split(';');
if (arr.Length > 1)
{
return boolValue ? arr[1] : arr[0];
}
return "";
}
return "";
}
return "";
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
}
@@ -0,0 +1,20 @@
using System;
using System.Globalization;
using System.Windows;
using System.Windows.Data;
namespace HandyControl.Tools.Converter
{
public class Boolean2VisibilityReConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return value != null && (bool)value ? Visibility.Collapsed : Visibility.Visible;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
}
@@ -0,0 +1,39 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Windows;
using System.Windows.Data;
namespace HandyControl.Tools.Converter
{
public class BooleanArr2VisibilityConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
if (values == null)
{
return Visibility.Collapsed;
}
var arr = new List<bool>();
foreach (var item in values)
{
if (item is bool boolValue)
{
arr.Add(boolValue);
}
else
{
return Visibility.Collapsed;
}
}
return arr.All(item => item) ? Visibility.Visible : Visibility.Collapsed;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
}
@@ -0,0 +1,39 @@
using System;
using System.Globalization;
using System.Windows.Data;
using System.Windows.Media;
namespace HandyControl.Tools.Converter
{
public class Color2ChannelAConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is SolidColorBrush brush)
{
return brush.Color.A;
}
return 0;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is string str)
{
try
{
if (ColorConverter.ConvertFromString(str) is Color color)
{
return new SolidColorBrush(color);
}
}
catch
{
return parameter;
}
return parameter;
}
return parameter;
}
}
}
@@ -0,0 +1,40 @@
using System;
using System.Globalization;
using System.Windows.Data;
using System.Windows.Media;
namespace HandyControl.Tools.Converter
{
public class Color2HexStrConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is SolidColorBrush brush)
{
var color = brush.Color;
return color.A < 255 ? $"#{color.A:X2}{color.R:X2}{color.G:X2}{color.B:X2}" : $"#{color.R:X2}{color.G:X2}{color.B:X2}";
}
return "";
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is string str)
{
try
{
if (ColorConverter.ConvertFromString(str) is Color color)
{
return new SolidColorBrush(color);
}
}
catch
{
return new SolidColorBrush(default(Color));
}
return new SolidColorBrush(default(Color));
}
return new SolidColorBrush(default(Color));
}
}
}
@@ -0,0 +1,35 @@
using System;
using System.Globalization;
using System.Windows.Data;
namespace HandyControl.Tools.Converter
{
public class Int2StrConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is int intValue)
{
if (parameter is string str)
{
var arr = str.Split(';');
if (arr.Length > intValue)
{
return arr[intValue];
}
return intValue;
}
return intValue;
}
return value;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
}
@@ -0,0 +1,52 @@
using System;
using System.Globalization;
using System.Windows.Data;
namespace HandyControl.Tools.Converter
{
public class Long2FileSizeConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value == null) return Properties.Langs.Lang.UnknownSize;
if (value is long intValue)
{
if (intValue < 0)
{
return Properties.Langs.Lang.UnknownSize;
}
if (intValue < 1024)
{
return $"{intValue} B";
}
if (intValue < 1048576)
{
return $"{intValue / 1024.0:0.00} KB";
}
if (intValue < 1073741824)
{
return $"{intValue / 1048576.0:0.00} MB";
}
if (intValue < 1099511627776)
{
return $"{intValue / 1073741824.0:0.00} GB";
}
if (intValue < 1125899906842624)
{
return $"{intValue / 1099511627776.0:0.00} TB";
}
if (intValue < 1152921504606847000)
{
return $"{intValue / 1125899906842624.0:0.00} PB";
}
return Properties.Langs.Lang.TooLarge;
}
return Properties.Langs.Lang.Unknown;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
}
@@ -0,0 +1,16 @@
using System;
using System.Globalization;
using System.Windows.Data;
namespace HandyControl.Tools.Converter
{
public class Object2BooleanConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture) => !(value is null);
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
}
@@ -0,0 +1,116 @@
using System;
using System.Globalization;
using System.Windows;
using System.Windows.Data;
using System.Windows.Media;
using System.Windows.Media.Animation;
namespace HandyControl.Tools.Converter
{
/// <summary>
/// The ProgressBarHighlightConverter class
/// </summary>
public class ProgressBarHighlightConverter : IMultiValueConverter
{
/// <summary>
/// Creates the brush for the ProgressBar
/// </summary>
/// <param name="values">ForegroundBrush, IsIndeterminate, Width, Height</param>
/// <param name="targetType"></param>
/// <param name="parameter"></param>
/// <param name="culture"></param>
/// <returns>Brush for the ProgressBar</returns>
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
//
// Parameter Validation
//
var doubleType = typeof(double);
if (values == null ||
values.Length != 3 ||
values[0] == null ||
values[1] == null ||
values[2] == null ||
!(values[0] is Brush) ||
!doubleType.IsInstanceOfType(values[1]) ||
!doubleType.IsInstanceOfType(values[2]))
return null;
//
// Conversion
//
var brush = (Brush) values[0];
var width = (double) values[1];
var height = (double) values[2];
// if an invalid height, return a null brush
if (width <= 0.0 || double.IsInfinity(width) || double.IsNaN(width) ||
height <= 0.0 || double.IsInfinity(height) || double.IsNaN(height))
return null;
var newBrush = new DrawingBrush();
// Create a Drawing Brush that is 2x longer than progress bar track
//
// +-------------+..............
// | highlight | empty :
// +-------------+.............:
//
// This brush will animate to the right.
var twiceWidth = width * 2.0;
// Set the viewport and viewbox to the 2*size of the progress region
newBrush.Viewport = newBrush.Viewbox = new Rect(-width, 0, twiceWidth, height);
newBrush.ViewportUnits = newBrush.ViewboxUnits = BrushMappingMode.Absolute;
newBrush.TileMode = TileMode.None;
newBrush.Stretch = Stretch.None;
var myDrawing = new DrawingGroup();
var myDrawingContext = myDrawing.Open();
// Draw the highlight
myDrawingContext.DrawRectangle(brush, null, new Rect(-width, 0, width, height));
// Animate the Translation
var translateTime = TimeSpan.FromSeconds(twiceWidth / 200.0); // travel at 200px /second
var pauseTime = TimeSpan.FromSeconds(1.0); // pause 1 second between animations
var animation = new DoubleAnimationUsingKeyFrames
{
BeginTime = TimeSpan.Zero,
Duration = new Duration(translateTime + pauseTime),
RepeatBehavior = RepeatBehavior.Forever
};
animation.KeyFrames.Add(new LinearDoubleKeyFrame(twiceWidth, translateTime));
var translation = new TranslateTransform();
// Set the animation to the XProperty
translation.BeginAnimation(TranslateTransform.XProperty, animation);
// Set the animated translation on the brush
newBrush.Transform = translation;
myDrawingContext.Close();
newBrush.Drawing = myDrawing;
return newBrush;
}
/// <summary>
/// Not Supported
/// </summary>
/// <param name="value">value, as produced by target</param>
/// <param name="targetTypes">target types</param>
/// <param name="parameter">converter parameter</param>
/// <param name="culture">culture information</param>
/// <returns>Nothing</returns>
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) => null;
}
}
@@ -0,0 +1,20 @@
using System;
using System.Globalization;
using System.Windows;
using System.Windows.Data;
namespace HandyControl.Tools.Converter
{
public class Source2ImageConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return value != null ? new Uri(value.ToString(), UriKind.Relative) : null;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return value != null && (Visibility)value == Visibility.Visible;
}
}
}
@@ -0,0 +1,20 @@
using System;
using System.Globalization;
using System.Windows;
using System.Windows.Data;
namespace HandyControl.Tools.Converter
{
public class String2VisibilityConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return string.IsNullOrEmpty((string)value) ? Visibility.Collapsed : Visibility.Visible;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return value != null && (Visibility)value == Visibility.Collapsed;
}
}
}
@@ -0,0 +1,20 @@
using System;
using System.Globalization;
using System.Windows;
using System.Windows.Data;
namespace HandyControl.Tools.Converter
{
public class String2VisibilityReConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return string.IsNullOrEmpty((string)value) ? Visibility.Visible : Visibility.Collapsed;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return value != null && (Visibility)value == Visibility.Collapsed;
}
}
}
@@ -0,0 +1,29 @@
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Media;
namespace HandyControl.Tools.Converter
{
public class TreeViewItemMarginConverter: IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
var left = 0.0;
UIElement element = value as TreeViewItem;
while (element != null && element.GetType() != typeof(TreeView))
{
element = (UIElement)VisualTreeHelper.GetParent(element);
if (element is TreeViewItem)
left += 19.0;
}
return new Thickness(left, 0, 0, 0);
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
}
@@ -0,0 +1,33 @@
using System.Collections.Generic;
using System.Windows.Media;
namespace HandyControl.Tools.Extension
{
/// <summary>
/// 颜色扩展类
/// </summary>
public static class ColorExtension
{
/// <summary>
/// 将颜色转换为10进制表示(rgb顺序颠倒)
/// </summary>
/// <param name="color"></param>
/// <returns></returns>
public static int ToInt32(this Color color) => color.R << 16 | color.G << 8 | color.B;
/// <summary>
/// 将颜色转换为10进制表示(rgb顺序颠倒)
/// </summary>
/// <param name="color"></param>
/// <returns></returns>
public static int ToInt32Reverse(this Color color) => color.R | color.G << 8 | color.B << 18;
internal static List<byte> ToList(this Color color) => new List<byte>
{
color.R,
color.G,
color.B
};
}
}
@@ -0,0 +1,45 @@
using System.Windows;
namespace HandyControl.Tools.Extension
{
// ReSharper disable once InconsistentNaming
public static class UIElementExtension
{
/// <summary>
/// 显示元素
/// </summary>
/// <param name="element"></param>
public static void Show(this UIElement element)
{
element.Visibility = Visibility.Visible;
}
/// <summary>
/// 显示元素
/// </summary>
/// <param name="element"></param>
/// <param name="show"></param>
public static void Show(this UIElement element, bool show)
{
element.Visibility = show ? Visibility.Visible : Visibility.Collapsed;
}
/// <summary>
/// 不现实元素,但保留空间
/// </summary>
/// <param name="element"></param>
public static void Hide(this UIElement element)
{
element.Visibility = Visibility.Hidden;
}
/// <summary>
/// 不显示元素,且不保留空间
/// </summary>
/// <param name="element"></param>
public static void Collapse(this UIElement element)
{
element.Visibility = Visibility.Collapsed;
}
}
}
@@ -0,0 +1,271 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Threading;
using HandyControl.Controls;
namespace HandyControl.Data
{
internal class ImageAnimator
{
private static List<GifImageInfo> ImageInfoList;
private static readonly ReaderWriterLock RwImgListLock = new ReaderWriterLock();
private static Thread AnimationThread;
private static bool AnyFrameDirty;
[ThreadStatic]
private static int ThreadWriterLockWaitCount;
public static bool CanAnimate(GifImage image)
{
if (image == null)
{
return false;
}
lock (image)
{
var dimensions = image.FrameDimensionsList;
if (dimensions.Select(guid => new GifFrameDimension(guid)).Contains(GifFrameDimension.Time))
{
return image.GetFrameCount(GifFrameDimension.Time) > 1;
}
}
return false;
}
public static void Animate(GifImage image, EventHandler onFrameChangedHandler)
{
if (image == null)
{
return;
}
GifImageInfo imageInfo;
// See comment in the class header about locking the image ref.
lock (image)
{
// could we avoid creating an ImageInfo object if FrameCount == 1 ?
imageInfo = new GifImageInfo(image);
}
// If the image is already animating, stop animating it
StopAnimate(image, onFrameChangedHandler);
// Acquire a writer lock to modify the image info list. If the thread has a reader lock we need to upgrade
// it to a writer lock; acquiring a reader lock in this case would block the thread on itself.
// If the thread already has a writer lock its ref count will be incremented w/o placing the request in the
// writer queue. See ReaderWriterLock.AcquireWriterLock method in the MSDN.
var readerLockHeld = RwImgListLock.IsReaderLockHeld;
var lockDowngradeCookie = new LockCookie();
ThreadWriterLockWaitCount++;
try
{
if (readerLockHeld)
{
lockDowngradeCookie = RwImgListLock.UpgradeToWriterLock(Timeout.Infinite);
}
else
{
RwImgListLock.AcquireWriterLock(Timeout.Infinite);
}
}
finally
{
ThreadWriterLockWaitCount--;
}
try
{
if (imageInfo.Animated)
{
// Construct the image array
//
if (ImageInfoList == null)
{
ImageInfoList = new List<GifImageInfo>();
}
// Add the new image
//
imageInfo.FrameChangedHandler = onFrameChangedHandler;
ImageInfoList.Add(imageInfo);
// Construct a new timer thread if we haven't already
//
if (AnimationThread == null)
{
AnimationThread = new Thread(AnimateImages50Ms)
{
Name = typeof(ImageAnimator).Name,
IsBackground = true
};
AnimationThread.Start();
}
}
}
finally
{
if (readerLockHeld)
{
RwImgListLock.DowngradeFromWriterLock(ref lockDowngradeCookie);
}
else
{
RwImgListLock.ReleaseWriterLock();
}
}
}
public static void StopAnimate(GifImage image, EventHandler onFrameChangedHandler)
{
// Make sure we have a list of images
if (image == null || ImageInfoList == null)
{
return;
}
// Acquire a writer lock to modify the image info list - See comments on Animate() about this locking.
var readerLockHeld = RwImgListLock.IsReaderLockHeld;
var lockDowngradeCookie = new LockCookie();
ThreadWriterLockWaitCount++;
try
{
if (readerLockHeld)
{
lockDowngradeCookie = RwImgListLock.UpgradeToWriterLock(Timeout.Infinite);
}
else
{
RwImgListLock.AcquireWriterLock(Timeout.Infinite);
}
}
finally
{
ThreadWriterLockWaitCount--;
}
try
{
// Find the corresponding reference and remove it
for (int i = 0; i < ImageInfoList.Count; i++)
{
var imageInfo = ImageInfoList[i];
if (Equals(image, imageInfo.Image))
{
if ((onFrameChangedHandler == imageInfo.FrameChangedHandler) || (onFrameChangedHandler != null && onFrameChangedHandler.Equals(imageInfo.FrameChangedHandler)))
{
ImageInfoList.Remove(imageInfo);
}
break;
}
}
}
finally
{
if (readerLockHeld)
{
RwImgListLock.DowngradeFromWriterLock(ref lockDowngradeCookie);
}
else
{
RwImgListLock.ReleaseWriterLock();
}
}
}
public static void UpdateFrames()
{
if (!AnyFrameDirty || ImageInfoList == null)
{
return;
}
if (ThreadWriterLockWaitCount > 0)
{
// Cannot acquire reader lock at this time, frames update will be missed.
return;
}
RwImgListLock.AcquireReaderLock(Timeout.Infinite);
try
{
foreach (var imageInfo in ImageInfoList)
{
// See comment in the class header about locking the image ref.
lock (imageInfo.Image)
{
imageInfo.UpdateFrame();
}
}
AnyFrameDirty = false;
}
finally
{
RwImgListLock.ReleaseReaderLock();
}
}
[SuppressMessage("Microsoft.Performance", "CA1804:RemoveUnusedLocals")]
private static void AnimateImages50Ms()
{
while (true)
{
// Acquire reader-lock to access imageInfoList, elemens in the list can be modified w/o needing a writer-lock.
// Observe that we don't need to check if the thread is waiting or a writer lock here since the thread this
// method runs in never acquires a writer lock.
RwImgListLock.AcquireReaderLock(Timeout.Infinite);
try
{
foreach (var imageInfo in ImageInfoList)
{
// Frame delay is measured in 1/100ths of a second. This thread
// sleeps for 50 ms = 5/100ths of a second between frame updates,
// so we increase the frame delay count 5/100ths of a second
// at a time.
//
imageInfo.FrameTimer += 5;
if (imageInfo.FrameTimer >= imageInfo.FrameDelay(imageInfo.Frame))
{
imageInfo.FrameTimer = 0;
if (imageInfo.Frame + 1 < imageInfo.FrameCount)
{
imageInfo.Frame++;
}
else
{
imageInfo.Frame = 0;
}
if (imageInfo.FrameDirty)
{
AnyFrameDirty = true;
}
}
}
}
finally
{
RwImgListLock.ReleaseReaderLock();
}
Thread.Sleep(50);
}
}
}
}
@@ -0,0 +1,34 @@
using System;
using System.Windows;
using System.Windows.Media.Animation;
namespace HandyControl.Tools
{
/// <summary>
/// 包含一些常用的动画辅助方法
/// </summary>
public class AnimationHelper
{
/// <summary>
/// 创建一个Thickness动画
/// </summary>
/// <param name="thickness"></param>
/// <param name="milliseconds"></param>
/// <returns></returns>
public static ThicknessAnimation CreateAnimation(Thickness thickness = default(Thickness), double milliseconds = 200)
{
return new ThicknessAnimation(thickness, new Duration(TimeSpan.FromMilliseconds(milliseconds)));
}
/// <summary>
/// 创建一个Double动画
/// </summary>
/// <param name="toValue"></param>
/// <param name="milliseconds"></param>
/// <returns></returns>
public static DoubleAnimation CreateAnimation(double toValue, double milliseconds = 200)
{
return new DoubleAnimation(toValue, new Duration(TimeSpan.FromMilliseconds(milliseconds)));
}
}
}
@@ -0,0 +1,152 @@
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Media.Media3D;
namespace HandyControl.Tools
{
/// <summary>
/// 包含内部使用的一些简单算法
/// </summary>
internal class ArithmeticHelper
{
/// <summary>
/// 平分一个整数到一个数组中
/// </summary>
/// <param name="num"></param>
/// <param name="count"></param>
/// <returns></returns>
public static int[] DivideInt2Arr(int num, int count)
{
var arr = new int[count];
var div = num / count;
var rest = num % count;
for (int i = 0; i < count; i++)
{
arr[i] = div;
}
for (int i = 0; i < rest; i++)
{
arr[i] += 1;
}
return arr;
}
/// <summary>
/// 计算控件在窗口中的可见坐标
/// </summary>
public static Point CalSafePoint(FrameworkElement element, FrameworkElement showElement, Thickness thickness = default(Thickness))
{
if (element == null || showElement == null) return default(Point);
var point = element.PointToScreen(new Point(0, 0));
if (point.X < 0) point.X = 0;
if (point.Y < 0) point.Y = 0;
var maxLeft = SystemParameters.WorkArea.Width -
((double.IsNaN(showElement.Width) ? showElement.ActualWidth : showElement.Width) +
thickness.Left + thickness.Right);
var maxTop = SystemParameters.WorkArea.Height -
((double.IsNaN(showElement.Height) ? showElement.ActualHeight : showElement.Height) +
thickness.Top + thickness.Bottom);
return new Point(maxLeft > point.X ? point.X : maxLeft, maxTop > point.Y ? point.Y : maxTop);
}
/// <summary>
/// 获取布局范围框
/// </summary>
/// <param name="element"></param>
/// <returns></returns>
public static Rect GetLayoutRect(FrameworkElement element)
{
var num1 = element.ActualWidth;
var num2 = element.ActualHeight;
if (element is Image || element is MediaElement)
if (element.Parent is Canvas)
{
num1 = double.IsNaN(element.Width) ? num1 : element.Width;
num2 = double.IsNaN(element.Height) ? num2 : element.Height;
}
else
{
num1 = element.RenderSize.Width;
num2 = element.RenderSize.Height;
}
var width = element.Visibility == Visibility.Collapsed ? 0.0 : num1;
var height = element.Visibility == Visibility.Collapsed ? 0.0 : num2;
var margin = element.Margin;
var layoutSlot = LayoutInformation.GetLayoutSlot(element);
var x = 0.0;
var y = 0.0;
switch (element.HorizontalAlignment)
{
case HorizontalAlignment.Left:
x = layoutSlot.Left + margin.Left;
break;
case HorizontalAlignment.Center:
x = (layoutSlot.Left + margin.Left + layoutSlot.Right - margin.Right) / 2.0 - width / 2.0;
break;
case HorizontalAlignment.Right:
x = layoutSlot.Right - margin.Right - width;
break;
case HorizontalAlignment.Stretch:
x = Math.Max(layoutSlot.Left + margin.Left,
(layoutSlot.Left + margin.Left + layoutSlot.Right - margin.Right) / 2.0 - width / 2.0);
break;
}
switch (element.VerticalAlignment)
{
case VerticalAlignment.Top:
y = layoutSlot.Top + margin.Top;
break;
case VerticalAlignment.Center:
y = (layoutSlot.Top + margin.Top + layoutSlot.Bottom - margin.Bottom) / 2.0 - height / 2.0;
break;
case VerticalAlignment.Bottom:
y = layoutSlot.Bottom - margin.Bottom - height;
break;
case VerticalAlignment.Stretch:
y = Math.Max(layoutSlot.Top + margin.Top,
(layoutSlot.Top + margin.Top + layoutSlot.Bottom - margin.Bottom) / 2.0 - height / 2.0);
break;
}
return new Rect(x, y, width, height);
}
/// <summary>
/// 计算两点的连线和x轴的夹角
/// </summary>
/// <param name="center"></param>
/// <param name="p"></param>
/// <returns></returns>
public static double CalAngle(Point center, Point p) => Math.Atan2(p.Y - center.Y, p.X - center.X) * 180 / Math.PI;
/// <summary>
/// 判断是否是有效的双精度浮点数
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
public static bool IsValidDoubleValue(object value)
{
var d = (double)value;
if (!double.IsNaN(d))
return !double.IsInfinity(d);
return false;
}
/// <summary>
/// 计算法线
/// </summary>
/// <param name="p0"></param>
/// <param name="p1"></param>
/// <param name="p2"></param>
/// <returns></returns>
public static Vector3D CalNormal(Point3D p0, Point3D p1, Point3D p2)
{
var v0 = new Vector3D(p1.X - p0.X, p1.Y - p0.Y, p1.Z - p0.Z);
var v1 = new Vector3D(p2.X - p1.X, p2.Y - p1.Y, p2.Z - p1.Z);
return Vector3D.CrossProduct(v0, v1);
}
}
}
@@ -0,0 +1,20 @@
using System;
using System.Windows.Media;
namespace HandyControl.Tools
{
public class ColorHelper
{
private const int Win32RedShift = 0;
private const int Win32GreenShift = 8;
private const int Win32BlueShift = 16;
public static int ToWin32(Color c) => c.R << Win32RedShift | c.G << Win32GreenShift | c.B << Win32BlueShift;
public static Color ToColor(uint c)
{
var bytes = BitConverter.GetBytes(c);
return Color.FromArgb(bytes[3], bytes[2], bytes[1], bytes[0]);
}
}
}
@@ -0,0 +1,22 @@
using System.ComponentModel;
using System.Windows;
namespace HandyControl.Tools
{
public class DesignerHelper
{
private static bool? _isInDesignMode;
public static bool IsInDesignMode
{
get
{
if (!_isInDesignMode.HasValue)
{
_isInDesignMode = (bool)DependencyPropertyDescriptor.FromProperty(DesignerProperties.IsInDesignModeProperty, typeof(FrameworkElement)).Metadata.DefaultValue;
}
return _isInDesignMode.Value;
}
}
}
}
@@ -0,0 +1,369 @@
using System;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Runtime.CompilerServices;
using System.Runtime.ConstrainedExecution;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Security.Permissions;
using System.Threading;
namespace HandyControl.Tools
{
internal class ExternDllHelper
{
private const string Gdi32 = "gdi32.dll";
private const string User32 = "user32.dll";
// ReSharper disable once InconsistentNaming
public const int E_FAIL = unchecked((int) 0x80004005);
[StructLayout(LayoutKind.Sequential)]
internal struct WINCOMPATTRDATA
{
public WINDOWCOMPOSITIONATTRIB Attribute;
public IntPtr Data;
public int DataSize;
}
internal enum WINDOWCOMPOSITIONATTRIB
{
WCA_ACCENT_POLICY = 19
}
internal enum ACCENTSTATE
{
ACCENT_DISABLED = 0,
ACCENT_ENABLE_GRADIENT = 1,
ACCENT_ENABLE_TRANSPARENTGRADIENT = 2,
ACCENT_ENABLE_BLURBEHIND = 3,
ACCENT_ENABLE_ACRYLICBLURBEHIND = 4,
ACCENT_INVALID_STATE = 5
}
[StructLayout(LayoutKind.Sequential)]
internal struct ACCENTPOLICY
{
public ACCENTSTATE AccentState;
public int AccentFlags;
public uint GradientColor;
public int AnimationId;
}
[DllImport(User32)]
internal static extern int SetWindowCompositionAttribute(IntPtr hwnd, ref WINCOMPATTRDATA data);
[ReflectionPermission(SecurityAction.Assert, Unrestricted = true), SecurityPermission(SecurityAction.Assert, Flags = SecurityPermissionFlag.UnmanagedCode)]
public static object PtrToStructure(IntPtr lparam, Type cls) => Marshal.PtrToStructure(lparam, cls);
[DllImport(Gdi32, CharSet = CharSet.Auto, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool DeleteObject(IntPtr hObject);
[ComImport, Guid("0000000C-0000-0000-C000-000000000046"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IStream
{
int Read(
[In]
IntPtr buf,
[In]
int len);
int Write(
[In]
IntPtr buf,
[In]
int len);
[return: MarshalAs(UnmanagedType.I8)]
long Seek(
[In, MarshalAs(UnmanagedType.I8)]
long dlibMove,
[In]
int dwOrigin);
void SetSize(
[In, MarshalAs(UnmanagedType.I8)]
long libNewSize);
[return: MarshalAs(UnmanagedType.I8)]
long CopyTo(
[In, MarshalAs(UnmanagedType.Interface)]
IStream pstm,
[In, MarshalAs(UnmanagedType.I8)]
long cb,
[Out, MarshalAs(UnmanagedType.LPArray)]
long[] pcbRead);
void Commit(
[In]
int grfCommitFlags);
void Revert();
void LockRegion(
[In, MarshalAs(UnmanagedType.I8)]
long libOffset,
[In, MarshalAs(UnmanagedType.I8)]
long cb,
[In]
int dwLockType);
void UnlockRegion(
[In, MarshalAs(UnmanagedType.I8)]
long libOffset,
[In, MarshalAs(UnmanagedType.I8)]
long cb,
[In]
int dwLockType);
void Stat(
[In]
IntPtr pStatstg,
[In]
int grfStatFlag);
[return: MarshalAs(UnmanagedType.Interface)]
IStream Clone();
}
[SuppressMessage("ReSharper", "InconsistentNaming")]
public class StreamConsts
{
public const int LOCK_WRITE = 0x1;
public const int LOCK_EXCLUSIVE = 0x2;
public const int LOCK_ONLYONCE = 0x4;
public const int STATFLAG_DEFAULT = 0x0;
public const int STATFLAG_NONAME = 0x1;
public const int STATFLAG_NOOPEN = 0x2;
public const int STGC_DEFAULT = 0x0;
public const int STGC_OVERWRITE = 0x1;
public const int STGC_ONLYIFCURRENT = 0x2;
public const int STGC_DANGEROUSLYCOMMITMERELYTODISKCACHE = 0x4;
public const int STREAM_SEEK_SET = 0x0;
public const int STREAM_SEEK_CUR = 0x1;
public const int STREAM_SEEK_END = 0x2;
}
internal class Gdip
{
private const string Gdiplus = "gdiplus.dll";
private const string ThreadDataSlotName = "system.drawing.threaddata";
private static IntPtr InitToken;
private static bool Initialized => InitToken != IntPtr.Zero;
static Gdip()
{
Initialize();
}
[StructLayout(LayoutKind.Sequential)]
private struct StartupInput
{
private int GdiplusVersion;
private readonly IntPtr DebugEventCallback;
private bool SuppressBackgroundThread;
private bool SuppressExternalCodecs;
public static StartupInput GetDefault()
{
var result = new StartupInput
{
GdiplusVersion = 1,
SuppressBackgroundThread = false,
SuppressExternalCodecs = false
};
return result;
}
}
[StructLayout(LayoutKind.Sequential)]
private struct StartupOutput
{
private readonly IntPtr hook;
private readonly IntPtr unhook;
}
[ResourceExposure(ResourceScope.None)]
[ResourceConsumption(ResourceScope.AppDomain, ResourceScope.AppDomain)]
[SuppressMessage("Microsoft.Performance", "CA1804:RemoveUnusedLocals")]
private static void Initialize()
{
var input = StartupInput.GetDefault();
var status = GdiplusStartup(out InitToken, ref input, out _);
if (status != Ok)
{
throw StatusException(status);
}
var currentDomain = AppDomain.CurrentDomain;
currentDomain.ProcessExit += OnProcessExit;
if (!currentDomain.IsDefaultAppDomain())
{
currentDomain.DomainUnload += OnProcessExit;
}
}
[PrePrepareMethod]
[ResourceExposure(ResourceScope.AppDomain)]
[ResourceConsumption(ResourceScope.AppDomain)]
private static void OnProcessExit(object sender, EventArgs e) => Shutdown();
[SuppressMessage("Microsoft.Reliability", "CA2001:AvoidCallingProblematicMethods")]
[ResourceExposure(ResourceScope.AppDomain)]
[ResourceConsumption(ResourceScope.AppDomain)]
private static void Shutdown()
{
if (Initialized)
{
ClearThreadData();
// unhook our shutdown handlers as we do not need to shut down more than once
var currentDomain = AppDomain.CurrentDomain;
currentDomain.ProcessExit -= OnProcessExit;
if (!currentDomain.IsDefaultAppDomain())
{
currentDomain.DomainUnload -= OnProcessExit;
}
}
}
[MethodImpl(MethodImplOptions.NoInlining)]
private static void ClearThreadData()
{
var slot = Thread.GetNamedDataSlot(ThreadDataSlotName);
Thread.SetData(slot, null);
}
//----------------------------------------------------------------------------------------
// Status codes
//----------------------------------------------------------------------------------------
internal const int Ok = 0;
internal const int GenericError = 1;
internal const int InvalidParameter = 2;
internal const int OutOfMemory = 3;
internal const int ObjectBusy = 4;
internal const int InsufficientBuffer = 5;
internal const int NotImplemented = 6;
internal const int Win32Error = 7;
internal const int WrongState = 8;
internal const int Aborted = 9;
internal const int FileNotFound = 10;
internal const int ValueOverflow = 11;
internal const int AccessDenied = 12;
internal const int UnknownImageFormat = 13;
internal const int FontFamilyNotFound = 14;
internal const int FontStyleNotFound = 15;
internal const int NotTrueTypeFont = 16;
internal const int UnsupportedGdiplusVersion = 17;
internal const int GdiplusNotInitialized = 18;
internal const int PropertyNotFound = 19;
internal const int PropertyNotSupported = 20;
[DllImport(Gdiplus, SetLastError = true, ExactSpelling = true, CharSet = CharSet.Unicode)]
[ResourceExposure(ResourceScope.None)]
internal static extern int GdipImageGetFrameDimensionsCount(HandleRef image, out int count);
internal static Exception StatusException(int status)
{
switch (status)
{
case GenericError: return new ExternalException("GdiplusGenericError");
case InvalidParameter: return new ArgumentException("GdiplusInvalidParameter");
case OutOfMemory: return new OutOfMemoryException("GdiplusOutOfMemory");
case ObjectBusy: return new InvalidOperationException("GdiplusObjectBusy");
case InsufficientBuffer: return new OutOfMemoryException("GdiplusInsufficientBuffer");
case NotImplemented: return new NotImplementedException("GdiplusNotImplemented");
case Win32Error: return new ExternalException("GdiplusGenericError");
case WrongState: return new InvalidOperationException("GdiplusWrongState");
case Aborted: return new ExternalException("GdiplusAborted");
case FileNotFound: return new FileNotFoundException("GdiplusFileNotFound");
case ValueOverflow: return new OverflowException("GdiplusOverflow");
case AccessDenied: return new ExternalException("GdiplusAccessDenied");
case UnknownImageFormat: return new ArgumentException("GdiplusUnknownImageFormat");
case PropertyNotFound: return new ArgumentException("GdiplusPropertyNotFoundError");
case PropertyNotSupported: return new ArgumentException("GdiplusPropertyNotSupportedError");
case FontFamilyNotFound: return new ArgumentException("GdiplusFontFamilyNotFound");
case FontStyleNotFound: return new ArgumentException("GdiplusFontStyleNotFound");
case NotTrueTypeFont: return new ArgumentException("GdiplusNotTrueTypeFont_NoName");
case UnsupportedGdiplusVersion: return new ExternalException("GdiplusUnsupportedGdiplusVersion");
case GdiplusNotInitialized: return new ExternalException("GdiplusNotInitialized");
}
return new ExternalException("GdiplusUnknown");
}
[DllImport(Gdiplus, SetLastError = true, ExactSpelling = true, CharSet = CharSet.Unicode)]
[ResourceExposure(ResourceScope.None)]
internal static extern int GdipImageGetFrameDimensionsList(HandleRef image, IntPtr buffer, int count);
[DllImport(Gdiplus, SetLastError = true, ExactSpelling = true, CharSet = CharSet.Unicode)]
[ResourceExposure(ResourceScope.None)]
internal static extern int GdipImageGetFrameCount(HandleRef image, ref Guid dimensionId, int[] count);
[DllImport(Gdiplus, SetLastError = true, ExactSpelling = true, CharSet = CharSet.Unicode)]
[ResourceExposure(ResourceScope.None)]
internal static extern int GdipGetPropertyItemSize(HandleRef image, int propid, out int size);
[DllImport(Gdiplus, SetLastError = true, ExactSpelling = true, CharSet = CharSet.Unicode)]
[ResourceExposure(ResourceScope.None)]
internal static extern int GdipGetPropertyItem(HandleRef image, int propid, int size, IntPtr buffer);
[DllImport(Gdiplus, SetLastError = true, ExactSpelling = true, CharSet = CharSet.Unicode)]
[ResourceExposure(ResourceScope.Machine)]
internal static extern int GdipCreateHBITMAPFromBitmap(HandleRef nativeBitmap, out IntPtr hbitmap, int argbBackground);
[DllImport(Gdiplus, SetLastError = true, ExactSpelling = true, CharSet = CharSet.Unicode)]
[ResourceExposure(ResourceScope.None)]
internal static extern int GdipImageSelectActiveFrame(HandleRef image, ref Guid dimensionId, int frameIndex);
[DllImport(Gdiplus, SetLastError = true, ExactSpelling = true, CharSet = CharSet.Unicode)]
[ResourceExposure(ResourceScope.Machine)]
internal static extern int GdipCreateBitmapFromFile(string filename, out IntPtr bitmap);
[DllImport(Gdiplus, SetLastError = true, ExactSpelling = true, CharSet = CharSet.Unicode)]
[ResourceExposure(ResourceScope.None)]
internal static extern int GdipImageForceValidation(HandleRef image);
[DllImport(Gdiplus, SetLastError = true, ExactSpelling = true, EntryPoint = "GdipDisposeImage", CharSet = CharSet.Unicode)]
[ResourceExposure(ResourceScope.None)]
private static extern int IntGdipDisposeImage(HandleRef image);
internal static int GdipDisposeImage(HandleRef image)
{
if (!Initialized) return Ok;
var result = IntGdipDisposeImage(image);
return result;
}
[DllImport(Gdiplus, SetLastError = true, ExactSpelling = true, CharSet = CharSet.Unicode)]
[ResourceExposure(ResourceScope.Process)]
private static extern int GdiplusStartup(out IntPtr token, ref StartupInput input, out StartupOutput output);
[DllImport(Gdiplus, SetLastError = true, ExactSpelling = true, CharSet = CharSet.Unicode)]
[ResourceExposure(ResourceScope.None)]
internal static extern int GdipGetImageRawFormat(HandleRef image, ref Guid format);
[DllImport(Gdiplus, SetLastError = true, ExactSpelling = true, CharSet = CharSet.Unicode)]
[ResourceExposure(ResourceScope.Machine)]
internal static extern int GdipCreateBitmapFromStream(IStream stream, out IntPtr bitmap);
}
}
}
@@ -0,0 +1,89 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Windows;
using HandyControl.Data;
namespace HandyControl.Tools
{
/// <summary>
/// 资源帮助类
/// </summary>
public class ResourceHelper
{
/// <summary>
/// 获取字符串
/// </summary>
/// <param name="key"></param>
/// <returns></returns>
public static string GetString(string key) => Application.Current.TryFindResource(key) as string;
/// <summary>
/// 获取字符串
/// </summary>
/// <param name="separator"></param>
/// <param name="keyArr"></param>
/// <returns></returns>
public static string GetString(string separator = ";", params string[] keyArr) =>
string.Join(separator, keyArr.Select(key => Application.Current.TryFindResource(key) as string).ToArray());
/// <summary>
/// 获取字符串
/// </summary>
/// <param name="keyArr"></param>
/// <returns></returns>
public static List<string> GetStringList(params string[] keyArr) => keyArr.Select(key => Application.Current.TryFindResource(key) as string).ToList();
/// <summary>
/// 获取资源
/// </summary>
/// <param name="key"></param>
/// <returns></returns>
public static T GetResource<T>(string key)
{
if (Application.Current.TryFindResource(key) is T resource)
{
return resource;
}
return default(T);
}
/// <summary>
/// 获取HandyControl皮肤
/// </summary>
/// <param name="skin"></param>
/// <returns></returns>
public static ResourceDictionary GetSkin(SkinType skin) => new ResourceDictionary
{
Source = new Uri($"pack://application:,,,/HandyControl;component/Themes/Skin{skin.ToString()}.xaml")
};
/// <summary>
/// 获取皮肤
/// </summary>
/// <param name="assembly"></param>
/// <param name="themePath"></param>
/// <param name="skin"></param>
/// <returns></returns>
public static ResourceDictionary GetSkin(Assembly assembly, string themePath, SkinType skin)
{
try
{
var uri = new Uri($"pack://application:,,,/{assembly.GetName().Name};component/{themePath}/Skin{skin.ToString()}.xaml");
return new ResourceDictionary
{
Source = uri
};
}
catch
{
return new ResourceDictionary
{
Source = new Uri($"pack://application:,,,/{assembly.GetName().Name};component/{themePath}/Skin{SkinType.Default.ToString()}.xaml")
};
}
}
}
}
@@ -0,0 +1,42 @@
using System.Collections.Generic;
using System.Linq;
using System.Windows.Media;
using HandyControl.Controls;
namespace HandyControl.Tools
{
/// <summary>
/// 该类可以为可视化元素提供单开的功能
/// </summary>
public class SingleOpenHelper
{
private static readonly Dictionary<string, ISingleOpen> OpenDic = new Dictionary<string, ISingleOpen>();
/// <summary>
/// 根据指定的类型创建实例
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public static T CreateControl<T>() where T : Visual, ISingleOpen, new()
{
var typeStr = typeof(T).FullName;
if (string.IsNullOrEmpty(typeStr)) return default(T);
var temp = new T();
if (!OpenDic.Keys.Contains(typeStr))
{
OpenDic.Add(typeStr, temp);
return temp;
}
var currentCtl = OpenDic[typeStr];
if (currentCtl.CanDispose)
{
currentCtl.Dispose();
OpenDic[typeStr] = temp;
return temp;
}
return default(T);
}
}
}
@@ -0,0 +1,19 @@
namespace HandyControl.Tools
{
/// <summary>
/// 验证帮助类
/// </summary>
internal class ValidateHelper
{
/// <summary>
/// 是否在浮点数范围内
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
internal static bool IsInRangeOfDouble(object value)
{
var v = (double)value;
return !(double.IsNaN(v) || double.IsInfinity(v));
}
}
}
@@ -0,0 +1,369 @@
using System.Windows;
using System.Windows.Input;
using System.Windows.Media;
using HandyControl.Data;
using HandyControl.Interactivity;
namespace HandyControl.Tools
{
/// <summary>
/// 鼠标拖动行为,该类是反编译微软的System.Windows.Interactivity程序集得到的,并对其做了些扩展
/// </summary>
internal class MouseDragElementBehaviorEx : Behavior<FrameworkElement>
{
public static readonly DependencyProperty XProperty = DependencyProperty.Register(nameof(X), typeof(double),
typeof(MouseDragElementBehaviorEx), new PropertyMetadata(double.NaN, OnXChanged));
public static readonly DependencyProperty YProperty = DependencyProperty.Register(nameof(Y), typeof(double),
typeof(MouseDragElementBehaviorEx), new PropertyMetadata(double.NaN, OnYChanged));
public static readonly DependencyProperty ConstrainToParentBoundsProperty =
DependencyProperty.Register(nameof(ConstrainToParentBounds), typeof(bool),
typeof(MouseDragElementBehaviorEx), new PropertyMetadata(ValueBoxes.FalseBox, OnConstrainToParentBoundsChanged));
private Transform _cachedRenderTransform;
private Point _relativePosition;
private bool _settingPosition;
/// <summary>
/// 是否固定住Y轴
/// </summary>
public bool LockY { get; set; }
/// <summary>
/// 是否固定住X轴
/// </summary>
public bool LockX { get; set; }
public double X
{
get => (double)GetValue(MouseDragElementBehavior.XProperty);
set => SetValue(MouseDragElementBehavior.XProperty, value);
}
public double Y
{
get => (double)GetValue(MouseDragElementBehavior.YProperty);
set => SetValue(MouseDragElementBehavior.YProperty, value);
}
public bool ConstrainToParentBounds
{
get => (bool)GetValue(MouseDragElementBehavior.ConstrainToParentBoundsProperty);
set => SetValue(MouseDragElementBehavior.ConstrainToParentBoundsProperty, value);
}
private Rect ElementBounds
{
get
{
var layoutRect = ArithmeticHelper.GetLayoutRect(AssociatedObject);
return new Rect(new Point(0.0, 0.0), new Size(layoutRect.Width, layoutRect.Height));
}
}
private FrameworkElement ParentElement => AssociatedObject.Parent as FrameworkElement;
private UIElement RootElement
{
get
{
DependencyObject reference = AssociatedObject;
for (var dependencyObject = reference;
dependencyObject != null;
dependencyObject = VisualTreeHelper.GetParent(reference))
reference = dependencyObject;
return reference as UIElement;
}
}
private Transform RenderTransform
{
get
{
if (_cachedRenderTransform == null ||
!ReferenceEquals(_cachedRenderTransform, AssociatedObject.RenderTransform))
RenderTransform = CloneTransform(AssociatedObject.RenderTransform);
return _cachedRenderTransform;
}
set
{
if (Equals(_cachedRenderTransform, value))
return;
_cachedRenderTransform = value;
AssociatedObject.RenderTransform = value;
}
}
public event MouseEventHandler DragBegun;
public event MouseEventHandler Dragging;
public event MouseEventHandler DragFinished;
private static void OnXChanged(object sender, DependencyPropertyChangedEventArgs args)
{
var dragElementBehavior = (MouseDragElementBehaviorEx)sender;
dragElementBehavior.UpdatePosition(new Point((double)args.NewValue, dragElementBehavior.Y));
}
private static void OnYChanged(object sender, DependencyPropertyChangedEventArgs args)
{
var dragElementBehavior = (MouseDragElementBehaviorEx)sender;
dragElementBehavior.UpdatePosition(new Point(dragElementBehavior.X, (double)args.NewValue));
}
private static void OnConstrainToParentBoundsChanged(object sender, DependencyPropertyChangedEventArgs args)
{
var dragElementBehavior = (MouseDragElementBehaviorEx)sender;
dragElementBehavior.UpdatePosition(new Point(dragElementBehavior.X, dragElementBehavior.Y));
}
private void UpdatePosition(Point point)
{
if (_settingPosition || AssociatedObject == null)
return;
var transformOffset = GetTransformOffset(AssociatedObject.TransformToVisual(RootElement));
ApplyTranslation(double.IsNaN(point.X) ? 0.0 : point.X - transformOffset.X,
double.IsNaN(point.Y) ? 0.0 : point.Y - transformOffset.Y);
}
private void ApplyTranslation(double x, double y)
{
if (ParentElement == null)
return;
var point = TransformAsVector(RootElement.TransformToVisual(ParentElement), x, y);
x = point.X;
y = point.Y;
if (ConstrainToParentBounds)
{
var parentElement = ParentElement;
var rect1 = new Rect(0.0, 0.0, parentElement.ActualWidth, parentElement.ActualHeight);
var rect2 = AssociatedObject.TransformToVisual(parentElement).TransformBounds(ElementBounds);
rect2.X += x;
rect2.Y += y;
if (!RectContainsRect(rect1, rect2))
{
if (rect2.X < rect1.Left)
{
var num = rect2.X - rect1.Left;
x -= num;
}
else if (rect2.Right > rect1.Right)
{
var num = rect2.Right - rect1.Right;
x -= num;
}
if (rect2.Y < rect1.Top)
{
var num = rect2.Y - rect1.Top;
y -= num;
}
else if (rect2.Bottom > rect1.Bottom)
{
var num = rect2.Bottom - rect1.Bottom;
y -= num;
}
}
}
ApplyTranslationTransform(x, y);
}
internal void ApplyTranslationTransform(double x, double y)
{
var renderTransform = RenderTransform;
var translateTransform = renderTransform as TranslateTransform;
if (translateTransform == null)
{
var matrixTransform = renderTransform as MatrixTransform;
if (renderTransform is TransformGroup transformGroup1)
{
if (transformGroup1.Children.Count > 0)
translateTransform =
transformGroup1.Children[transformGroup1.Children.Count - 1] as TranslateTransform;
if (translateTransform == null)
{
translateTransform = new TranslateTransform();
transformGroup1.Children.Add(translateTransform);
}
}
else
{
if (matrixTransform != null)
{
var matrix = matrixTransform.Matrix;
//在该处对微软的类进行了修改
if (!LockX)
{
matrix.OffsetX += x;
}
if (!LockY)
{
matrix.OffsetY += y;
}
//修改结束
RenderTransform = new MatrixTransform
{
Matrix = matrix
};
return;
}
var transformGroup2 = new TransformGroup();
translateTransform = new TranslateTransform();
if (renderTransform != null)
transformGroup2.Children.Add(renderTransform);
transformGroup2.Children.Add(translateTransform);
RenderTransform = transformGroup2;
}
}
//在该处对微软的类进行了修改
if (!LockX)
{
translateTransform.X += x;
}
if (!LockY)
{
translateTransform.Y += y;
}
//修改结束
}
internal static Transform CloneTransform(Transform transform)
{
if (transform == null)
return null;
if (transform is ScaleTransform scaleTransform)
return new ScaleTransform
{
CenterX = scaleTransform.CenterX,
CenterY = scaleTransform.CenterY,
ScaleX = scaleTransform.ScaleX,
ScaleY = scaleTransform.ScaleY
};
if (transform is RotateTransform rotateTransform)
return new RotateTransform
{
Angle = rotateTransform.Angle,
CenterX = rotateTransform.CenterX,
CenterY = rotateTransform.CenterY
};
if (transform is SkewTransform skewTransform)
return new SkewTransform
{
AngleX = skewTransform.AngleX,
AngleY = skewTransform.AngleY,
CenterX = skewTransform.CenterX,
CenterY = skewTransform.CenterY
};
if (transform is TranslateTransform translateTransform)
return new TranslateTransform
{
X = translateTransform.X,
Y = translateTransform.Y
};
if (transform is MatrixTransform matrixTransform)
return new MatrixTransform
{
Matrix = matrixTransform.Matrix
};
if (!(transform is TransformGroup transformGroup1))
return null;
var transformGroup2 = new TransformGroup();
foreach (var child in transformGroup1.Children)
transformGroup2.Children.Add(CloneTransform(child));
return transformGroup2;
}
private void UpdatePosition()
{
var transformOffset = GetTransformOffset(AssociatedObject.TransformToVisual(RootElement));
X = transformOffset.X;
Y = transformOffset.Y;
}
internal void StartDrag(Point positionInElementCoordinates)
{
_relativePosition = positionInElementCoordinates;
AssociatedObject.CaptureMouse();
AssociatedObject.MouseMove += OnMouseMove;
AssociatedObject.LostMouseCapture += OnLostMouseCapture;
AssociatedObject.AddHandler(UIElement.MouseLeftButtonUpEvent,
new MouseButtonEventHandler(OnMouseLeftButtonUp), false);
}
internal void HandleDrag(Point newPositionInElementCoordinates)
{
var point = TransformAsVector(AssociatedObject.TransformToVisual(RootElement),
newPositionInElementCoordinates.X - _relativePosition.X,
newPositionInElementCoordinates.Y - _relativePosition.Y);
_settingPosition = true;
ApplyTranslation(point.X, point.Y);
UpdatePosition();
_settingPosition = false;
}
internal void EndDrag()
{
AssociatedObject.MouseMove -= OnMouseMove;
AssociatedObject.LostMouseCapture -= OnLostMouseCapture;
AssociatedObject.RemoveHandler(UIElement.MouseLeftButtonUpEvent,
new MouseButtonEventHandler(OnMouseLeftButtonUp));
}
private void OnMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
StartDrag(e.GetPosition(AssociatedObject));
DragBegun?.Invoke(this, e);
}
private void OnLostMouseCapture(object sender, MouseEventArgs e)
{
EndDrag();
DragFinished?.Invoke(this, e);
}
private void OnMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
AssociatedObject.ReleaseMouseCapture();
}
private void OnMouseMove(object sender, MouseEventArgs e)
{
HandleDrag(e.GetPosition(AssociatedObject));
Dragging?.Invoke(this, e);
}
private static bool RectContainsRect(Rect rect1, Rect rect2)
{
if (rect1.IsEmpty || rect2.IsEmpty || rect1.X > rect2.X || rect1.Y > rect2.Y ||
rect1.X + rect1.Width < rect2.X + rect2.Width)
return false;
return rect1.Y + rect1.Height >= rect2.Y + rect2.Height;
}
private static Point TransformAsVector(GeneralTransform transform, double x, double y)
{
var point1 = transform.Transform(new Point(0.0, 0.0));
var point2 = transform.Transform(new Point(x, y));
return new Point(point2.X - point1.X, point2.Y - point1.Y);
}
private static Point GetTransformOffset(GeneralTransform transform)
{
return transform.Transform(new Point(0.0, 0.0));
}
protected override void OnAttached()
{
AssociatedObject.AddHandler(UIElement.MouseLeftButtonDownEvent,
new MouseButtonEventHandler(OnMouseLeftButtonDown), false);
}
protected override void OnDetaching()
{
AssociatedObject.RemoveHandler(UIElement.MouseLeftButtonDownEvent,
new MouseButtonEventHandler(OnMouseLeftButtonDown));
}
}
}
@@ -0,0 +1,97 @@
using System;
using System.Text.RegularExpressions;
using HandyControl.Data;
namespace HandyControl.Tools
{
/// <summary>
/// 包含一些正则验证操作
/// </summary>
public static class RegularJudgment
{
private static readonly RegularPatterns RegularPatterns = new RegularPatterns();
/// <summary>
/// 判断字符串格式是否符合某种要求
/// </summary>
/// <param name="str">需要判断的字符串</param>
/// <param name="pattern">正则表达式</param>
/// <returns></returns>
public static bool IsKindOf(this string str, string pattern)
{
return Regex.IsMatch(str, pattern);
}
/// <summary>
/// 判断字符串是否满足指定的格式
/// </summary>
/// <param name="text">需要判断的字符串</param>
/// <param name="textType">指定格式的文本</param>
/// <returns></returns>
public static bool IsKindOf(this string text, TextType textType)
{
if (textType == TextType.Common) return true;
return Regex.IsMatch(text,
RegularPatterns.GetValue(Enum.GetName(typeof(TextType), textType) + "Pattern").ToString());
}
/// <summary>
/// 判断字符串格式是否为电子邮件
/// </summary>
/// <param name="email">需要判断的Email字符串</param>
/// <returns>方法返回布尔值</returns>
public static bool IsEmail(this string email)
{
return Regex.IsMatch(email, RegularPatterns.MailPattern);
}
/// <summary>
/// 判断字符串格式是否为指定类型的IP地址
/// </summary>
/// <param name="ip">需要判断的IP字符串</param>
/// <param name="ipType">指定的IP类型</param>
/// <returns>方法返回布尔值</returns>
public static bool IsIp(this string ip, IpType ipType)
{
switch (ipType)
{
case IpType.A: return Regex.IsMatch(ip, RegularPatterns.IpAPattern);
case IpType.B: return Regex.IsMatch(ip, RegularPatterns.IpBPattern);
case IpType.C: return Regex.IsMatch(ip, RegularPatterns.IpCPattern);
case IpType.D: return Regex.IsMatch(ip, RegularPatterns.IpDPattern);
case IpType.E: return Regex.IsMatch(ip, RegularPatterns.IpEPattern);
default: return false;
}
}
/// <summary>
/// 判断字符串格式是否为IP地址
/// </summary>
/// <param name="ip">需要判断的IP字符串</param>
/// <returns>方法返回布尔值</returns>
public static bool IsIp(this string ip)
{
return Regex.IsMatch(ip, RegularPatterns.IpPattern);
}
/// <summary>
/// 判断字符串格式是否为单个汉字
/// </summary>
/// <param name="str">需要判断的单个汉字字符串</param>
/// <returns>方法返回布尔值</returns>
public static bool IsChinese(this string str)
{
return Regex.IsMatch(str, RegularPatterns.ChinesePattern);
}
/// <summary>
/// 判断字符串格式是否为url
/// </summary>
/// <param name="str">需要判断的url字符串</param>
/// <returns>方法返回布尔值</returns>
public static bool IsUrl(this string str)
{
return Regex.IsMatch(str, RegularPatterns.UrlPattern);
}
}
}
@@ -0,0 +1,151 @@
namespace HandyControl.Tools
{
/// <summary>
/// 包含一些正则验证所需要的字符串
/// </summary>
public sealed class RegularPatterns
{
/// <summary>
/// 邮件正则匹配表达式
/// </summary>
public const string MailPattern =
@"^([\w-\.]+)@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.)|(([\w-]+\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\]?)$";
/// <summary>
/// 手机号正则匹配表达式
/// </summary>
public const string PhonePattern = @"^((13[0-9])|(15[^4,\d])|(18[0,5-9]))\d{8}$";
/// <summary>
/// IP正则匹配
/// </summary>
public const string IpPattern =
@"^(25[0-5]|2[0-4]\d|1\d{2}|[1-9]?\d)\."
+ @"(25[0-5]|2[0-4]\d|1\d{2}|[1-9]?\d)\."
+ @"(25[0-5]|2[0-4]\d|1\d{2}|[1-9]?\d)\."
+ @"(25[0-5]|2[0-4]\d|1\d{2}|[1-9]?\d)$";
/// <summary>
/// A类IP正则匹配
/// </summary>
public const string IpAPattern =
@"^(12[0-6]|1[0-1]\d|[1-9]?\d)\."
+ @"(25[0-5]|2[0-4]\d|1\d{2}|[1-9]?\d)\."
+ @"(25[0-5]|2[0-4]\d|1\d{2}|[1-9]?\d)\."
+ @"(25[0-5]|2[0-4]\d|1\d{2}|[1-9]?\d)$";
/// <summary>
/// B类IP正则匹配
/// </summary>
public const string IpBPattern =
@"^(19[0-1]|12[8-9]|1[3-8]\d)\."
+ @"(25[0-5]|2[0-4]\d|1\d{2}|[1-9]?\d)\."
+ @"(25[0-5]|2[0-4]\d|1\d{2}|[1-9]?\d)\."
+ @"(25[0-5]|2[0-4]\d|1\d{2}|[1-9]?\d)$";
/// <summary>
/// C类IP正则匹配
/// </summary>
public const string IpCPattern =
@"^(19[2-9]|22[0-3]|2[0-1]\d)\."
+ @"(25[0-5]|2[0-4]\d|1\d{2}|[1-9]?\d)\."
+ @"(25[0-5]|2[0-4]\d|1\d{2}|[1-9]?\d)\."
+ @"(25[0-5]|2[0-4]\d|1\d{2}|[1-9]?\d)$";
/// <summary>
/// D类IP正则匹配
/// </summary>
public const string IpDPattern =
@"^(22[4-9]|23\d\."
+ @"(25[0-5]|2[0-4]\d|1\d{2}|[1-9]?\d)\."
+ @"(25[0-5]|2[0-4]\d|1\d{2}|[1-9]?\d)\."
+ @"(25[0-5]|2[0-4]\d|1\d{2}|[1-9]?\d)$";
/// <summary>
/// E类IP正则匹配
/// </summary>
public const string IpEPattern =
@"^(25[0-5]|24\d\."
+ @"(25[0-5]|2[0-4]\d|1\d{2}|[1-9]?\d)\."
+ @"(25[0-5]|2[0-4]\d|1\d{2}|[1-9]?\d)\."
+ @"(25[0-5]|2[0-4]\d|1\d{2}|[1-9]?\d)$";
/// <summary>
/// 汉字正则匹配
/// </summary>
public const string ChinesePattern = @"^[\u4e00-\u9fa5]$";
/// <summary>
/// Url正则匹配
/// </summary>
public const string UrlPattern =
@"((http|ftp|https)://)(([a-zA-Z0-9\._-]+\.[a-zA-Z]{2,6})|([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}))(:[0-9]{1,4})*(/[a-zA-Z0-9\&%_\./-~-]*)?";
/// <summary>
/// 数字正则匹配
/// </summary>
public const string NumberPattern = @"^\d$";
/// <summary>
/// 计算性质数字正则匹配
/// </summary>
public const string DigitsPattern = @"^[+-]?([0-9]*\.?[0-9]+|[0-9]+\.?[0-9]*)([eE][+-]?[0-9]+)?$";
/// <summary>
/// 正整数正则匹配
/// </summary>
public const string PIntPattern = @"^[1-9]\d*$";
/// <summary>
/// 负整数正则匹配
/// </summary>
public const string NIntPattern = @"^-[1-9]\d*$ ";
/// <summary>
/// 整数正则匹配
/// </summary>
public const string IntPattern = @"^-?[1-9]\d*$";
/// <summary>
/// 非负整数正则匹配
/// </summary>
public const string NnIntPattern = @"^[1-9]\d*|0$";
/// <summary>
/// 非正整数正则匹配
/// </summary>
public const string NpIntPattern = @"^-[1-9]\d*|0$";
/// <summary>
/// 正浮点数正则匹配
/// </summary>
public const string PDoublePattern = @"^[1-9]\d*\.\d*|0\.\d*[1-9]\d*$";
/// <summary>
/// 负浮点数正则匹配
/// </summary>
public const string NDoublePattern = @"^-([1-9]\d*\.\d*|0\.\d*[1-9]\d*)$";
/// <summary>
/// 浮点数正则匹配
/// </summary>
public const string DoublePattern = @"^-?([1-9]\d*\.\d*|0\.\d*[1-9]\d*|0?\.0+|0)$";
/// <summary>
/// 非负浮点数正则匹配
/// </summary>
public const string NnDoublePattern = @"^[1-9]\d*\.\d*|0\.\d*[1-9]\d*|0?\.0+|0$";
/// <summary>
/// 非正浮点数正则匹配
/// </summary>
public const string NpDoublePattern = @"^(-([1-9]\d*\.\d*|0\.\d*[1-9]\d*))|0?\.0+|0$";
/// <summary>
/// 根据属性名称使用反射来获取值
/// </summary>
/// <param name="propertyName"></param>
/// <returns></returns>
public object GetValue(string propertyName) => GetType().GetField(propertyName).GetValue(null);
}
}
@@ -0,0 +1,37 @@
using System.Windows;
using System.Windows.Controls;
using HandyControl.Controls;
using HandyControl.Data;
namespace HandyControl.Tools
{
public class RadioGroupItemStyleSelector : StyleSelector
{
public override Style SelectStyle(object item, DependencyObject container)
{
if (container is RadioGroup radioGroup && item is UIElement radioButton)
{
var count = radioGroup.Items.Count;
if (count == 1)
{
return ResourceHelper.GetResource<Style>(ResourceToken.RadioGroupItemSingle);
}
var index = radioGroup.Items.IndexOf(radioButton);
return radioGroup.Orientation == Orientation.Horizontal
? index == 0
? ResourceHelper.GetResource<Style>(ResourceToken.RadioGroupItemHorizontalFirst)
: ResourceHelper.GetResource<Style>(index == count - 1
? ResourceToken.RadioGroupItemHorizontalLast
: ResourceToken.RadioGroupItemDefault)
: index == 0
? ResourceHelper.GetResource<Style>(ResourceToken.RadioGroupItemVerticalFirst)
: ResourceHelper.GetResource<Style>(index == count - 1
? ResourceToken.RadioGroupItemVerticalLast
: ResourceToken.RadioGroupItemDefault);
}
return null;
}
}
}
@@ -0,0 +1,30 @@
using System.Windows;
using System.Windows.Controls;
using HandyControl.Data;
namespace HandyControl.Tools
{
public class TabItemCapsuleStyleSelector : StyleSelector
{
public override Style SelectStyle(object item, DependencyObject container)
{
if (item is TabItem tabItem && tabItem.Parent is TabControl tabControl)
{
var count = tabControl.Items.Count;
if (count == 1)
{
return ResourceHelper.GetResource<Style>(ResourceToken.TabItemCapsuleSingle);
}
var index = tabControl.Items.IndexOf(tabItem);
return index == 0
? ResourceHelper.GetResource<Style>(ResourceToken.TabItemCapsuleHorizontalFirst)
: ResourceHelper.GetResource<Style>(index == count - 1
? ResourceToken.TabItemCapsuleHorizontalLast
: ResourceToken.TabItemCapsuleDefault);
}
return null;
}
}
}