using System.Windows.Markup; using System.Windows.Media.Imaging; using System.Xaml; using Melskin.Appearance; using Melskin.Assets; using Melskin.Controls; namespace Melskin.Markup { /// /// 方便在 XAML 中内联创建 IconElement 的标记扩展。 /// 用法示例: /// /// xmlns:neo="clr-namespace:Melskin.Markup;assembly=Melskin" /// /// [MarkupExtensionReturnType(typeof(IconElement))] public class IconExtension : MarkupExtension { /// /// 获取或设置图标符号的字符串表示形式。此属性用于指定要显示的图标的名称。 /// /// /// 通过设置此属性,可以使用一个字符串来标识所需的图标。如果设置了此属性且未被设置,则会尝试将这个字符串解析为有效的图标符号。 /// public string? Symbol { get; set; } /// /// 获取或设置图标的值。此属性用于直接指定图标的具体值。 /// /// /// 通过设置此属性,可以直接指定一个具体的图标值。如果被设置,则会优先使用这个值来显示图标,而不是通过字符串解析的方式。 /// public MaterialSymbol? SymbolValue { get; set; } /// /// 获取或设置图标的字符表示。此属性用于指定要显示的图标的具体字符。 /// /// /// 通过设置此属性,可以使用一个字符来标识所需的图标。这个字符通常是从特定字体中选择的一个图标符号,例如BoxIcons等图标字体中的某个字符。 /// public string? Glyph { get; set; } /// /// 获取或设置图标图像的源。此属性用于指定要显示的图像的位置。 /// /// /// 通过设置此属性,可以使用一个字符串来标识图像的来源路径。如果设置了此属性且其他与图像相关的属性未被设置,则会尝试将这个字符串解析为有效的图像源,并加载相应的图像。 /// public string? Image { get; set; } /// /// 获取或设置几何图形的字符串表示形式。此属性用于指定要显示的几何图形的数据。 /// /// /// 通过设置此属性,可以使用一个字符串来定义所需的几何图形。如果设置了此属性且字符串有效,则会尝试将这个字符串解析为几何图形数据并应用到图标元素上。 /// public string? Geo { get; set; } /// /// 获取或设置用于查找绘图资源的键。此属性允许通过指定的键从资源中检索绘图对象。 /// /// /// 当设置了此属性且提供的键有效时,将尝试从当前资源上下文中查找对应的绘图资源,并将其应用于图标元素。如果找到了与键匹配的绘图资源,则该资源会被用作图标的内容。 /// public string? DrawingKey { get; set; } /// /// 获取或设置用于查找画刷资源的键。此属性允许通过资源字典中的键来指定一个画刷或绘图画刷。 /// /// /// 通过设置此属性,可以使用一个字符串作为键来引用应用程序资源中的画刷对象()或绘图画刷对象()。如果设置了此属性,并且在资源字典中找到了对应的资源,则会将该资源应用到图标元素上。 /// public string? BrushKey { get; set; } /// /// 获取或设置图标的大小。此属性用于指定图标在显示时的尺寸。 /// /// /// 通过设置此属性,可以控制图标元素的高度和宽度。如果设置了属性而未明确设置,则将默认采用的值。 /// public double Size { get; set; } = double.NaN; /// /// 获取或设置图标的宽度。此属性用于指定图标元素的宽度。 /// /// /// 通过设置此属性,可以自定义图标元素的宽度。如果设置了此属性且没有设置属性,则会直接使用这个值作为图标的宽度。如果同时设置了,则优先级更高。 /// public double Width { get; set; } = double.NaN; /// /// 获取或设置图标的高度。此属性用于指定图标在显示时的高度。 /// /// /// 通过设置此属性,可以控制图标元素的高度。如果设置了属性而未明确设置,则将默认采用的值。 /// public double Height { get; set; } = double.NaN; /// /// 获取或设置图标的前景色。此属性用于指定图标中文字或几何图形部分的颜色。 /// /// /// 通过设置此属性,可以自定义图标的颜色。如果未显式设置此属性,则可能使用默认值或其他相关属性来确定颜色。 /// public Brush? Foreground { get; set; } /// /// 获取或设置几何图形填充的画刷。此属性用于指定图标中几何图形部分的填充颜色。 /// /// /// 通过设置此属性,可以自定义图标的几何图形部分的填充效果。如果未显式设置此属性,则可能使用默认值或其他相关属性(如)来确定填充颜色。 /// public Brush? GeometryFill { get; set; } /// /// 获取或设置几何图形描边的颜色。此属性用于指定图标的几何形状边缘所使用的画刷。 /// /// /// 通过设置此属性,可以自定义图标中几何形状边缘的颜色。如果设置了此属性,则会使用指定的画刷来绘制几何形状的边缘。 /// public Brush? GeometryStroke { get; set; } /// /// 获取或设置几何图形描边的厚度。此属性用于控制图标的几何形状描边宽度。 /// /// /// 通过设置此属性,可以自定义图标中几何图形描边的宽度。如果设置了此属性且值不是NaN,则会应用指定的描边厚度。 /// public double GeometryStrokeThickness { get; set; } = double.NaN; /// /// 获取或设置图标内容的类型。此属性用于指定图标的显示方式。 /// /// /// 通过设置此属性,可以定义图标是作为材料图标、字形、图像、几何图形、绘图还是画刷来展示。默认值为Auto,表示自动选择最合适的内容类型。 /// public IconElement.IconContentType ContentType { get; set; } = IconElement.IconContentType.Auto; /// /// 获取或设置图标的字体系列。此属性用于指定图标所使用的字体,以便正确显示图标符号。 /// /// /// 通过设置此属性,可以指定一个字体系列来确保图标能够以正确的样式显示。如果设置了此属性,则会应用到图标元素的字体系列上。 /// public FontFamily? FontFamily { get; set; } /// /// 获取或设置图标的字体大小。此属性用于控制图标显示时的字体大小。 /// /// /// 通过设置此属性,可以指定图标字体的具体大小。如果设置了此属性且未被其他尺寸属性覆盖,则会使用这个值来设置图标的字体大小。 /// public double FontSize { get; set; } = double.NaN; /// /// 获取或设置图像的拉伸模式。此属性决定了图像如何填充其布局空间。 /// /// /// 通过设置此属性,可以控制图像在显示时的拉伸方式。默认值为 ,表示图像将按比例缩放以适应可用空间,同时保持其原始宽高比。 /// public Stretch ImageStretch { get; set; } = Stretch.Uniform; /// /// 获取或设置图标元素的边距。此属性用于控制图标与其容器之间的空间。 /// /// /// 通过设置此属性,可以自定义图标在布局中的位置和间距。边距值是一个类型的对象,允许指定左、上、右、下四个方向的边距。 /// public Thickness Margin { get; set; } /// /// 获取或设置图标元素的水平对齐方式。此属性用于指定图标在其容器中的水平位置。 /// /// /// 通过设置此属性,可以控制图标在水平方向上的对齐方式。默认值为居中对齐(HorizontalAlignment.Center)。 /// public HorizontalAlignment HorizontalAlignment { get; set; } = HorizontalAlignment.Center; /// /// 获取或设置图标在垂直方向上的对齐方式。此属性用于控制图标在其容器中的垂直位置。 /// /// /// 通过设置此属性,可以指定图标在其容器中是顶部对齐、居中对齐还是底部对齐。默认情况下,图标是居中对齐的。 /// public VerticalAlignment VerticalAlignment { get; set; } = VerticalAlignment.Center; /// /// 用于在XAML中创建图标元素的标记扩展。通过设置不同的属性,可以自定义图标的样式和内容。 /// public IconExtension() { } /// /// 用于在XAML中创建图标元素的标记扩展。通过设置不同的属性,可以自定义图标的样式、内容和外观。 /// public IconExtension(string symbol) { Symbol = symbol; } /// /// 用于在XAML中创建图标元素的标记扩展。通过设置不同的属性,可以自定义图标的样式、内容和外观。 /// public IconExtension(MaterialSymbol materialSymbol) { SymbolValue = materialSymbol; } /// public override object ProvideValue(IServiceProvider serviceProvider) { var icon = new IconElement(); // 尺寸 if (!double.IsNaN(Size)) { if (double.IsNaN(Width)) icon.Width = Size; if (double.IsNaN(Height)) icon.Height = Size; if (double.IsNaN(FontSize)) FontSize = Size; } if (!double.IsNaN(Width)) icon.Width = Width; if (!double.IsNaN(Height)) icon.Height = Height; if (!double.IsNaN(FontSize)) icon.SetValue(Control.FontSizeProperty, FontSize); if (FontFamily != null) icon.SetValue(Control.FontFamilyProperty, FontFamily); if (Margin != default) icon.Margin = Margin; icon.HorizontalAlignment = HorizontalAlignment; icon.VerticalAlignment = VerticalAlignment; if (Foreground != null) icon.SetValue(Control.ForegroundProperty, Foreground); // 上下文(已移除元组) var ctx = GetContext(serviceProvider); var resourceOwner = ctx.RootObject as FrameworkElement ?? ctx.TargetObject as FrameworkElement; // Drawing if (!string.IsNullOrEmpty(DrawingKey)) { var drawingObj = TryFindResource(resourceOwner, DrawingKey); if (drawingObj is Drawing d) { icon.Drawing = d; } } // DrawingBrush / Brush if (!string.IsNullOrEmpty(BrushKey)) { var brushObj = TryFindResource(resourceOwner, BrushKey); switch (brushObj) { case DrawingBrush db: icon.DrawingBrush = db; break; case Brush b: GeometryFill ??= b; break; } } // Geometry if (!string.IsNullOrEmpty(Geo)) { try { var g = Geometry.Parse(Geo); if (g.CanFreeze) g.Freeze(); icon.Geometry = g; } catch { // ignored } } // Image if (!string.IsNullOrEmpty(Image)) { try { var uri = TryCreateUri(Image); if (uri != null) { var bmp = new BitmapImage(); bmp.BeginInit(); bmp.UriSource = uri; bmp.CacheOption = BitmapCacheOption.OnLoad; bmp.EndInit(); if (bmp.CanFreeze) bmp.Freeze(); icon.ImageSource = bmp; } } catch { } } // Glyph if (!string.IsNullOrEmpty(Glyph)) icon.Glyph = Glyph; // Symbol if (SymbolValue.HasValue) icon.Symbol = SymbolValue.Value; else if (!string.IsNullOrEmpty(Symbol)) icon.Symbol = IconElement.Parse(Symbol); // ContentType if (ContentType != IconElement.IconContentType.Auto) icon.ContentType = ContentType; // Geometry 样式 if (icon.Geometry != null) { if (GeometryFill != null) icon.GeometryFill = GeometryFill; else if (Foreground != null && icon.GeometryFill == System.Windows.Media.Brushes.Black) icon.GeometryFill = Foreground; if (GeometryStroke != null) icon.GeometryStroke = GeometryStroke; if (!double.IsNaN(GeometryStrokeThickness)) icon.GeometryStrokeThickness = GeometryStrokeThickness; } icon.ImageStretch = ImageStretch; return icon; } private sealed class ProvideContext { public object? TargetObject { get; set; } public object? TargetProperty { get; set; } public object? RootObject { get; set; } } private static ProvideContext GetContext(IServiceProvider serviceProvider) { object? targetObject = null; object? targetProperty = null; object? rootObject = null; var pvt = serviceProvider?.GetService(typeof(IProvideValueTarget)) as IProvideValueTarget; if (pvt != null) { targetObject = pvt.TargetObject; targetProperty = pvt.TargetProperty; } var rop = serviceProvider?.GetService(typeof(IRootObjectProvider)) as IRootObjectProvider; if (rop != null) { rootObject = rop.RootObject; } return new ProvideContext { TargetObject = targetObject, TargetProperty = targetProperty, RootObject = rootObject }; } private static object? TryFindResource(FrameworkElement? fe, string? key) { if (fe == null||key == null) return null; var found = fe?.TryFindResource(key); if (found != null) return found; if (Application.Current != null && Application.Current.Resources.Contains(key)) return Application.Current.Resources[key]; else { return ThemeManager.Current?.TryFindResource(key); } //return null; } private static Uri? TryCreateUri(string? path) { if (string.IsNullOrEmpty(path)) return null; if (path!.IndexOf(";component/", StringComparison.OrdinalIgnoreCase) >= 0 || path.StartsWith("pack://", StringComparison.OrdinalIgnoreCase)) { if (!path.StartsWith("pack://", StringComparison.OrdinalIgnoreCase)) path = "pack://application:,,," + (path.StartsWith("/") ? path : "/" + path); return new Uri(path, UriKind.Absolute); } return Uri.TryCreate(path, UriKind.RelativeOrAbsolute, out var u) ? u : null; } } }