功能更新

This commit is contained in:
GG Z
2026-02-12 21:29:00 +08:00
parent a9faf251be
commit b3479d1f39
342 changed files with 4671 additions and 2223 deletions

View File

@@ -0,0 +1,44 @@
using System.Globalization;
using System.Windows.Data;
namespace Melskin.Converters.Internal;
/// <summary>
/// 该类实现了IValueConverter接口用于将Alpha透明度值0-255与百分比字符串0%-100%)之间进行转换。
/// 在Convert方法中接受一个0到255之间的整数值作为输入并将其转换为相应的百分比字符串表示形式。
/// 而在ConvertBack方法中则执行相反的操作接收一个百分比字符串并尝试将其解析回0至255范围内的整数。
/// 若输入非预期格式或超出合理范围时,转换器将采取适当措施以确保输出结果的有效性。
/// </summary>
internal class AlphaToPercentConverter : IValueConverter
{
public static readonly AlphaToPercentConverter Instance = new();
/// <inheritdoc />
public object Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
{
if (value is byte alpha)
{
// 将 0-255 的值转换为 0-100 的百分比字符串
return $"{Math.Round(alpha / 255.0 * 100.0)}%";
}
return "100%";
}
/// <inheritdoc />
public object ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
{
if (value is string percentStr)
{
// 移除 '%' 符号并尝试解析
if (double.TryParse(percentStr.TrimEnd('%'), out var percent))
{
var clampedPercent = Math.Max(0, Math.Min(100, percent));
var alpha = (byte)Math.Round(clampedPercent / 100.0 * 255.0);
return alpha;
}
}
// 如果转换失败,返回 UnsetValue 以保留原值
return DependencyProperty.UnsetValue;
}
}

View File

@@ -0,0 +1,46 @@
using System.Globalization;
using System.Windows.Data;
namespace Melskin.Converters.Internal;
/// <summary>
/// 将颜色值转换为其对应的十六进制字符串表示形式的转换器。该类实现了IValueConverter接口用于在WPF绑定中将Color类型的对象转换为十六进制颜色代码例如#AARRGGBB格式以及执行反向操作。
/// Convert方法接受一个Color对象作为输入并返回一个包含Alpha、Red、Green和Blue分量的十六进制字符串。如果输入不是Color类型则默认返回"#FFFFFFFF",代表完全不透明的白色。
/// ConvertBack方法尝试从给定的十六进制颜色字符串创建一个Color对象。它首先检查字符串是否以'#'开始如果不是则自动添加。然后使用内置的ColorConverter来解析这个字符串。如果提供的字符串格式错误或无法解析则返回DependencyProperty.UnsetValue表示转换失败。
/// </summary>
internal class ColorToHexConverter : IValueConverter
{
public static readonly ColorToHexConverter Instance = new();
public object Convert(object? value, Type t, object? p, CultureInfo c) =>
value is Color color ? $"#{color.A:X2}{color.R:X2}{color.G:X2}{color.B:X2}".ToUpper() : "#FFFFFFFF";
public object? ConvertBack(object? value, Type t, object? p, CultureInfo c)
{
if (value is not string hexStr || string.IsNullOrWhiteSpace(hexStr))
{
return DependencyProperty.UnsetValue;
}
var processedHex = hexStr.Trim();
// 如果用户没有输入 '#',自动为他们添加
if (!processedHex.StartsWith("#"))
{
processedHex = "#" + processedHex;
}
try
{
// 使用内置的 ColorConverter它非常强大能处理各种有效格式
return ColorConverter.ConvertFromString(processedHex);
}
catch
{
// 如果转换失败(例如,格式错误),则忽略此次输入
return DependencyProperty.UnsetValue;
}
}
}

View File

@@ -0,0 +1,34 @@
using System.Globalization;
using System.Windows.Data;
namespace Melskin.Converters.Internal;
/// <summary>
/// 将带有透明度的颜色转换为不透明颜色的转换器。此转换器实现了IValueConverter接口主要用于在WPF应用程序中进行数据绑定时使用。
/// 当从视图模型传递到视图的颜色值包含透明度信息时,该转换器能够将这个颜色转换成一个完全不透明的新颜色,
/// 通过移除其透明度分量并保持RGB值不变来实现这一点。如果输入不是Color类型则返回黑色作为默认值。
/// </summary>
internal class ColorToOpaqueColorConverter : IValueConverter
{
public static readonly ColorToOpaqueColorConverter Instance = new();
/// <inheritdoc />
public object Convert(object? value,
Type t,
object? p,
CultureInfo cul) =>
value is Color c
? Color.FromRgb(c.R,
c.G,
c.B)
: Colors.Black;
/// <inheritdoc />
public object ConvertBack(object? v,
Type t,
object? p,
CultureInfo c) =>
Binding.DoNothing;
}

View File

@@ -0,0 +1,147 @@
using System.Globalization;
using System.Windows.Data;
namespace Melskin.Converters.Internal;
/// <summary>
/// CreateSlotClipGeometryConverter 是一个实现了 IMultiValueConverter 接口的类,用于根据给定的参数创建一个具有圆角的矩形几何图形。
/// 该转换器接受三个输入值:圆角半径、宽度和高度,并可选地接受一个表示边距的字符串参数。
/// 输出为 RectangleGeometry 对象,可用于定义 UI 元素的剪裁区域。
/// </summary>
internal class CreateSlotClipGeometryConverter : IMultiValueConverter
{
public static readonly CreateSlotClipGeometryConverter Instance = new();
//public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
//{
// if (values == null || values.Length < 4 ||
// values[0] is not CornerRadius radius ||
// values[1] is not double width ||
// values[2] is not double height ||
// values[3] is not double paddingValue)
// return null;
// var padding = new Thickness(paddingValue);
// var rectW = Math.Max(0, width - padding.Left - padding.Right);
// var rectH = Math.Max(0, height - padding.Top - padding.Bottom);
// var tl = Math.Max(0, radius.TopLeft);
// var tr = Math.Max(0, radius.TopRight);
// var br = Math.Max(0, radius.BottomRight);
// var bl = Math.Max(0, radius.BottomLeft);
// double left = padding.Left; double top = padding.Top; double right = left + rectW; double bottom = top + rectH;
// // 构造路径
// var figure = new PathFigure { IsClosed = true, IsFilled = true, StartPoint = new Point(left + tl, top) };
// var segments = new PathSegmentCollection
// {
// // Top line + TopRight arc
// new LineSegment(new Point(right - tr, top), true),
// new ArcSegment(new Point(right, top + tr), new Size(tr, tr), 0, false, SweepDirection.Clockwise, true),
// // Right line + BottomRight arc
// new LineSegment(new Point(right, bottom - br), true),
// new ArcSegment(new Point(right - br, bottom), new Size(br, br), 0, false, SweepDirection.Clockwise, true),
// // Bottom line + BottomLeft arc
// new LineSegment(new Point(left + bl, bottom), true),
// new ArcSegment(new Point(left, bottom - bl), new Size(bl, bl), 0, false, SweepDirection.Clockwise, true),
// // Left line + TopLeft arc
// new LineSegment(new Point(left, top + tl), true),
// new ArcSegment(new Point(left + tl, top), new Size(tl, tl), 0, false, SweepDirection.Clockwise, true)
// };
// figure.Segments = segments;
// var geometry = new PathGeometry();
// geometry.Figures.Add(figure);
// return geometry;
//}
public object? Convert(object[]? values, Type targetType, object parameter, CultureInfo culture)
{
if (values == null || values.Length < 3 ||
values[0] is not CornerRadius radius ||
values[1] is not double width ||
values[2] is not double height)
return null;
var paramStr = parameter as string;
_ = double.TryParse(paramStr, out var margin);
var rectW = width - (2 * margin);
var rectH = height - (2 * margin);
if (rectW < 0) rectW = 0;
if (rectH < 0) rectH = 0;
var left = margin;
var top = margin;
var right = left + rectW;
var bottom = top + rectH;
var tl = Math.Max(0, radius.TopLeft);
var tr = Math.Max(0, radius.TopRight);
var br = Math.Max(0, radius.BottomRight);
var bl = Math.Max(0, radius.BottomLeft);
var figure = new PathFigure
{
IsClosed = true,
IsFilled = true,
StartPoint = new Point(left + tl, top)
};
var segments = new PathSegmentCollection
{
// Top line + TopRight arc
new LineSegment(new Point(right - tr, top), true),
new ArcSegment(new Point(right, top + tr), new Size(tr, tr), 0, false, SweepDirection.Clockwise, true),
// Right line + BottomRight arc
new LineSegment(new Point(right, bottom - br), true),
new ArcSegment(new Point(right - br, bottom), new Size(br, br), 0, false, SweepDirection.Clockwise, true),
// Bottom line + BottomLeft arc
new LineSegment(new Point(left + bl, bottom), true),
new ArcSegment(new Point(left, bottom - bl), new Size(bl, bl), 0, false, SweepDirection.Clockwise, true),
// Left line + TopLeft arc
new LineSegment(new Point(left, top + tl), true),
new ArcSegment(new Point(left + tl, top), new Size(tl, tl), 0, false, SweepDirection.Clockwise, true)
};
figure.Segments = segments;
var geometry = new PathGeometry();
geometry.Figures.Add(figure);
return geometry;
}
//public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
//{
// var radius = (CornerRadius)values[0];
// var width = (double)values[1];
// var height = (double)values[2];
// var paramStr = parameter as string;
// _ = double.TryParse(paramStr, out var margin);
// var rectW = width - (2 * margin);
// var rectH = height - (2 * margin);
// //防止初始化时候小于0
// if (rectW < 0)
// {
// rectW = 0;
// }
// if (rectH < 0)
// {
// rectH = 0;
// }
// RectangleGeometry rectangleGeometry = new()
// {
// RadiusX = radius.BottomLeft,
// RadiusY = radius.TopRight,
// Rect = new Rect(margin, margin, rectW, rectH),
// };
// return rectangleGeometry;
//}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}

View File

@@ -0,0 +1,38 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Data;
namespace Melskin.Converters.Internal
{
/// <summary>
/// 数字转换为圆角转换器
/// </summary>
public class DoubleToCornerRadiusConverter : IValueConverter
{
/// <summary>
/// 单例
/// </summary>
public static readonly DoubleToCornerRadiusConverter Instance = new();
/// <inheritdoc />
public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
{
if (double.TryParse(value?.ToString(),out double result))
{
return new CornerRadius(result);
}
return new CornerRadius();
}
/// <inheritdoc />
public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
}

View File

@@ -0,0 +1,61 @@
// Cascader.cs
using System.Collections;
using System.Globalization;
using System.Windows.Data;
namespace Melskin.Converters.Internal;
/// <summary>
/// HasChildrenMultiConverter 类实现 IMultiValueConverter 接口,用于在 WPF 应用程序中检查数据项是否包含子项。
/// 该转换器通常应用于级联菜单或需要根据数据项是否有子项来决定显示行为的控件。通过提供一个静态实例Instance
/// 可以方便地在 XAML 中作为资源引用,从而简化绑定配置过程。此转换器接受两个输入值:一个是待检查的数据项,
/// 另一个是表示子项集合属性名称的字符串。如果指定的属性存在且其值为非空可枚举类型,则返回 true表明该数据项有子项
/// 否则返回 false。
/// </summary>
internal class HasChildrenMultiConverter : IMultiValueConverter
{
/// <summary>
/// HasChildrenMultiConverter 的单例实例,用于在 XAML 中通过静态资源引用。
/// 该实例提供了一个方便的方式来检查数据项是否包含子项,通常用于级联菜单或其他需要展开子项的控件中。
/// </summary>
public static readonly HasChildrenMultiConverter Instance = new();
/// <inheritdoc />
public object Convert(object[]? values, Type targetType, object parameter, CultureInfo culture)
{
// IMultiValueConverter 的输入是一个对象数组 (values)
// 根据我们在 XAML 中定义的 MultiBinding我们期望
// values[0] 是当前的数据项 (例如 Staff 对象)
// values[1] 是 SubmenuMemberPath 属性的值 (例如 "StaffList" 字符串)
if (values == null || values.Length < 2 || values[0] == null || values[1] == null)
{
return false;
}
var dataItem = values[0];
var childPropertyName = values[1].ToString();
if (string.IsNullOrEmpty(childPropertyName))
{
return false;
}
// 后续逻辑与之前完全相同:使用反射来检查子集合
var propertyInfo = dataItem.GetType().GetProperty(childPropertyName);
if (propertyInfo == null)
{
return false;
}
var children = propertyInfo.GetValue(dataItem) as IEnumerable;
return children != null && children.Cast<object>().Any();
}
/// <inheritdoc />
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}

View File

@@ -0,0 +1,49 @@
using System.Globalization;
using System.Windows.Data;
using Melskin.Controls;
namespace Melskin.Converters.Internal;
/// <summary>
/// 将色相值转换为画刷的转换器。此转换器实现了IValueConverter接口用于在绑定过程中将int类型的色相值转换成相应的SolidColorBrush。
/// 转换方法使用HsvToRgb静态方法来根据给定的色相、饱和度固定为1.0和亮度固定为1.0生成颜色并创建一个SolidColorBrush实例。
/// 如果输入值不是int类型则返回一个透明的画刷。
/// </summary>
internal class HueToBrushConverter : IValueConverter
{
public static HueToBrushConverter Instance { get; set; }= new();
/// <summary>
/// 将色相值转换为画刷的方法。此方法实现了IValueConverter接口中的Convert方法用于在数据绑定过程中将int类型的色相值转换成相应的SolidColorBrush。
/// 如果输入值是int类型则根据给定的色相、饱和度固定为1.0和亮度固定为1.0生成颜色并创建一个SolidColorBrush实例。如果输入值不是int类型则返回一个透明的画刷。
/// </summary>
/// <param name="value">要转换的色相值。</param>
/// <param name="t">目标类型,即转换后的数据类型,这里通常不需要使用。</param>
/// <param name="p">参数,通常用于传递额外的信息给转换器,本方法中未使用。</param>
/// <param name="c">文化信息,提供关于语言、国家/地区等的信息,以支持区域性特定的转换,本方法中未使用。</param>
/// <returns>如果输入值是有效的int类型色相值则返回对应的颜色画刷否则返回一个透明画刷。</returns>
public object Convert(object? value,
Type t,
object? p,
CultureInfo c) =>
value is int hue
? new SolidColorBrush(ColorPanel.HsvToRgb(hue,
1.0,
1.0,
255))
: Brushes.Transparent;
/// <summary>
/// 将画刷转换回色相值的方法。当前实现中此方法抛出NotImplementedException异常表示尚未实现该功能。
/// </summary>
/// <param name="v">要转换的画刷对象。</param>
/// <param name="t">目标类型,即转换后的数据类型。</param>
/// <param name="p">参数,通常用于传递额外的信息给转换器。</param>
/// <param name="c">文化信息,提供关于语言、国家/地区等的信息,以支持区域性特定的转换。</param>
/// <returns>由于方法未实现,实际上不返回任何有意义的数据,并且总是抛出异常。</returns>
/// <exception cref="NotImplementedException">始终抛出此异常因为ConvertBack方法在当前版本中没有具体实现。</exception>
public object ConvertBack(object? v,
Type t,
object? p,
CultureInfo c) =>
Binding.DoNothing;
}

View File

@@ -0,0 +1,75 @@
using System.Globalization;
using System.Windows.Data;
using Melskin.Controls;
namespace Melskin.Converters.Internal;
/// <summary>
/// 表示一个值转换器用于将色调Hue值转换为对应的RGB颜色。该转换器实现了IValueConverter接口
/// 通过Convert方法接收一个表示色调的int类型的值并将其转换成System.Windows.Media.Color结构体中的颜色。
/// 转换过程中饱和度和亮度均设为最大值1.0透明度设为完全不透明255。如果输入不是有效的int类型或者转换失败则返回透明色。
/// ConvertBack方法目前未实现尝试调用它会抛出NotImplementedException异常。
/// 此转换器适用于需要根据给定的色调快速生成对应RGB颜色的应用场景。
/// </summary>
internal class HueToColorConverter : IValueConverter
{
public object Convert(object? value,
Type t,
object? p,
CultureInfo c) =>
value is int hue
? ColorPanel.HsvToRgb(hue,
1.0,
1.0,
255)
: Colors.Transparent;
public object ConvertBack(object? value, Type t, object? p, CultureInfo c)
{
if (value is Color color)
{
// 将RGB颜色转换为HSV颜色
var r = color.R / 255.0;
var g = color.G / 255.0;
var b = color.B / 255.0;
var max = Math.Max(r, Math.Max(g, b));
var min = Math.Min(r, Math.Min(g, b));
var delta = max - min;
double h = 0, s = 0, v = max;
if (delta != 0)
{
if (r == max)
{
h = (g - b) / delta;
}
else if (g == max)
{
h = 2 + (b - r) / delta;
}
else if (b == max)
{
h = 4 + (r - g) / delta;
}
h *= 60;
if (h < 0)
{
h += 360;
}
s = delta / max;
}
return (int)Math.Round(h); // 返回整数色调值
}
throw new ArgumentException("Invalid input type for conversion.");
}
}

View File

@@ -0,0 +1,31 @@
using System.Globalization;
using System.Windows.Data;
namespace Melskin.Converters.Internal;
/// <summary>
/// IntensityToSlotBlurConverter 类实现 IValueConverter 接口,用于将给定的强度值转换为模糊效果的数值。
/// 此转换器接收一个 double 类型的强度值,并将其转换为对应的模糊效果大小。模糊效果的范围是从 4 到 12
/// 其中强度值越大,产生的模糊效果越强。
/// </summary>
[ValueConversion(typeof(double), typeof(double))]
internal sealed class IntensityToEmbossBlurConverter : IValueConverter
{
/// <summary>
/// Instance 是 IntensityToSlotBlurConverter 类的单例实例,用于在整个应用程序中共享同一转换器对象。
/// 通过使用此单例,可以避免在不同地方创建多个相同功能的转换器实例,从而提高性能和减少资源消耗。
/// </summary>
public static readonly IntensityToEmbossBlurConverter Instance = new();
public object Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
{
var intensity = (double)(value ?? 1);
//数值最大的时候,模糊达到预设最大值 4~12
//return 4 + intensity * 8;
return 4 + intensity * 8;
}
public object ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
{
return Binding.DoNothing;
}
}

View File

@@ -0,0 +1,27 @@
using System.Globalization;
using System.Windows.Data;
namespace Melskin.Converters.Internal;
/// <summary>
/// 将浮点数类型的强度值转换为左上边距的 Thickness 对象。该转换器适用于需要根据浮点数值动态调整 UI 元素边距的场景。
/// 当输入的强度值为 1 时,生成的边距最小,随着强度值减小,边距逐渐增大,从而影响 UI 元素显示效果,如阴影或光照效果的范围。
/// </summary>
[ValueConversion(typeof(double), typeof(Thickness))]
internal class IntensityToEmbossMarginLeftTopConverter : IValueConverter
{
public static readonly IntensityToEmbossMarginLeftTopConverter Instance = new();
public object Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
{
var intensity = (double)(value ?? 0.5);
//数值为1的时候边距最小露出的着色边框越大
var marginValue = 2 * intensity;
return new Thickness(-marginValue, -marginValue, marginValue, marginValue);
}
public object ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
{
return Binding.DoNothing;
}
}

View File

@@ -0,0 +1,29 @@
using System.Globalization;
using System.Windows.Data;
namespace Melskin.Converters.Internal;
/// <summary>
/// IntensityToEmbossMarginRightBottomConverter 转换器用于将浮点数强度值转换为 Thickness 结构,以应用于右下边距。
/// 该转换器主要用于创建浮雕效果的控件中,通过调整边距来模拟不同强度下的视觉效果。
/// 当输入的强度值为1时产生的边距最小从而使得着色边框更加明显。随着强度值减小边距增大
/// 使着色部分看起来更模糊或更不显著。
/// </summary>
[ValueConversion(typeof(double), typeof(Thickness))]
internal sealed class IntensityToEmbossMarginRightBottomConverter : IValueConverter
{
public static readonly IntensityToEmbossMarginRightBottomConverter Instance = new();
public object Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
{
var intensity = (double)(value ?? 0.5);
//数值为1的时候边距最小露出的着色边框越大
var marginValue = 4 * intensity;
return new Thickness(marginValue, marginValue, -marginValue, -marginValue);
}
public object ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
{
return Binding.DoNothing;
}
}

View File

@@ -0,0 +1,31 @@
using System.Globalization;
using System.Windows.Data;
namespace Melskin.Converters.Internal;
/// <summary>
/// IntensityToSlotBlurConverter 类实现了 IValueConverter 接口用于将强度值double 类型)转换为模糊半径(同样为 double 类型。此转换器在UI设计中特别适用于根据特定的强度参数动态调整元素的模糊效果。例如在实现视觉反馈或突出显示某一部分时可通过改变强度来控制模糊程度从而达到更好的用户体验。
/// </summary>
/// <remarks>
/// 该转换器定义了一个从4到8之间的线性映射关系其中输入的强度值被放大后加上一个基础值以确保输出结果始终落在指定范围内。这种转换逻辑简单直接易于理解与维护。
/// </remarks>
[ValueConversion(typeof(double), typeof(double))]
internal class IntensityToSlotBlurConverter : IValueConverter
{
/// <summary>
/// Instance 是 IntensityToSlotBlurConverter 类的单例实例,用于在整个应用程序中共享同一转换器对象。
/// 通过使用此单例,可以避免在不同地方创建多个相同功能的转换器实例,从而提高性能和减少资源消耗。
/// </summary>
public static readonly IntensityToSlotBlurConverter Instance = new();
public object Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
{
var intensity = (double)(value ?? 0);
//模糊范围 4~8
return 4 * intensity + 4;
}
public object ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
{
return Binding.DoNothing;
}
}

View File

@@ -0,0 +1,29 @@
using System.Globalization;
using System.Windows.Data;
namespace Melskin.Converters.Internal;
/// <summary>
/// 将光强度值转换为灯光边距的转换器。该转换器实现了 IValueConverter 接口,用于将双精度浮点数表示的光强度值转换成 Thickness 类型的对象,代表了灯光效果在用户界面中的边距。
/// </summary>
/// <remarks>
/// 该转换器主要用于 WPF 应用程序中,通过绑定到 UI 元素上,实现基于光强度动态调整边距的效果。它不支持从 Thickness 转换回光强度的逆向操作。
/// </remarks>
[ValueConversion(typeof(double), typeof(Thickness))]
internal class IntensityToSlotLightMarginConverter : IValueConverter
{
public static readonly IntensityToSlotLightMarginConverter Instance = new();
public object Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
{
var intensity = (double)(value ?? 0);
//此处与中央Margin匹配
var offset = 4 + intensity * 4;
return new Thickness(offset, offset, 0, 0);
}
public object ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
{
return Binding.DoNothing;
}
}

View File

@@ -0,0 +1,26 @@
using System.Globalization;
using System.Windows.Data;
namespace Melskin.Converters.Internal;
/// <summary>
///
/// </summary>
[ValueConversion(typeof(double), typeof(Thickness))]
internal class IntensityToSlotMarginConverter : IValueConverter
{
public static readonly IntensityToSlotMarginConverter Instance = new();
public object Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
{
var intensity = (double)(value ?? 0);
//边距越小,留下的边框越窄
var uniformValue = 4 * intensity + 4;
return new Thickness(uniformValue);
}
public object ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
{
return Binding.DoNothing;
}
}

View File

@@ -0,0 +1,28 @@
using System.Globalization;
using System.Windows.Data;
namespace Melskin.Converters.Internal;
/// <summary>
/// IntensityToSlotShadowMarginConverter 类用于将阴影强度double类型转换为阴影边距Thickness类型。此转换器特别适用于需要根据阴影强度动态调整UI元素阴影边距的场景。
/// 该类实现了IValueConverter接口提供了从双精度浮点数到厚度值的单向转换逻辑。转换过程中基于输入的强度值计算出一个偏移量并以此构建新的 Thickness 实例来表示阴影边距。
/// 注意:此转换器不支持反向转换操作。
/// </summary>
[ValueConversion(typeof(double), typeof(Thickness))]
internal class IntensityToSlotShadowMarginConverter : IValueConverter
{
public static readonly IntensityToSlotShadowMarginConverter Instance = new();
public object Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
{
var intensity = (double)(value ?? 0);
//此处与中央Margin匹配
var offset = 4 + intensity * 4;
return new Thickness(0, 0, offset, offset);
}
public object ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
{
return Binding.DoNothing;
}
}

View File

@@ -0,0 +1,30 @@
using System.Globalization;
using System.Windows.Data;
namespace Melskin.Converters.Internal;
/// <summary>
/// 隔行交错背景颜色
/// </summary>
[ValueConversion(typeof(int), typeof(Visibility))]
public class InterlacedBackgroundConverter : IValueConverter
{
/// <summary>
/// InterlacedBackgroundConverter 的单例实例,用于提供隔行交错背景颜色的转换功能。
/// 该实例可以直接在XAML中通过静态资源引用以便为列表项等控件设置交替背景颜色。
/// </summary>
public static readonly InterlacedBackgroundConverter Instance = new();
/// <inheritdoc />
public object Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
{
var line = (int)(value ?? throw new ArgumentNullException(nameof(value)));
return line % 2 == 0 ? Visibility.Collapsed : Visibility.Visible;
}
/// <inheritdoc />
public object ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
{
return Binding.DoNothing;
}
}

View File

@@ -0,0 +1,27 @@
using System.Globalization;
using System.Windows.Data;
namespace Melskin.Converters.Internal;
// 返回 1.0 - 输入值,用于反向不透明度
/// <summary>
/// InverseOpacityConverter 类用于实现不透明度的反转转换。给定一个介于 0.0 到 1.0 之间的双精度浮点数值,该转换器将返回 1.0 减去输入值的结果,从而达到反向不透明度的效果。
/// 此转换器特别适用于需要动态调整 UI 元素不透明度的应用场景中,比如在某些控件的状态切换时,改变背景或前景内容的可见性。
/// </summary>
internal class InverseOpacityConverter : IValueConverter
{
/// <summary>
/// 获取 InverseOpacityConverter 的唯一实例,用于在 XAML 或代码中直接引用以执行不透明度反转转换。
/// 该属性提供了一个静态的、可重用的 InverseOpacityConverter 实例,避免了每次使用时都创建新的对象,
/// 从而优化了性能。此实例特别适用于需要频繁调整 UI 元素不透明度的应用场景,如动态改变控件背景或前景内容的可见性。
/// </summary>
public static InverseOpacityConverter Instance { get; } = new();
/// <inheritdoc />
public object Convert(object? value, Type targetType, object? parameter, CultureInfo culture) => value is double d
? 1.0 - d
: 1.0;
/// <inheritdoc />
public object ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture) => Binding.DoNothing;
}

View File

@@ -0,0 +1,42 @@
using System.Globalization;
using System.Windows.Data;
namespace Melskin.Converters.Internal;
/// <summary>
/// 用于判断集合中的项目是否为最后一个项目的转换器。
/// 此转换器通常在数据绑定场景中使用,以识别并处理列表或集合的最后一个元素。
/// </summary>
/// <remarks>
/// 使用此转换器时,需要传递一个包含当前项和整个集合的参数。转换器将基于这些信息来确定当前项是否是集合中的最后一项。
/// </remarks>
internal class IsLastItemConverter : IMultiValueConverter
{
/// <summary>
/// IsLastItemConverter 的单例实例,用于在 XAML 中方便地引用和使用该转换器。
/// 通过此静态只读属性,可以在多个地方重复使用同一个 IsLastItemConverter 实例,
/// 而无需每次创建新的实例,从而提高性能并简化资源管理。
/// </summary>
public static readonly IsLastItemConverter Instance = new();
/// <inheritdoc/>
public object Convert(object[]? values, Type targetType, object parameter, CultureInfo culture)
{
if (values == null || values.Length < 2 || values[0] == null || values[1] == null)
{
return false;
}
if (values[0] is ItemsControl itemsControl && values[1] is Control item)
{
return itemsControl.ItemContainerGenerator.IndexFromContainer(item) == itemsControl.Items.Count - 1;
}
return Binding.DoNothing;
}
/// <inheritdoc/>
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
return [value];
}
}

View File

@@ -0,0 +1,30 @@
using System;
using System.Globalization;
using System.Windows.Data;
namespace Melskin.Converters.Internal
{
internal class NegativeConverter : IValueConverter
{
public static readonly NegativeConverter Instance = new NegativeConverter();
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
//if (value is double d) return -d;
//if (value is int i) return -i;
//if (value is float f) return -f;
//if (value is decimal dec) return -dec;
//if (value is long l) return -l;
if (value is Thickness thickness)
{
return new Thickness(-thickness.Left, -thickness.Top, -thickness.Right, -thickness.Bottom);
}
// 也可以使用 Convert.ToDouble 进行更通用的转换
return -(System.Convert.ToDouble(value));
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
}

View File

@@ -0,0 +1,59 @@
// Cascader.cs
using System.Globalization;
using System.Windows.Data;
namespace Melskin.Converters.Internal;
/// <summary>
/// 该类实现了 IMultiValueConverter 接口,用于将多个绑定值转换为单个输出值。特别适用于需要从数据项中动态获取属性值的场景,
/// 比如在 WPF 的 MultiBinding 中使用时,可以根据提供的属性名称动态地获取对象的属性值。
/// </summary>
internal class PropertyValueConverter : IMultiValueConverter
{
/// <summary>
/// 代表 PropertyValueConverter 类的单例实例。此实例在整个应用程序中可重用,用于在数据绑定中执行属性值转换。
/// 它特别适用于需要将多个源值(如数据项和属性名称)转换为单一输出值的情况。
/// </summary>
public static readonly PropertyValueConverter Instance = new();
/// <inheritdoc />
public object? Convert(object[]? values, Type targetType, object parameter, CultureInfo culture)
{
// 根据 MultiBinding 的定义,我们期望:
// values[0] 是当前的数据项 (例如 Staff 对象)
// values[1] 是 DisplayMemberPath 属性的值 (例如 "Name" 字符串)
if (values == null || values.Length < 2 || values[0] == null || values[1] == null)
{
return null;
}
var dataItem = values[0];
var propertyName = values[1].ToString();
if (string.IsNullOrEmpty(propertyName))
{
// 如果没有提供 DisplayMemberPath则回退到调用 .ToString()
return dataItem.ToString();
}
try
{
// 使用反射动态获取属性的值
var propertyInfo = dataItem.GetType().GetProperty(propertyName);
return propertyInfo?.GetValue(dataItem);
}
catch
{
// 如果属性不存在或发生其他错误,返回 null
return null;
}
}
/// <inheritdoc />
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}

View File

@@ -0,0 +1,73 @@
using System.Globalization;
using System.Text.RegularExpressions;
using System.Windows.Data;
using System.Windows.Documents;
namespace Melskin.Converters.Internal;
/// <summary>
/// StringToCodeConverter 类实现了 IValueConverter 接口,用于将字符串转换为具有代码样式的 RichTextBox 控件。该类提供了一个静态实例 Instance用于在 XAML 中方便地引用。
/// 通过 Convert 方法可以将输入的字符串转换为富文本格式并且会对字符串中的特殊字符如引号、尖括号等进行高亮显示。ConvertBack 方法返回 DependencyProperty.UnsetValue表示不支持反向转换。
/// </summary>
internal class StringToCodeConverter : IValueConverter
{
//显式静态构造函数告诉c#编译器
//不将type标记为beforefieldinit
static StringToCodeConverter()
{
}
private StringToCodeConverter()
{
}
/// <summary>
///
/// </summary>
public static StringToCodeConverter Instance { get; } = new();
/// <inheritdoc />
public object Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
{
var str = value as string;
var textBox = new RichTextBox()
{
IsReadOnly = true,
FontSize = 12,
Foreground = Brushes.Red,
BorderThickness = new Thickness(0),
};
if (string.IsNullOrEmpty(str)) return textBox;
const string pattern = "=\".*?\"|[</>]";
var index = 0;
var document = new FlowDocument();
var paragraph = new Paragraph();
var inlines = paragraph.Inlines;
str = str?.Replace(@"\n", Environment.NewLine).Replace(@"\t", "\t");
if (str != null)
{
foreach (Match match in Regex.Matches(str, pattern))
{
inlines.Add(str.Substring(index, match.Index - index));
inlines.Add(new Run(match.Value) { Foreground = Brushes.Blue });
index = match.Index + match.Length;
}
inlines.Add(str.Substring(index));
}
document.Blocks.Add(paragraph);
textBox.Document = document;
return textBox;
}
/// <inheritdoc />
public object ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
{
return DependencyProperty.UnsetValue;
}
}

View File

@@ -0,0 +1,63 @@
using System.Globalization;
using System.Text.RegularExpressions;
using System.Windows.Data;
using System.Windows.Documents;
namespace Melskin.Converters.Internal;
/// <summary>
/// StringToMarkDownConverter 类用于将普通字符串转换为具有Markdown格式的富文本显示。该类实现了IValueConverter接口主要功能是通过正则表达式识别并处理字符串中的代码块以反引号`包围的部分将其转换为带有特定前景色和背景色的富文本Run对象并嵌入到TextBlock中返回。
/// </summary>
/// <remarks>
/// 本转换器特别适用于需要在WPF应用程序中展示含有内联代码样式的Markdown文本的情况。它会自动处理输入字符串中的换行符并将匹配到的代码片段高亮显示。
/// </remarks>
internal class StringToMarkDownConverter : IValueConverter
{
// Explicit static constructor to tell C# compiler
// not to mark type as beforefieldinit
private StringToMarkDownConverter() { }
/// <summary>
/// 获取 StringToMarkDownConverter 的单例实例。此属性确保在整个应用程序生命周期中只有一个 StringToMarkDownConverter 实例被创建和使用,从而提高性能并减少内存占用。
/// </summary>
/// <returns>返回一个 StringToMarkDownConverter 类型的实例。</returns>
/// <remarks>
/// 该属性通过延迟初始化Lazy Initialization模式来实现单例模式首次访问时才创建实例。这样可以保证在多线程环境下也能安全地获取到唯一的实例。
/// </remarks>
public static readonly StringToMarkDownConverter Instance = new();
/// <inheritdoc />
public object Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
{
var str = value as string;
var textBlock = new TextBlock() { TextWrapping = TextWrapping.Wrap };
if (string.IsNullOrEmpty(str)) return textBlock;
const string pattern = @"`(.*?)`";
var index = 0;
var inlines = textBlock.Inlines;
str = str?.Replace(@"\n", Environment.NewLine);
var foreground = new SolidColorBrush(Color.FromRgb(31, 46, 59));
var background = new SolidColorBrush(Color.FromRgb(242, 244, 245));
if (str == null) return textBlock;
foreach (Match match in Regex.Matches(str, pattern))
{
inlines.Add(str.Substring(index, match.Index - index));
inlines.Add(new Run(match.Value.Replace('`', ' ')) { Foreground = foreground, Background = background });
index = match.Index + match.Length;
}
inlines.Add(str.Substring(index));
return textBlock;
}
/// <inheritdoc />
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return DependencyProperty.UnsetValue;
}
}

View File

@@ -0,0 +1,32 @@
using System.Globalization;
using System.Windows.Data;
using Melskin.Assets;
namespace Melskin.Converters.Internal;
/// <summary>
/// SymbolToCharConverter 类用于将 MaterialSymbol 枚举值转换为其对应的字符表示。此转换器实现了 IValueConverter 接口,主要用于 WPF 应用程序中绑定数据时进行类型转换。
/// </summary>
/// <remarks>
/// 该类提供了一个静态实例 Instance可以直接在 XAML 中引用以避免重复创建对象。转换逻辑仅支持从 MaterialSymbol 到字符串的单向转换Convert 方法不支持反向转换ConvertBack 方法未实现)。
/// </remarks>
internal class SymbolToCharConverter : IValueConverter
{
public static readonly SymbolToCharConverter Instance = new();
/// <inheritdoc />
public object Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
{
if (value is MaterialSymbol symbol)
{
return ((char)(int)symbol).ToString();
}
return string.Empty;
}
/// <inheritdoc />
public object ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}

View File

@@ -0,0 +1,48 @@
using System.Globalization;
using System.Windows.Data;
namespace Melskin.Converters.Internal;
// 根据进度、总宽度和滑块宽度(总高度)直接计算滑块位置
/// <summary>
/// ThumbPositionConverter 类用于根据进度、总宽度和滑块宽度(或总高度)来计算滑块的位置。此转换器实现了 IMultiValueConverter 接口,允许它处理多个输入值,并返回一个基于这些输入值的输出值。
/// 该类提供了一个静态实例 (Instance),可以方便地在 XAML 中重用而无需创建新的实例。
/// </summary>
/// <remarks>
/// 使用时需要传递三个参数进度0.0 到 1.0 之间的数值)、总宽度以及滑块宽度(或当布局为垂直方向时使用总高度)。转换器会根据这些信息计算出滑块应该放置的确切位置。
/// 如果输入值不正确或存在异常情况,则返回 0.0 作为默认位置。
/// </remarks>
internal class ThumbPositionConverter : IMultiValueConverter
{
/// <summary>
/// 获取 ThumbPositionConverter 类的单例实例。
/// 该实例用于根据进度、总宽度和滑块宽度(总高度)直接计算滑块位置。
/// </summary>
/// <remarks>
/// 使用此属性可以方便地在 XAML 或代码中引用 ThumbPositionConverter 的同一实例,从而避免了多次创建对象带来的性能开销。
/// </remarks>
public static ThumbPositionConverter Instance { get; } = new();
/// <inheritdoc />
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
if (values.Length < 3 || values.Any(v => v == null || v == DependencyProperty.UnsetValue))
return 0.0;
try
{
var progress = System.Convert.ToDouble(values[0]);
var totalWidth = System.Convert.ToDouble(values[1]);
var thumbWidth = System.Convert.ToDouble(values[2]);
var maxDistance = totalWidth - thumbWidth;
return progress * Math.Max(0, maxDistance);
}
catch
{
return 0.0;
}
}
/// <inheritdoc />
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) => throw new NotImplementedException(
);
}

View File

@@ -0,0 +1,44 @@
using System.Globalization;
using System.Windows.Data;
namespace Melskin.Converters.Internal;
/// <summary>
/// 该转换器实现了IMultiValueConverter接口用于将值转换为范围宽度。它主要用于进度条或滑块控件中根据当前值、最小值和最大值来计算并返回一个宽度值。
/// 通过传入的参数(以字符串形式),可以额外指定边距,该边距会从总宽度中减去。最终返回的宽度基于当前值在最小值与最大值之间的比例,并且考虑了调整后的实际宽度。
/// </summary>
internal class ValueToRangeWidthConverter : IMultiValueConverter
{
public static readonly ValueToRangeWidthConverter Instance = new();
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
var paramStr = parameter as string;
_ = double.TryParse(paramStr, out var margin);
var minimum = (double)values[0];
var maximum = (double)values[1];
var currentValue = (double)values[2];
var width = (double)values[3];
var actualWidth = width - 2 * margin;
if (actualWidth < 0)
{
actualWidth = 0;
}
if (currentValue < minimum)
{
return 0;
}
if (currentValue > maximum)
{
return actualWidth;
}
return ((currentValue - minimum) / (maximum - minimum)) * actualWidth;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}