月更
This commit is contained in:
498
AntDesignWPF/Controls/Icon.cs
Normal file
498
AntDesignWPF/Controls/Icon.cs
Normal file
@@ -0,0 +1,498 @@
|
||||
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
using System.Windows;
|
||||
using System.Windows.Documents;
|
||||
using System.Windows.Media;
|
||||
using AntDesign.WPF.Contracts;
|
||||
using AntDesign.WPF.Utils;
|
||||
|
||||
namespace AntDesign.WPF.Controls;
|
||||
/// <summary>
|
||||
/// 头像、警告提示语义矢量图形
|
||||
/// </summary>
|
||||
public class Icon : FrameworkElement, ISpinable
|
||||
{
|
||||
#region Fields
|
||||
|
||||
private Geometry definingGeometry;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Document Properties
|
||||
|
||||
/// <summary>
|
||||
/// DependencyProperty for <see cref="FontSize" /> property.
|
||||
/// </summary>
|
||||
public static readonly DependencyProperty FontSizeProperty =
|
||||
TextElement.FontSizeProperty.AddOwner(typeof(Icon));
|
||||
|
||||
/// <summary>
|
||||
/// The FontSize property specifies the size of the font.
|
||||
/// </summary>
|
||||
[TypeConverter(typeof(FontSizeConverter))]
|
||||
[Localizability(LocalizationCategory.None)]
|
||||
public double FontSize
|
||||
{
|
||||
get { return (double)GetValue(FontSizeProperty); }
|
||||
set { SetValue(FontSizeProperty, value); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// DependencyProperty setter for <see cref="FontSize" /> property.
|
||||
/// </summary>
|
||||
/// <param name="element">The element to which to write the attached property.</param>
|
||||
/// <param name="value">The property value to set</param>
|
||||
public static void SetFontSize(DependencyObject element, double value)
|
||||
{
|
||||
if (element == null)
|
||||
{
|
||||
throw new ArgumentNullException("element");
|
||||
}
|
||||
|
||||
element.SetValue(FontSizeProperty, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// DependencyProperty getter for <see cref="FontSize" /> property.
|
||||
/// </summary>
|
||||
/// <param name="element">The element from which to read the attached property.</param>
|
||||
[TypeConverter(typeof(FontSizeConverter))]
|
||||
public static double GetFontSize(DependencyObject element)
|
||||
{
|
||||
if (element == null)
|
||||
{
|
||||
throw new ArgumentNullException("element");
|
||||
}
|
||||
|
||||
return (double)element.GetValue(FontSizeProperty);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// DependencyProperty for <see cref="Foreground" /> property.
|
||||
/// </summary>
|
||||
public static readonly DependencyProperty ForegroundProperty =
|
||||
TextElement.ForegroundProperty.AddOwner(typeof(Icon));
|
||||
|
||||
/// <summary>
|
||||
/// The Foreground property specifies the foreground brush of an element's text content.
|
||||
/// </summary>
|
||||
public Brush Foreground
|
||||
{
|
||||
get { return (Brush)GetValue(ForegroundProperty); }
|
||||
set { SetValue(ForegroundProperty, value); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// DependencyProperty setter for <see cref="Foreground" /> property.
|
||||
/// </summary>
|
||||
/// <param name="element">The element to which to write the attached property.</param>
|
||||
/// <param name="value">The property value to set</param>
|
||||
public static void SetForeground(DependencyObject element, Brush value)
|
||||
{
|
||||
if (element == null)
|
||||
{
|
||||
throw new ArgumentNullException("element");
|
||||
}
|
||||
|
||||
element.SetValue(ForegroundProperty, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// DependencyProperty getter for <see cref="Foreground" /> property.
|
||||
/// </summary>
|
||||
/// <param name="element">The element from which to read the attached property.</param>
|
||||
public static Brush GetForeground(DependencyObject element)
|
||||
{
|
||||
if (element == null)
|
||||
{
|
||||
throw new ArgumentNullException("element");
|
||||
}
|
||||
|
||||
return (Brush)element.GetValue(ForegroundProperty);
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty BackgroundProperty =
|
||||
TextElement.BackgroundProperty.AddOwner(
|
||||
typeof(Icon),
|
||||
new FrameworkPropertyMetadata(
|
||||
Brushes.Transparent,
|
||||
FrameworkPropertyMetadataOptions.AffectsRender));
|
||||
|
||||
/// <summary>
|
||||
/// The Background property defines the brush used to fill the content area.
|
||||
/// </summary>
|
||||
public Brush Background
|
||||
{
|
||||
get { return (Brush)GetValue(BackgroundProperty); }
|
||||
set { SetValue(BackgroundProperty, value); }
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
|
||||
/// <summary>
|
||||
/// Get the geometry that defines this icon.
|
||||
/// </summary>
|
||||
protected Geometry DefiningGeometry
|
||||
{
|
||||
get
|
||||
{
|
||||
if (definingGeometry == null)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(Type))
|
||||
{
|
||||
var key = "anticon." + Type.ToLower();
|
||||
|
||||
// With theme suffix.
|
||||
if (Theme == IconTheme.Filled)
|
||||
{
|
||||
key += ".fill";
|
||||
} else if (Theme == IconTheme.Colorful)
|
||||
{
|
||||
key += ".colorful";
|
||||
}
|
||||
|
||||
definingGeometry = TryFindResource(key) as Geometry ?? Geometry.Empty;
|
||||
}
|
||||
else
|
||||
{
|
||||
definingGeometry = Geometry.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
return definingGeometry;
|
||||
}
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty TypeProperty =
|
||||
DependencyProperty.Register("Type", typeof(string), typeof(Icon),
|
||||
new FrameworkPropertyMetadata(null,
|
||||
FrameworkPropertyMetadataOptions.AffectsMeasure | FrameworkPropertyMetadataOptions.AffectsRender, OnSpinChanged));
|
||||
|
||||
/// <summary>
|
||||
/// Gets/sets the type of the ant design icon.
|
||||
/// </summary>
|
||||
public string Type
|
||||
{
|
||||
get { return (string)GetValue(TypeProperty); }
|
||||
set { SetValue(TypeProperty, value); }
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty ThemeProperty =
|
||||
DependencyProperty.Register("Theme", typeof(IconTheme), typeof(Icon),
|
||||
new FrameworkPropertyMetadata(IconTheme.Outlined, FrameworkPropertyMetadataOptions.AffectsRender));
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Gets/sets the theme of the ant design icon.
|
||||
/// </summary>
|
||||
public IconTheme Theme
|
||||
{
|
||||
get { return (IconTheme)GetValue(ThemeProperty); }
|
||||
set { SetValue(ThemeProperty, value); }
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty SpinProperty =
|
||||
DependencyProperty.Register("Spin", typeof(bool?), typeof(Icon), new PropertyMetadata(null, OnSpinChanged));
|
||||
|
||||
/// <summary>
|
||||
/// Gets/sets whether the icon has a spin animation.
|
||||
/// </summary>
|
||||
public bool? Spin
|
||||
{
|
||||
get { return (bool?)GetValue(SpinProperty); }
|
||||
set { SetValue(SpinProperty, value); }
|
||||
}
|
||||
|
||||
private static void OnSpinChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
(d as Icon).SetSpinAnimation();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Attached Propperties
|
||||
|
||||
/// <summary>
|
||||
/// Why is it not a dependency property?
|
||||
/// icons are introduced by way of resources. if you define them as dependency properties, you will lose more flexibility.
|
||||
/// For example, each icon needs to use different stretch parameters.
|
||||
/// </summary>
|
||||
public static readonly DependencyProperty ViewBoxProperty =
|
||||
DependencyProperty.RegisterAttached("ViewBox", typeof(Rect), typeof(Icon), new PropertyMetadata(new Rect(0, 0, 1024, 1024)), OnViewBoxValidate);
|
||||
|
||||
private static bool OnViewBoxValidate(object value)
|
||||
{
|
||||
var viewBox = (Rect)value;
|
||||
return viewBox.IsEmpty || (viewBox.Width >= 0 && viewBox.Height >= 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the rectangular area of the geometric stretch.
|
||||
/// </summary>
|
||||
[AttachedPropertyBrowsableForType(typeof(Geometry))]
|
||||
public static Rect GetViewBox(DependencyObject obj)
|
||||
{
|
||||
return (Rect)obj.GetValue(ViewBoxProperty);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the rectangular area of the geometric stretch.
|
||||
/// </summary>
|
||||
public static void SetViewBox(DependencyObject obj, Rect value)
|
||||
{
|
||||
obj.SetValue(ViewBoxProperty, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// When you need colorful icons, you need to be able to support custom brushes.
|
||||
/// </summary>
|
||||
public static readonly DependencyProperty FillProperty =
|
||||
DependencyProperty.RegisterAttached("Fill", typeof(Brush), typeof(Icon), new PropertyMetadata(null));
|
||||
|
||||
/// <summary>
|
||||
/// Gets the brush that fill the geometry.
|
||||
/// </summary>
|
||||
/// <param name="obj"></param>
|
||||
/// <returns></returns>
|
||||
[AttachedPropertyBrowsableForType(typeof(Geometry))]
|
||||
public static Brush GetFill(DependencyObject obj)
|
||||
{
|
||||
return (Brush)obj.GetValue(FillProperty);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the brush to fill the geometry. Valid when Theme is colorful.
|
||||
/// </summary>
|
||||
public static void SetFill(DependencyObject obj, Brush value)
|
||||
{
|
||||
obj.SetValue(FillProperty, value);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
static Icon()
|
||||
{
|
||||
DefaultStyleKeyProperty.OverrideMetadata(typeof(Icon), new FrameworkPropertyMetadata(typeof(Icon)));
|
||||
}
|
||||
|
||||
public Icon()
|
||||
{
|
||||
Loaded += (s, e) => SetSpinAnimation();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Overrides
|
||||
|
||||
/// <summary>
|
||||
/// Notification that a specified property has been invalidated.
|
||||
/// </summary>
|
||||
/// <param name="e">EventArgs that contains the property, metadata, old value, and new value for this change</param>
|
||||
protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
if (e.NewValue != e.OldValue && (e.Property == TypeProperty || e.Property == ThemeProperty))
|
||||
{
|
||||
// Reset definition geometry.
|
||||
definingGeometry = null;
|
||||
}
|
||||
|
||||
base.OnPropertyChanged(e);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates DesiredSize of the icon. Called by parent UIElement during is the first pass of layout.
|
||||
/// </summary>
|
||||
/// <param name="constraint">Constraint size is an "upper limit" that should not exceed.</param>
|
||||
/// <returns>icon desired size.</returns>
|
||||
protected override Size MeasureOverride(Size constraint)
|
||||
{
|
||||
return GetRenderSize(constraint, FontSize);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compute the rendered geometry.
|
||||
/// </summary>
|
||||
/// <param name="finalSize"></param>
|
||||
/// <returns></returns>
|
||||
protected override Size ArrangeOverride(Size finalSize)
|
||||
{
|
||||
return GetRenderSize(finalSize, FontSize);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Render callback.
|
||||
/// </summary>
|
||||
protected override void OnRender(DrawingContext dc)
|
||||
{
|
||||
Geometry rendered;
|
||||
var geometry = DefiningGeometry;
|
||||
|
||||
Debug.Assert(geometry != null);
|
||||
|
||||
var foreground = Foreground;
|
||||
var matrix = GetStretchMatrix(geometry, FontSize);
|
||||
|
||||
// Need to use colorful render.
|
||||
if (geometry is GeometryGroup)
|
||||
{
|
||||
Brush brush;
|
||||
int index = 0;
|
||||
var isSolidColor = foreground is SolidColorBrush;
|
||||
var children = ((GeometryGroup)geometry).Children;
|
||||
|
||||
foreach (var child in children)
|
||||
{
|
||||
rendered = GetRenderedGeometry(child, matrix);
|
||||
|
||||
if (rendered != Geometry.Empty)
|
||||
{
|
||||
brush = rendered.GetValue(FillProperty) as Brush;
|
||||
|
||||
// It may need to be tinted
|
||||
if (brush == null)
|
||||
{
|
||||
if (!isSolidColor || index == 0 || index == 6 || index > 9)
|
||||
{
|
||||
brush = foreground;
|
||||
}else
|
||||
{
|
||||
brush = new SolidColorBrush(ColorPalette.Toning(((SolidColorBrush)foreground).Color, index));
|
||||
}
|
||||
|
||||
index++;
|
||||
}
|
||||
|
||||
dc.DrawGeometry(brush, null, rendered);
|
||||
}
|
||||
}
|
||||
|
||||
} else
|
||||
{
|
||||
rendered = GetRenderedGeometry(geometry, matrix);
|
||||
|
||||
if (rendered != Geometry.Empty)
|
||||
{
|
||||
dc.DrawGeometry(foreground, null, rendered);
|
||||
}
|
||||
}
|
||||
|
||||
// Without background, the mouse can penetrate geometry and cause event failure.
|
||||
var background = Background;
|
||||
|
||||
if (background != null)
|
||||
{
|
||||
dc.DrawRectangle(background, null, new Rect(0, 0, RenderSize.Width, RenderSize.Height));
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Methods
|
||||
|
||||
private void SetSpinAnimation()
|
||||
{
|
||||
var spin = Spin ?? Type == "loading";
|
||||
|
||||
if (spin)
|
||||
{
|
||||
this.BeginSpin(1d);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.StopSpin();
|
||||
}
|
||||
}
|
||||
|
||||
private Size GetRenderSize(Size availableSize, double fontSize)
|
||||
{
|
||||
if (IsGeometryEmpty(DefiningGeometry))
|
||||
{
|
||||
return new Size(0d, 0d);
|
||||
}
|
||||
|
||||
return new Size(Math.Min(availableSize.Width, fontSize), Math.Min(availableSize.Height, fontSize));
|
||||
}
|
||||
|
||||
private bool IsGeometryEmpty(Geometry geometry)
|
||||
{
|
||||
return geometry.IsEmpty() || geometry.Bounds.IsEmpty;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the rendered geometry.
|
||||
/// </summary>
|
||||
private Geometry GetRenderedGeometry(Geometry geometry, Matrix matrix)
|
||||
{
|
||||
var rendered = geometry.CloneCurrentValue();
|
||||
|
||||
if (ReferenceEquals(geometry, rendered))
|
||||
{
|
||||
rendered = rendered.Clone();
|
||||
}
|
||||
|
||||
var transform = rendered.Transform;
|
||||
|
||||
if (transform == null)
|
||||
{
|
||||
rendered.Transform = new MatrixTransform(matrix);
|
||||
}
|
||||
else
|
||||
{
|
||||
rendered.Transform = new MatrixTransform(transform.Value * matrix);
|
||||
}
|
||||
|
||||
return rendered;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the stretch matrix of the geometry.
|
||||
/// </summary>
|
||||
private Matrix GetStretchMatrix(Geometry geometry, double size)
|
||||
{
|
||||
var matrix = Matrix.Identity;
|
||||
|
||||
if (!IsGeometryEmpty(geometry))
|
||||
{
|
||||
double scaleX, scaleY;
|
||||
var viewBox = (Rect)geometry.GetValue(ViewBoxProperty);
|
||||
|
||||
if (viewBox.IsEmpty)
|
||||
{
|
||||
viewBox = geometry.Bounds;
|
||||
scaleX = size / viewBox.Right;
|
||||
scaleY = size / viewBox.Bottom;
|
||||
|
||||
if (scaleX > scaleY)
|
||||
{
|
||||
scaleX = scaleY;
|
||||
} else
|
||||
{
|
||||
scaleY = scaleX;
|
||||
}
|
||||
|
||||
} else
|
||||
{
|
||||
scaleX = size / viewBox.Width;
|
||||
scaleY = size / viewBox.Height;
|
||||
matrix.Translate(-(scaleX * viewBox.X), -(scaleY * viewBox.Y));
|
||||
}
|
||||
|
||||
matrix.Scale(scaleX, scaleY);
|
||||
}
|
||||
|
||||
return matrix;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
public enum IconTheme : byte
|
||||
{
|
||||
Filled, Outlined, Colorful
|
||||
}
|
||||
Reference in New Issue
Block a user