CPF/CPF/Shapes/Shape.cs
2023-11-21 23:05:03 +08:00

232 lines
7.9 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Text;
using CPF.Drawing;
namespace CPF.Shapes
{
/// <summary>
/// 为 Ellipse、Polygon 和 Rectangle 之类的形状元素提供基类。
/// </summary>
[Description("为 Ellipse、Polygon 和 Rectangle 之类的形状元素提供基类。")]
public abstract class Shape : UIElement
{
Drawing.PathGeometry definingGeometry;
/// <summary>
/// 图形内部填充
/// </summary>
[UIPropertyMetadata(null, UIPropertyOptions.AffectsRender)]
public ViewFill Fill
{
get { return (ViewFill)GetValue(); }
set { SetValue(value); }
}
/// <summary>
/// 边框线条填充
/// </summary>
[UIPropertyMetadata(typeof(ViewFill), "black", UIPropertyOptions.AffectsRender)]
public ViewFill StrokeFill
{
get { return (ViewFill)GetValue(); }
set { SetValue(value); }
}
/// <summary>
/// 获取或设置线条类型
/// </summary>
[UIPropertyMetadata(typeof(Stroke), "1", UIPropertyOptions.AffectsRender)]
public Stroke StrokeStyle
{
get { return (Stroke)GetValue(); }
set { SetValue(value); }
}
//public Thickness Padding
//{
// get { return GetValue(PaddingProperty); }
// set { SetValue(PaddingProperty, value); }
//}
// protected Thickness Padding
// {
// get
// {
// var s = ActualSize;
// var pl = PaddingLeft;
// var pt = PaddingTop;
// var pr = PaddingRight;
// var pb = PaddingBottom;
// return new Thickness(
//pl.IsAuto ? 0 : (pl.Unit == Unit.Default ? pl.Value : pl.Value * s.Width),
//pt.IsAuto ? 0 : (pt.Unit == Unit.Default ? pt.Value : pt.Value * s.Height),
//pr.IsAuto ? 0 : (pr.Unit == Unit.Default ? pr.Value : pr.Value * s.Width),
//pb.IsAuto ? 0 : (pb.Unit == Unit.Default ? pb.Value : pb.Value * s.Height));
// }
// }
/// <summary>
/// 调用InvalidateGeometry的时候是否释放Geometry
/// </summary>
protected virtual bool DisposeGeometryOnInvalidateGeometry
{
get { return true; }
}
/// <summary>
/// 定义的图形
/// </summary>
public Drawing.PathGeometry DefiningGeometry
{
get
{
if (definingGeometry == null)
{
definingGeometry = CreateDefiningGeometry();
if (VisualClip != null)
{
VisualClip.Dispose();
}
if (IsHitTestOnPath)
{
VisualClip = new Geometry(definingGeometry.CreateStrokePath(StrokeStyle.Width));
}
else
{
VisualClip = new Geometry(definingGeometry);
}
}
return definingGeometry;
}
}
/// <summary>
/// 事件响应范围是路径的线条上还是路径围成的范围内true就是在线条上
/// </summary>
[Description("事件响应范围是路径的线条上还是路径围成的范围内true就是在线条上")]
public bool IsHitTestOnPath
{
get { return GetValue<bool>(); }
set { SetValue(value); }
}
[PropertyChanged(nameof(IsHitTestOnPath))]
void OnHitTestOnPath(object newValue, object oldValue, PropertyMetadataAttribute propertyTabAttribute)
{
InvalidateGeometry();
}
[PropertyChanged(nameof(StrokeStyle))]
void OnStrokeStyle(object newValue, object oldValue, PropertyMetadataAttribute propertyTabAttribute)
{
InvalidateGeometry();
}
protected override void OnRender(DrawingContext dc)
{
Size size = ActualSize;
if (size.Width > 0 && size.Height > 0)
{
Drawing.PathGeometry geometry = DefiningGeometry;
if (geometry != null)
{
//var p = Padding;
var m = dc.Transform;
//m.Translate(p.Left, p.Top);
dc.Transform = m;
var f = Fill;
if (f != null)
{
using (var fill = f.CreateBrush(new Rect(0, 0, size.Width, size.Height), Root.RenderScaling))
{
dc.FillPath(fill, geometry);
}
}
var stroke = StrokeFill;
if (stroke != null)
{
using (var s = stroke.CreateBrush(new Rect(0, 0, size.Width, size.Height), Root.RenderScaling))
{
var ss = StrokeStyle;
if (UseLayoutRounding)
{
var sc = Root.RenderScaling;
ss.Width = (float)Math.Round(ss.Width * sc) / sc;
}
if (ss.Width > 0)
{
dc.DrawPath(s, ss, geometry);
}
}
}
}
}
base.OnRender(dc);
}
protected abstract Drawing.PathGeometry CreateDefiningGeometry();
protected void InvalidateGeometry()
{
if (definingGeometry != null && DisposeGeometryOnInvalidateGeometry)
{
definingGeometry.Dispose();
}
definingGeometry = null;
InvalidateMeasure();
}
Size oldSize;
protected override void OnLayoutUpdated()
{
if (oldSize != ActualSize)
{
oldSize = ActualSize;
InvalidateGeometry();
}
base.OnLayoutUpdated();
}
protected override Size MeasureOverride(in Size availableSize)
{
//var wv = Width;
//var hv = Height;
//var w = wv.Unit == Unit.Default ? wv.Value : wv.Value * availableSize.Width;
//var h = hv.Unit == Unit.Default ? hv.Value : hv.Value * availableSize.Height;
//if (float.IsNaN(w) || float.IsNaN(h))
//{
// //var p = Padding;
// var rect = DefiningGeometry.GetBounds();
// var stroke = StrokeStyle;
// if (wv.IsAuto)
// {
// w = rect.Width + rect.Left + stroke.Width;
// }
// if (hv.IsAuto)
// {
// h = rect.Height + rect.Top + stroke.Width;
// }
//}
//if (!float.IsNaN(w) || !float.IsNaN(h))
//{
// var size = base.MeasureOverride(availableSize);
// return new Size(Math.Max(size.Width, w), Math.Max(size.Height, h));
//}
//else
//{
// return new Size(w, h);
//}
var rect = DefiningGeometry.GetBounds();
var stroke = StrokeStyle;
var size = new Size(Math.Max(1, rect.Width + stroke.Width + rect.Left), Math.Max(1, rect.Height + stroke.Width + rect.Top));
return size;
}
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
if (definingGeometry != null)
{
definingGeometry.Dispose();
definingGeometry = null;
}
}
}
}