CPF/CPF/Controls/Control.cs
2023-11-21 23:05:03 +08:00

614 lines
27 KiB
C#
Raw Permalink 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.Text;
using CPF;
using System.Reflection;
using System.Linq;
using CPF.Drawing;
using CPF.Reflection;
using System.ComponentModel;
namespace CPF.Controls
{
/// <summary>
/// 支持可视化设计的控件基类
/// </summary>
[Description("支持可视化设计的控件基类")]
public class Control : UIElement
{
/// <summary>
/// 背景填充
/// </summary>
[UIPropertyMetadata(null, UIPropertyOptions.AffectsRender), Description("背景填充")]
public ViewFill Background
{
get { return (ViewFill)GetValue(45); }
set { SetValue(value, 45); }
}
/// <summary>
/// 边框线条填充
/// </summary>
[Description("边框线条填充")]
[UIPropertyMetadata(null, UIPropertyOptions.AffectsRender)]
public ViewFill BorderFill
{
get { return (ViewFill)GetValue(46); }
set { SetValue(value, 46); }
}
/// <summary>
/// 获取或设置线条类型
/// </summary>
[Description("获取或设置线条类型")]
[UIPropertyMetadata(typeof(Stroke), "0", UIPropertyOptions.AffectsRender)]
public Stroke BorderStroke
{
get { return (Stroke)GetValue(47); }
set { SetValue(value, 47); }
}
/// <summary>
/// 获取或设置描述 Thickness 及其子元素之间的空间量的 Border 值。格式all或者left,top,right,bottom
/// </summary>
[Description("获取或设置描述 Thickness 及其子元素之间的空间量的 Border 值。格式all或者left,top,right,bottom")]
[UIPropertyMetadata(typeof(Thickness), "0", UIPropertyOptions.AffectsMeasure)]
public Thickness Padding
{
get { return GetValue<Thickness>(56); }
set { SetValue(value, 56); }
}
/// <summary>
/// 四周边框粗细
/// </summary>
[Description("四周边框粗细")]
[UIPropertyMetadata(typeof(Thickness), "0", UIPropertyOptions.AffectsMeasure | UIPropertyOptions.AffectsRender)]
public Thickness BorderThickness
{
get { return GetValue<Thickness>(48); }
set { SetValue(value, 48); }
}
/// <summary>
/// 获取或设置一个值,该值表示将 Border 的角倒圆的程度。格式 一个数字或者四个数字 比如10或者 10,10,10,10 topLeft,topRight,bottomRight,bottomLeft
/// </summary>
[Description("获取或设置一个值,该值表示将 Border 的角倒圆的程度。格式 一个数字或者四个数字 比如10或者 10,10,10,10 topLeft,topRight,bottomRight,bottomLeft")]
[UIPropertyMetadata(typeof(CornerRadius), "0", UIPropertyOptions.AffectsRender)]
public CornerRadius CornerRadius
{
get { return (CornerRadius)GetValue(50); }
set { SetValue(value, 50); }
}
/// <summary>
/// 边框类型BorderStroke和BorderThickness
/// </summary>
[UIPropertyMetadata(BorderType.BorderStroke, UIPropertyOptions.AffectsMeasure | UIPropertyOptions.AffectsRender), Description("边框类型BorderStroke和BorderThickness")]
public BorderType BorderType
{
get { return GetValue<BorderType>(49); }
set { SetValue(value, 49); }
}
/// <summary>
/// 字体名
/// </summary>
[UIPropertyMetadata("宋体", UIPropertyOptions.AffectsRender | UIPropertyOptions.Inherits), Description("字体名")]
public string FontFamily
{
get { return (string)GetValue(51); }
set { SetValue(value, 51); }
}
/// <summary>
/// 字体尺寸,点
/// </summary>
[UIPropertyMetadata(12f, UIPropertyOptions.AffectsRender | UIPropertyOptions.AffectsMeasure | UIPropertyOptions.Inherits), Description("字体尺寸,点")]
public float FontSize
{
get { return (float)GetValue(52); }
set { SetValue(value, 52); }
}
/// <summary>
/// 字体样式
/// </summary>
[UIPropertyMetadata(FontStyles.Regular, UIPropertyOptions.AffectsRender | UIPropertyOptions.AffectsMeasure | UIPropertyOptions.Inherits), Description("字体样式")]
public FontStyles FontStyle
{
get { return (FontStyles)GetValue(53); }
set { SetValue(value, 53); }
}
/// <summary>
/// 表示一个文本修饰,它是可添加到文本的视觉装饰(如下划线)。字符串格式: overline/Underline/Strikethrough [width[,Solid/Dash/Dot/DashDot/DashDotDot]] [color]
/// </summary>
[UIPropertyMetadata(typeof(TextDecoration), "", UIPropertyOptions.AffectsRender | UIPropertyOptions.Inherits)]
[Description("表示一个文本修饰,它是可添加到文本的视觉装饰(如下划线)。字符串格式: overline/Underline/Strikethrough/none [width[,Solid/Dash/Dot/DashDot/DashDotDot]] [color]")]
public TextDecoration TextDecoration
{
get { return (TextDecoration)GetValue(57); }
set { SetValue(value, 57); }
}
/// <summary>
/// 控件文字的填充
/// </summary>
[UIPropertyMetadata(typeof(ViewFill), "Black", UIPropertyOptions.AffectsRender | UIPropertyOptions.Inherits), Description("控件文字的填充")]
public ViewFill Foreground
{
get { return (ViewFill)GetValue(54); }
set { SetValue(value, 54); }
}
/// <summary>
/// 初始化模板重写时一般不需要调用base.InitializeComponent()
/// </summary>
protected virtual void InitializeComponent()
{
}
/// <summary>
/// 是否初始化组件完成
/// </summary>
[Browsable(false)]
public bool IsInitialized
{
get { return GetValue<bool>(55); }
internal set { SetValue(value, 55); }
}
public Control()
{
loadStyle = false;
}
/// <summary>
/// 模板用来外部替换InitializeComponent操作第一个参数就是当前对象第二个就是当前对象的Children由于Children大部分情况下是Protect所以这里通过参数提供。初始化组件之前设置才有意义
/// </summary>
[NotCpfProperty]
[Browsable(false)]
public Action<UIElement, UIElementCollection> Template { get; set; }
[PropertyChanged(nameof(Visibility))]
void OnVisibility(object newValue, object oldValue, PropertyMetadataAttribute attribute)
{
if (!IsInitialized && Root != null)
{
if (NeedInitialize(this))
{
//Initialize();
Root.OnElementInitialize(this);
}
}
else if ((Visibility)newValue == Visibility.Visible && Root != null && Children.Count > 0 && NeedInitialize(this))
{
foreach (Control item in Children.Where(a => a is Control))
{
item.CheckIni();
}
}
}
void CheckIni()
{
if (Visibility == Visibility.Visible)
{
if (!IsInitialized)
{
Root.OnElementInitialize(this);
}
else
{
foreach (Control item in Children.Where(a => a is Control))
{
item.CheckIni();
}
}
}
}
//protected override void OnPropertyChanged(string propertyName, object oldValue, object newValue, PropertyMetadataAttribute propertyMetadata)
//{
// if (propertyName == nameof(Visibility))
// {
// if (!IsInitialized && Root != null)
// {
// if (NeedInitialize(this))
// {
// Initialize();
// }
// }
// }
// base.OnPropertyChanged(propertyName, oldValue, newValue, propertyMetadata);
//}
protected override void OnAttachedToVisualTree()
{
base.OnAttachedToVisualTree();
if (NeedInitialize(this))
{
//Initialize();
Root.OnElementInitialize(this);
}
}
bool NeedInitialize(UIElement element)
{
if (element.Visibility == Visibility.Collapsed && !(element is Popup))
{
return false;
}
if (element.Parent is Control control)
{
return NeedInitialize(control);
}
return true;
}
void ini(Control control)
{
foreach (Control item in control.Children.Where(a => a is Control))
{
if (!item.IsInitialized && item.Visibility != Visibility.Collapsed)
{
//item.Initialize();
Root.OnElementInitialize(item);
ini(item);
}
}
}
/// <summary>
/// 调用内部InitializeComponent初始化组件
/// </summary>
public void Initialize()
{
if (!IsInitialized)
{
if (Children.Count > 0)
{
//foreach (Control item in Find<Control>())
//{
// if (item.Visibility != Visibility.Collapsed)
// {
// item.Initialize();
// }
//}
ini(this);
}
if (Template == null)
{
InitializeComponent();
}
else
{
Template(this, this.Children);
}
IsInitialized = true;
OnInitialized();
}
LoadStyle();
}
protected virtual void OnInitialized()
{
if (triggers != null)
{
triggers.Sort((a, b) =>
{
if (a.Style == b.Style)
{
return 0;
}
if (a.Style != null && b.Style != null)
{
return a.Style.Index - b.Style.Index;
}
if (a.Style != null)
{
return 1;
}
else
{
return -1;
}
});
}
this.RaiseEvent(EventArgs.Empty, nameof(Initialized));
}
public event EventHandler Initialized
{
add { AddHandler(value); }
remove { RemoveHandler(value); }
}
protected override Size MeasureOverride(in Size availableSize)
{
var ava = availableSize;
var p = Padding;
Size size;
if (BorderType == BorderType.BorderStroke)
{
var b = BorderStroke;
ava.Width = Math.Max(0, ava.Width - p.Left - p.Right - b.Width - b.Width);
ava.Height = Math.Max(0, ava.Height - p.Top - p.Bottom - b.Width - b.Width);
size = base.MeasureOverride(ava);
size.Width = p.Left + p.Right + size.Width + b.Width + b.Width;
size.Height = p.Top + p.Bottom + size.Height + b.Width + b.Width;
}
else
{
var b = BorderThickness;
ava.Width = Math.Max(0, ava.Width - p.Left - p.Right - b.Left - b.Right);
ava.Height = Math.Max(0, ava.Height - p.Top - p.Bottom - b.Top - b.Bottom);
size = base.MeasureOverride(ava);
size.Width = p.Left + p.Right + size.Width + b.Left + b.Right;
size.Height = p.Top + p.Bottom + size.Height + b.Top + b.Bottom;
}
return size;
}
protected override Size ArrangeOverride(in Size finalSize)
{
var rect = new Rect(0, 0, finalSize.Width, finalSize.Height);
var p = Padding;
if (BorderType == BorderType.BorderStroke)
{
var b = BorderStroke;
rect.Location = new Point(p.Left + b.Width, p.Top + b.Width);
rect.Size = new Size(Math.Max(0, rect.Width - p.Left - p.Right - b.Width - b.Width), Math.Max(0, rect.Height - p.Top - p.Bottom - b.Width - b.Width));
}
else
{
var b = BorderThickness;
rect.Location = new Point(p.Left + b.Left, p.Top + b.Top);
rect.Size = new Size(Math.Max(0, rect.Width - p.Left - p.Right - b.Left - b.Right), Math.Max(0, rect.Height - p.Top - p.Bottom - b.Top - b.Bottom));
}
foreach (UIElement child in Children)
{
child.Arrange(rect);
}
return finalSize;
}
protected override void OnRender(DrawingContext dc)
{
var cr = CornerRadius;
var s = ActualSize;
var rect = new Rect(0, 0, Math.Max(0, s.Width), Math.Max(0, s.Height));
if (BorderType == BorderType.BorderStroke)
{
var bs = BorderStroke;
using (var path = new PathGeometry())
{
if (UseLayoutRounding)
{
bs.Width = (float)Math.Round(bs.Width * Root.RenderScaling) / Root.RenderScaling;
}
var w2 = bs.Width / 2;
path.BeginFigure(w2 + cr.TopLeft + rect.Left, w2 + rect.Top);
path.LineTo(rect.Right - w2 - cr.TopRight, w2 + rect.Top);
path.ArcTo(new Point(rect.Right - w2, w2 + cr.TopRight + rect.Top), new Size(cr.TopRight, cr.TopRight), 90, true, false);
path.LineTo(rect.Right - w2, rect.Bottom - cr.BottomRight - w2);
path.ArcTo(new Point(rect.Right - w2 - cr.BottomRight, rect.Bottom - w2), new Size(cr.BottomRight, cr.BottomRight), 90, true, false);
path.LineTo(w2 + cr.BottomLeft + rect.Left, rect.Bottom - w2);
path.ArcTo(new Point(w2 + rect.Left, rect.Bottom - w2 - cr.BottomLeft), new Size(cr.BottomLeft, cr.BottomLeft), 90, true, false);
path.LineTo(w2 + rect.Left, w2 + cr.TopLeft + rect.Top);
path.ArcTo(new Point(w2 + cr.TopLeft + rect.Left, w2 + rect.Top), new Size(cr.TopLeft, cr.TopLeft), 90, true, false);
path.EndFigure(true);
var ba = Background;
if (ba != null)
{
using (var brush = ba.CreateBrush(rect, Root.RenderScaling))
{
dc.FillPath(brush, path);
}
}
if (bs.Width > 0)
{
var bf = BorderFill;
if (bf != null)
{
using (var brush = bf.CreateBrush(rect, Root.RenderScaling))
{
dc.DrawPath(brush, bs, path);
}
}
}
}
}
else
{
var bt = BorderThickness;
if (!cr.IsZero)
{
using (var path = new PathGeometry())
{
using (var p = new PathGeometry())
{
var w2 = 0.5f;
p.BeginFigure(w2 + cr.TopLeft + bt.Left + rect.Left, w2 + bt.Top + rect.Top);
p.LineTo(rect.Right - w2 - cr.TopRight - bt.Right, w2 + bt.Top + rect.Top);
p.ArcTo(new Point(rect.Right - w2 - bt.Right, w2 + cr.TopRight + bt.Top + rect.Top), new Size(cr.TopRight, cr.TopRight), 90, true, false);
p.LineTo(rect.Right - w2 - bt.Right, rect.Bottom - cr.BottomRight - w2 - bt.Bottom);
p.ArcTo(new Point(rect.Right - w2 - cr.BottomRight - bt.Right, rect.Bottom - w2 - bt.Bottom), new Size(cr.BottomRight, cr.BottomRight), 90, true, false);
p.LineTo(w2 + cr.BottomLeft + bt.Left + rect.Left, rect.Bottom - w2 - bt.Bottom);
p.ArcTo(new Point(w2 + bt.Left + rect.Left, rect.Bottom - w2 - cr.BottomLeft - bt.Bottom), new Size(cr.BottomLeft, cr.BottomLeft), 90, true, false);
p.LineTo(w2 + bt.Left + rect.Left, w2 + cr.TopLeft + bt.Top + rect.Top);
p.ArcTo(new Point(w2 + cr.TopLeft + bt.Left + rect.Left, w2 + bt.Top + rect.Top), new Size(cr.TopLeft, cr.TopLeft), 90, true, false);
p.EndFigure(true);
//w2 = 1;
path.BeginFigure(w2 + cr.TopLeft + rect.Left, w2 + rect.Top);
path.LineTo(rect.Right - w2 - cr.TopRight, w2 + rect.Top);
path.ArcTo(new Point(rect.Right - w2, w2 + cr.TopRight + rect.Top), new Size(cr.TopRight, cr.TopRight), 90, true, false);
path.LineTo(rect.Right - w2, rect.Bottom - cr.BottomRight - w2);
path.ArcTo(new Point(rect.Right - w2 - cr.BottomRight, rect.Bottom - w2), new Size(cr.BottomRight, cr.BottomRight), 90, true, false);
path.LineTo(w2 + cr.BottomLeft + rect.Left, rect.Bottom - w2);
path.ArcTo(new Point(w2 + rect.Left, rect.Bottom - w2 - cr.BottomLeft), new Size(cr.BottomLeft, cr.BottomLeft), 90, true, false);
path.LineTo(w2 + rect.Left, w2 + cr.TopLeft + rect.Top);
path.ArcTo(new Point(w2 + cr.TopLeft + rect.Left, w2 + rect.Top), new Size(cr.TopLeft, cr.TopLeft), 90, true, false);
path.EndFigure(true);
using (var geo = new Geometry(path))
{
using (var ge = new Geometry(p))
{
geo.Xor(ge);
var bf = BorderFill;
Brush brush = null;
var str = new Stroke(1);
var ww = 0.8f;
if (bf != null)
{
brush = bf.CreateBrush(rect, Root.RenderScaling);
}
if (brush != null)
{
dc.FillGeometry(brush, geo);
}
var ba = Background;
if (ba != null)
{
using (var brush1 = ba.CreateBrush(rect, Root.RenderScaling))
{
dc.FillGeometry(brush1, ge);
dc.DrawPath(brush1, new Stroke(0.8f), p);
}
}
if (brush != null)
{
if (cr.TopLeft > 0 && (bt.Top > 0 || bt.Left > 0))
{
using (var pp = new PathGeometry())
{
pp.BeginFigure(ww + rect.Left, ww + cr.TopLeft + rect.Top);
pp.ArcTo(new Point(ww + cr.TopLeft + rect.Left, ww + rect.Top), new Size(cr.TopLeft, cr.TopLeft), 90, true, false);
pp.EndFigure(false);
dc.DrawPath(brush, str, pp);
}
}
if (cr.TopRight > 0 && (bt.Top > 0 || bt.Right > 0))
{
using (var pp = new PathGeometry())
{
pp.BeginFigure(rect.Right - ww - cr.TopRight, ww + rect.Top);
pp.ArcTo(new Point(rect.Right - ww, ww + cr.TopRight + rect.Top), new Size(cr.TopRight, cr.TopRight), 90, true, false);
pp.EndFigure(false);
dc.DrawPath(brush, str, pp);
}
}
if (cr.BottomLeft > 0 && (bt.Bottom > 0 || bt.Left > 0))
{
using (var pp = new PathGeometry())
{
pp.BeginFigure(ww + cr.BottomLeft + rect.Left, rect.Bottom - ww);
pp.ArcTo(new Point(ww + rect.Left, rect.Bottom - ww - cr.BottomLeft), new Size(cr.BottomLeft, cr.BottomLeft), 90, true, false);
pp.EndFigure(false);
dc.DrawPath(brush, str, pp);
}
}
if (cr.BottomRight > 0 && (bt.Bottom > 0 || bt.Right > 0))
{
using (var pp = new PathGeometry())
{
pp.BeginFigure(rect.Right - ww, rect.Bottom - cr.BottomRight - ww);
pp.ArcTo(new Point(rect.Right - ww - cr.BottomRight, rect.Bottom - ww), new Size(cr.BottomRight, cr.BottomRight), 90, true, false);
pp.EndFigure(false);
dc.DrawPath(brush, str, pp);
}
}
ww = 0.5f;
str = new Stroke(1);
if (bt.Left > 0)
{
dc.DrawLine(str, brush, new Point(ww + rect.Left, ww + cr.TopLeft + rect.Top), new Point(ww + rect.Left, rect.Bottom - cr.BottomLeft - ww));
}
if (bt.Top > 0)
{
dc.DrawLine(str, brush, new Point(ww + cr.TopLeft + rect.Left, ww + rect.Top), new Point(rect.Right - ww - cr.TopRight, ww + rect.Top));
}
if (bt.Right > 0)
{
dc.DrawLine(str, brush, new Point(rect.Right - ww, ww + cr.TopRight + rect.Top), new Point(rect.Right - ww, rect.Bottom - cr.BottomRight - ww));
}
if (bt.Bottom > 0)
{
dc.DrawLine(str, brush, new Point(ww + cr.BottomLeft + rect.Left, rect.Bottom - ww), new Point(rect.Right - ww - cr.BottomRight, rect.Bottom - ww));
}
brush.Dispose();
}
}
}
}
}
}
else
{
var bf = BorderFill;
if (UseLayoutRounding || !IsAntiAlias)
{
var sc = Root.RenderScaling;
bt.Left = (float)Math.Round(bt.Left * sc) / sc;
bt.Right = (float)Math.Round(bt.Right * sc) / sc;
bt.Top = (float)Math.Round(bt.Top * sc) / sc;
bt.Bottom = (float)Math.Round(bt.Bottom * sc) / sc;
}
if (bf != null)
{
using (var brush = bf.CreateBrush(rect, Root.RenderScaling))
{
if (bt.Top > 0)
{
dc.DrawLine(new Stroke(bt.Top), brush, new Point(rect.Left, bt.Top / 2 + rect.Top), new Point(rect.Right, bt.Top / 2 + rect.Top));
}
if (bt.Left > 0)
{
dc.DrawLine(new Stroke(bt.Left), brush, new Point(bt.Left / 2 + rect.Left, rect.Top), new Point(bt.Left / 2 + rect.Left, rect.Bottom));
}
if (bt.Right > 0)
{
dc.DrawLine(new Stroke(bt.Right), brush, new Point(rect.Right - bt.Right / 2, rect.Top), new Point(rect.Right - bt.Right / 2, rect.Bottom));
}
if (bt.Bottom > 0)
{
dc.DrawLine(new Stroke(bt.Bottom), brush, new Point(rect.Left, rect.Bottom - bt.Bottom / 2), new Point(rect.Right, rect.Bottom - bt.Bottom / 2));
}
}
}
var ba = Background;
if (ba != null)
{
using (var brush = ba.CreateBrush(rect, Root.RenderScaling))
{
dc.FillRectangle(brush, new Rect(bt.Left + rect.Left, bt.Top + rect.Top, Math.Max(0, rect.Width - bt.Left - bt.Right), Math.Max(0, rect.Height - bt.Top - bt.Bottom)));
}
}
}
}
}
//protected override void OnOverrideMetadata(OverrideMetadata overridePropertys)
//{
// base.OnOverrideMetadata(overridePropertys);
// overridePropertys.Override(nameof(UseLayoutRounding), new PropertyMetadataAttribute(true));
//}
public override object Clone()
{
var control = base.Clone() as Control;
if (this.IsInitialized)
{
control.Initialize();
}
return control;
}
}
}