mirror of
https://gitee.com/chinware/atomui.git
synced 2024-11-29 18:38:16 +08:00
* 调整AtomLayer,对Adorner提供Bounds;
This commit is contained in:
parent
1d70e4535b
commit
0e379e0d3c
22
docs/Code Style.md
Normal file
22
docs/Code Style.md
Normal file
@ -0,0 +1,22 @@
|
||||
# Code Styles
|
||||
|
||||
## C# Naming Style
|
||||
- Type, NameSpace, Method, Property, Event: **UpperCamelCase**
|
||||
- Interface: **IUpperCamelCase**
|
||||
- Type parameter: **TUpperCamelCase**
|
||||
- Local variable, Parameter: **lowerCamelCase**
|
||||
- Public Field (基本不用): **UpperCamelCase**
|
||||
- Private Field: **_lowerCamelCase**
|
||||
|
||||
- Control Template Part Name: **PART_UpperCamelCase**
|
||||
- Control Pseudo Class Name: **STATE_UpperCamelCase**
|
||||
|
||||
## 其他
|
||||
- 使用Rider配置,尽量和Rider的建议保持一致即可。
|
||||
- 不要在一行中声明多个变量;
|
||||
|
||||
## Xaml
|
||||
- 使用 XamlStyler插件,添加配置。
|
||||
- 不超过两个属性的共用一行。
|
||||
- Name:UpperCamelCase。
|
||||
- 结构复杂增加注释。
|
@ -1,76 +1,60 @@
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Controls.Presenters;
|
||||
using Avalonia.Layout;
|
||||
using Avalonia.VisualTree;
|
||||
using Avalonia.Threading;
|
||||
|
||||
// ReSharper disable SuggestBaseTypeForParameter
|
||||
|
||||
namespace AtomUI.Controls.Primitives
|
||||
{
|
||||
public class AtomLayer : Panel
|
||||
public class AtomLayer : Canvas
|
||||
{
|
||||
public static Control? GetTarget(Visual element)
|
||||
{
|
||||
return element.GetValue(TargetProperty);
|
||||
}
|
||||
public static void SetTarget(Visual element, Control? value)
|
||||
{
|
||||
element.SetValue(TargetProperty, value);
|
||||
}
|
||||
public static readonly AttachedProperty<Control?> TargetProperty = AvaloniaProperty
|
||||
.RegisterAttached<AtomLayer, Visual, Control?>("Target");
|
||||
#region Static
|
||||
|
||||
public static AtomLayer? GetLayer(Visual target)
|
||||
{
|
||||
return target.GetLayer();
|
||||
}
|
||||
|
||||
public static Visual GetTarget(Control adorner)
|
||||
{
|
||||
return adorner.GetValue(TargetProperty);
|
||||
}
|
||||
private static void SetTarget(Control adorner, Visual value)
|
||||
{
|
||||
adorner.SetValue(TargetProperty, value);
|
||||
}
|
||||
public static readonly AttachedProperty<Visual> TargetProperty = AvaloniaProperty
|
||||
.RegisterAttached<AtomLayer, Control, Visual>("Target");
|
||||
|
||||
public static Rect GetTargetRect(Control adorner)
|
||||
{
|
||||
return adorner.GetValue(TargetRectProperty);
|
||||
}
|
||||
private static void SetTargetRect(Control adorner, Rect value)
|
||||
{
|
||||
adorner.SetValue(TargetRectProperty, value);
|
||||
}
|
||||
public static readonly AttachedProperty<Rect> TargetRectProperty = AvaloniaProperty
|
||||
.RegisterAttached<AtomLayer, Control, Rect>("TargetRect");
|
||||
|
||||
public static AtomLayer? GetLayer(Visual visual)
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
private readonly IList<WeakReference<Control>> _detachedAdorners = new List<WeakReference<Control>>();
|
||||
|
||||
internal Visual? ParentHost { get; set; }
|
||||
|
||||
internal AtomLayer() { }
|
||||
|
||||
public T? GetAdorner<T>(Visual target) where T : Control
|
||||
{
|
||||
var host = visual.FindAncestorOfType<ScrollContentPresenter>(true)?.Content as Control
|
||||
?? TopLevel.GetTopLevel(visual);
|
||||
|
||||
if (host == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var layer = host.GetVisualChildren().FirstOrDefault(c => c is AtomLayer) as AtomLayer;
|
||||
layer ??= TryInject(host);
|
||||
return layer;
|
||||
return this.Children.OfType<T>().FirstOrDefault(a => GetTarget(a) == target);
|
||||
}
|
||||
|
||||
private static AtomLayer TryInject(Control control)
|
||||
public IEnumerable<Control> GetAdorners(Visual target)
|
||||
{
|
||||
var layer = new AtomLayer();
|
||||
|
||||
InjectCore(control, layer);
|
||||
|
||||
return layer;
|
||||
}
|
||||
|
||||
private static void InjectCore(Control control, AtomLayer layer)
|
||||
{
|
||||
if (control.GetVisualChildren() is not IList<Visual> visualChildren)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (visualChildren.Any(c => c is AtomLayer))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
layer.HorizontalAlignment = HorizontalAlignment.Stretch;
|
||||
layer.VerticalAlignment = VerticalAlignment.Stretch;
|
||||
layer.InheritanceParent = control;
|
||||
|
||||
visualChildren.Add(layer);
|
||||
control.InvalidateMeasure();
|
||||
}
|
||||
|
||||
|
||||
|
||||
private AtomLayer()
|
||||
{
|
||||
|
||||
return this.Children.Where(v => GetTarget(v) == target).ToList();
|
||||
}
|
||||
|
||||
public void AddAdorner(Visual target, Control adorner)
|
||||
@ -80,58 +64,117 @@ namespace AtomUI.Controls.Primitives
|
||||
return;
|
||||
}
|
||||
|
||||
SetTarget(target, adorner);
|
||||
Children.Add(adorner);
|
||||
target.PropertyChanged -= TargetOnPropertyChanged;
|
||||
target.PropertyChanged += TargetOnPropertyChanged;
|
||||
|
||||
target.AttachedToVisualTree -= OnTargetOnAttachedToVisualTree;
|
||||
target.DetachedFromVisualTree -= OnTargetOnDetachedFromVisualTree;
|
||||
target.AttachedToVisualTree += OnTargetOnAttachedToVisualTree;
|
||||
target.DetachedFromVisualTree += OnTargetOnDetachedFromVisualTree;
|
||||
|
||||
if (target is Control c)
|
||||
{
|
||||
c.SizeChanged -= OnTargetOnSizeChanged;
|
||||
c.SizeChanged += OnTargetOnSizeChanged;
|
||||
}
|
||||
|
||||
SetTarget(adorner, target);
|
||||
Children.Add(adorner);
|
||||
Locate(target, adorner);
|
||||
}
|
||||
|
||||
private void OnTargetOnSizeChanged(object? sender, SizeChangedEventArgs e)
|
||||
private void TargetOnPropertyChanged(object? sender, AvaloniaPropertyChangedEventArgs e)
|
||||
{
|
||||
if (sender is not Visual visual)
|
||||
if (e.Property != BoundsProperty)
|
||||
{
|
||||
return;
|
||||
}
|
||||
foreach (var adorner in GetAdorners(visual))
|
||||
|
||||
if (sender is not Visual target)
|
||||
{
|
||||
adorner.InvalidateMeasure();
|
||||
adorner.InvalidateVisual();
|
||||
return;
|
||||
}
|
||||
|
||||
// Child element's bounds will be updated first.
|
||||
Dispatcher.UIThread.Post(() =>
|
||||
{
|
||||
foreach (var adorner in GetAdorners(target))
|
||||
{
|
||||
Locate(target, adorner);
|
||||
}
|
||||
}, DispatcherPriority.Send);
|
||||
}
|
||||
|
||||
private void Locate(Visual target, Control adorner)
|
||||
{
|
||||
if (this.ParentHost is Control { IsLoaded: false })
|
||||
{
|
||||
this.ParentHost.PropertyChanged -= ParentHostOnPropertyChanged;
|
||||
this.ParentHost.PropertyChanged += ParentHostOnPropertyChanged;
|
||||
}
|
||||
else
|
||||
{
|
||||
LocateCore(target, adorner);
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
void ParentHostOnPropertyChanged(object? sender, AvaloniaPropertyChangedEventArgs e)
|
||||
{
|
||||
this.ParentHost.PropertyChanged -= ParentHostOnPropertyChanged;
|
||||
LocateCore(target, adorner);
|
||||
}
|
||||
}
|
||||
|
||||
private void LocateCore(Visual target, Control adorner)
|
||||
{
|
||||
var matrix = target.TransformToVisual(this)!;
|
||||
var x = matrix.Value.M31;
|
||||
var y = matrix.Value.M32;
|
||||
var rect = new Rect(x, y, target.Bounds.Width, target.Bounds.Height);
|
||||
|
||||
SetTargetRect(adorner, rect);
|
||||
SetLeft(adorner, x);
|
||||
SetTop(adorner, y);
|
||||
adorner.Width = target.Bounds.Width;
|
||||
adorner.Height = target.Bounds.Height;
|
||||
|
||||
adorner.Measure(target.Bounds.Size);
|
||||
adorner.Arrange(rect);
|
||||
}
|
||||
|
||||
private void OnTargetOnAttachedToVisualTree(object? sender, VisualTreeAttachmentEventArgs args)
|
||||
{
|
||||
if (Children.Contains(this))
|
||||
if (sender is not Visual target)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Children.Add(this);
|
||||
var adorners = new List<Control>();
|
||||
for (var i = 0; i < _detachedAdorners.Count; i++)
|
||||
{
|
||||
var w = _detachedAdorners[i];
|
||||
if (w.TryGetTarget(out var t) && Children.Contains(t) == false && GetTarget(t) == target)
|
||||
{
|
||||
adorners.Add(t);
|
||||
_detachedAdorners.RemoveAt(i--);
|
||||
}
|
||||
}
|
||||
|
||||
adorners = adorners.Where(a => Children.Contains(a) == false && GetTarget(a) == target).ToList();
|
||||
foreach (var adorner in adorners)
|
||||
{
|
||||
Children.Add(adorner);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnTargetOnDetachedFromVisualTree(object? sender, VisualTreeAttachmentEventArgs args)
|
||||
{
|
||||
Children.Remove(this);
|
||||
}
|
||||
|
||||
public Control? GetAdorner<T>(Visual target) where T : Control, IAtomAdorner
|
||||
{
|
||||
var adorner = this.Children.OfType<T>().FirstOrDefault(a => a.Target == target);
|
||||
return adorner;
|
||||
}
|
||||
|
||||
public IEnumerable<Control> GetAdorners(Visual target)
|
||||
{
|
||||
return this.Children.OfType<IAtomAdorner>().Where(c => c.Target == target).OfType<Control>();
|
||||
if (sender is not Visual target)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var adorners = target.GetAdorners();
|
||||
foreach (var adorner in adorners)
|
||||
{
|
||||
Children.Remove(adorner);
|
||||
_detachedAdorners.Add(new WeakReference<Control>(adorner));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,8 @@
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Controls.Presenters;
|
||||
using Avalonia.Layout;
|
||||
using Avalonia.VisualTree;
|
||||
|
||||
namespace AtomUI.Controls.Primitives;
|
||||
|
||||
@ -6,6 +10,67 @@ public static class AtomLayerExtension
|
||||
{
|
||||
public static AtomLayer? GetLayer(this Visual? visual)
|
||||
{
|
||||
return visual == null ? null : AtomLayer.GetLayer(visual);
|
||||
if (visual == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var host = visual.FindAncestorOfType<ScrollContentPresenter>(true)?.Content as Visual
|
||||
?? TopLevel.GetTopLevel(visual);
|
||||
|
||||
if (host == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var layer = host.GetVisualChildren().FirstOrDefault(c => c is AtomLayer) as AtomLayer
|
||||
?? TryInject(host);
|
||||
|
||||
return layer;
|
||||
}
|
||||
|
||||
public static T? GetAdorner<T>(this Visual target) where T : Control
|
||||
{
|
||||
return target.GetLayer()?.GetAdorner<T>(target);
|
||||
}
|
||||
|
||||
public static IEnumerable<Control> GetAdorners(this Visual target)
|
||||
{
|
||||
return target.GetLayer()?.GetAdorners(target) ?? [];
|
||||
}
|
||||
|
||||
public static void AddAdorner(this Visual target, Control adorner)
|
||||
{
|
||||
target.GetLayer()?.AddAdorner(target, adorner);
|
||||
}
|
||||
|
||||
|
||||
|
||||
private static AtomLayer? TryInject(Visual host)
|
||||
{
|
||||
var layer = new AtomLayer
|
||||
{
|
||||
HorizontalAlignment = HorizontalAlignment.Stretch,
|
||||
VerticalAlignment = VerticalAlignment.Stretch
|
||||
};
|
||||
|
||||
return InjectCore(host, layer) ? layer : null;
|
||||
}
|
||||
|
||||
private static bool InjectCore(Visual host, AtomLayer layer)
|
||||
{
|
||||
if (host.GetVisualChildren() is not IList<Visual> visualChildren)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (visualChildren.Any(c => c is AtomLayer))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
visualChildren.Add(layer);
|
||||
layer.ParentHost = host;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
@ -1,8 +0,0 @@
|
||||
using Avalonia;
|
||||
|
||||
namespace AtomUI.Controls.Primitives;
|
||||
|
||||
public interface IAtomAdorner
|
||||
{
|
||||
public Visual Target { get; }
|
||||
}
|
@ -9,7 +9,7 @@ using Avalonia.VisualTree;
|
||||
|
||||
namespace AtomUI.Controls;
|
||||
|
||||
public sealed class Watermark : Control, IAtomAdorner
|
||||
public sealed class Watermark : Control
|
||||
{
|
||||
public static WatermarkGlyph? GetGlyph(Visual element)
|
||||
{
|
||||
@ -27,8 +27,6 @@ public sealed class Watermark : Control, IAtomAdorner
|
||||
public Visual Target { get; }
|
||||
|
||||
private WatermarkGlyph? Glyph { get; }
|
||||
|
||||
private AtomLayer Layer { get; }
|
||||
|
||||
|
||||
|
||||
@ -38,11 +36,10 @@ public sealed class Watermark : Control, IAtomAdorner
|
||||
GlyphProperty.Changed.AddClassHandler<Visual>(OnGlyphChanged);
|
||||
}
|
||||
|
||||
private Watermark(AtomLayer layer, Visual target, WatermarkGlyph? glyph)
|
||||
private Watermark(Visual target, WatermarkGlyph? glyph)
|
||||
{
|
||||
Target = target;
|
||||
Glyph = glyph;
|
||||
Layer = layer;
|
||||
|
||||
if (glyph != null)
|
||||
{
|
||||
@ -90,7 +87,7 @@ public sealed class Watermark : Control, IAtomAdorner
|
||||
return;
|
||||
}
|
||||
|
||||
watermark = new Watermark(layer, target, GetGlyph(target));
|
||||
watermark = new Watermark(target, GetGlyph(target));
|
||||
layer.AddAdorner(target ,watermark);
|
||||
}
|
||||
|
||||
@ -114,24 +111,12 @@ public sealed class Watermark : Control, IAtomAdorner
|
||||
return;
|
||||
}
|
||||
|
||||
if (CheckLayer(Target, out var layer) == false)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var matrix = Target.TransformToVisual(layer);
|
||||
if (matrix == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var size = Glyph.GetDesiredSize();
|
||||
if (size.Width == 0 || size.Height == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
using (context.PushTransform(matrix.Value))
|
||||
using (context.PushClip(new Rect(Target.Bounds.Size)))
|
||||
using (context.PushOpacity(Glyph.Opacity))
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user