优化更新代码,添加界面功能并整合
This commit is contained in:
BIN
WPFluent/Controls/Arc/Arc.bmp
Normal file
BIN
WPFluent/Controls/Arc/Arc.bmp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 824 B |
214
WPFluent/Controls/Arc/Arc.cs
Normal file
214
WPFluent/Controls/Arc/Arc.cs
Normal file
@@ -0,0 +1,214 @@
|
||||
|
||||
|
||||
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Shapes;
|
||||
|
||||
using Point = System.Windows.Point;
|
||||
using Size = System.Windows.Size;
|
||||
|
||||
// ReSharper disable CheckNamespace
|
||||
namespace WPFluent.Controls;
|
||||
|
||||
/// <summary>
|
||||
/// Control that draws a symmetrical arc with rounded edges.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// <code lang="xml"> /// <ui:Arc /// EndAngle="359" /// StartAngle="0" /// Stroke="{ui:ThemeResource
|
||||
/// SystemAccentColorSecondaryBrush}" /// StrokeThickness="2" /// Visibility="Visible" /> ///</code>
|
||||
/// </example>
|
||||
public class Arc : Shape
|
||||
{
|
||||
/// <summary>
|
||||
/// Identifies the <see cref="EndAngle"/> dependency property.
|
||||
/// </summary>
|
||||
public static readonly DependencyProperty EndAngleProperty = DependencyProperty.Register(
|
||||
nameof(EndAngle),
|
||||
typeof(double),
|
||||
typeof(Arc),
|
||||
new PropertyMetadata(0.0d, PropertyChangedCallback));
|
||||
|
||||
/// <summary>
|
||||
/// Identifies the <see cref="StartAngle"/> dependency property.
|
||||
/// </summary>
|
||||
public static readonly DependencyProperty StartAngleProperty = DependencyProperty.Register(
|
||||
nameof(StartAngle),
|
||||
typeof(double),
|
||||
typeof(Arc),
|
||||
new PropertyMetadata(0.0d, PropertyChangedCallback));
|
||||
|
||||
/// <summary>
|
||||
/// Identifies the <see cref="SweepDirection"/> dependency property.
|
||||
/// </summary>
|
||||
public static readonly DependencyProperty SweepDirectionProperty = DependencyProperty.Register(
|
||||
nameof(SweepDirection),
|
||||
typeof(SweepDirection),
|
||||
typeof(Arc),
|
||||
new PropertyMetadata(SweepDirection.Clockwise, PropertyChangedCallback));
|
||||
private Viewbox _rootLayout;
|
||||
|
||||
static Arc()
|
||||
{
|
||||
// Modify the metadata of the StrokeStartLineCap dependency property.
|
||||
StrokeStartLineCapProperty.OverrideMetadata(
|
||||
typeof(Arc),
|
||||
new FrameworkPropertyMetadata(PenLineCap.Round, PropertyChangedCallback));
|
||||
}
|
||||
|
||||
private void EnsureRootLayout()
|
||||
{
|
||||
if(_rootLayout != null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_rootLayout = new Viewbox { SnapsToDevicePixels = true };
|
||||
AddVisualChild(_rootLayout);
|
||||
}
|
||||
|
||||
protected override Size ArrangeOverride(Size finalSize)
|
||||
{
|
||||
EnsureRootLayout();
|
||||
|
||||
_rootLayout!.Arrange(new Rect(default, finalSize));
|
||||
return finalSize;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the geometry that defines this shape. <para><see href="https://stackoverflow.com/a/36756365/13224348">Based
|
||||
/// on Mark Feldman implementation.</see></para>
|
||||
/// </summary>
|
||||
protected Geometry DefinedGeometry()
|
||||
{
|
||||
var geometryStream = new StreamGeometry();
|
||||
var arcSize = new Size(
|
||||
Math.Max(0, (RenderSize.Width - StrokeThickness) / 2),
|
||||
Math.Max(0, (RenderSize.Height - StrokeThickness) / 2));
|
||||
|
||||
using var context = geometryStream.Open();
|
||||
context.BeginFigure(PointAtAngle(Math.Min(StartAngle, EndAngle)), false, false);
|
||||
|
||||
context.ArcTo(PointAtAngle(Math.Max(StartAngle, EndAngle)), arcSize, 0, IsLargeArc, SweepDirection, true, false);
|
||||
|
||||
geometryStream.Transform = new TranslateTransform(StrokeThickness / 2, StrokeThickness / 2);
|
||||
|
||||
return geometryStream;
|
||||
}
|
||||
|
||||
protected override Visual GetVisualChild(int index)
|
||||
{
|
||||
if(index != 0)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(index), "Arc should have only 1 child");
|
||||
}
|
||||
|
||||
EnsureRootLayout();
|
||||
|
||||
return _rootLayout;
|
||||
}
|
||||
|
||||
protected override Size MeasureOverride(Size availableSize)
|
||||
{
|
||||
EnsureRootLayout();
|
||||
|
||||
_rootLayout!.Measure(availableSize);
|
||||
return _rootLayout.DesiredSize;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Overrides the default OnRender method to draw the <see cref="Arc"/> element.
|
||||
/// </summary>
|
||||
/// <param name="drawingContext">A <see cref="DrawingContext"/> object that is drawn during the rendering pass of this <see cref="Shape"/>.</param>
|
||||
protected override void OnRender(DrawingContext drawingContext)
|
||||
{
|
||||
base.OnRender(drawingContext);
|
||||
Pen pen = new(Stroke, StrokeThickness) { StartLineCap = StrokeStartLineCap, EndLineCap = StrokeStartLineCap, };
|
||||
|
||||
drawingContext.DrawGeometry(Stroke, pen, DefinedGeometry());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Draws a point on the coordinates of the given angle. <para><see
|
||||
/// href="https://stackoverflow.com/a/36756365/13224348">Based on Mark Feldman implementation.</see></para>
|
||||
/// </summary>
|
||||
/// <param name="angle">The angle at which to create the point.</param>
|
||||
protected Point PointAtAngle(double angle)
|
||||
{
|
||||
if(SweepDirection == SweepDirection.Counterclockwise)
|
||||
{
|
||||
angle += 90;
|
||||
angle %= 360;
|
||||
if(angle < 0)
|
||||
{
|
||||
angle += 360;
|
||||
}
|
||||
|
||||
var radAngle = angle * (Math.PI / 180);
|
||||
var xRadius = (RenderSize.Width - StrokeThickness) / 2;
|
||||
var yRadius = (RenderSize.Height - StrokeThickness) / 2;
|
||||
|
||||
return new Point(xRadius + xRadius * Math.Cos(radAngle), yRadius - yRadius * Math.Sin(radAngle));
|
||||
}
|
||||
else
|
||||
{
|
||||
angle -= 90;
|
||||
angle %= 360;
|
||||
if(angle < 0)
|
||||
{
|
||||
angle += 360;
|
||||
}
|
||||
|
||||
var radAngle = angle * (Math.PI / 180);
|
||||
var xRadius = (RenderSize.Width - StrokeThickness) / 2;
|
||||
var yRadius = (RenderSize.Height - StrokeThickness) / 2;
|
||||
|
||||
return new Point(xRadius + xRadius * Math.Cos(-radAngle), yRadius - yRadius * Math.Sin(-radAngle));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Event triggered when one of the key parameters is changed. Forces the geometry to be redrawn.
|
||||
/// </summary>
|
||||
protected static void PropertyChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
if(d is not Arc control)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
control.IsLargeArc = Math.Abs(control.EndAngle - control.StartAngle) > 180;
|
||||
control.InvalidateVisual();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override Geometry DefiningGeometry => DefinedGeometry();
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the final angle from which the arc will be drawn.
|
||||
/// </summary>
|
||||
public double EndAngle { get => (double)GetValue(EndAngleProperty); set => SetValue(EndAngleProperty, value); }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether one of the two larger arc sweeps is chosen; otherwise, if is <see
|
||||
/// langword="false"/>, one of the smaller arc sweeps is chosen.
|
||||
/// </summary>
|
||||
public bool IsLargeArc { get; internal set; } = false;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the initial angle from which the arc will be drawn.
|
||||
/// </summary>
|
||||
public double StartAngle
|
||||
{
|
||||
get => (double)GetValue(StartAngleProperty);
|
||||
set => SetValue(StartAngleProperty, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the direction to where the arc will be drawn.
|
||||
/// </summary>
|
||||
public SweepDirection SweepDirection
|
||||
{
|
||||
get => (SweepDirection)GetValue(SweepDirectionProperty);
|
||||
set => SetValue(SweepDirectionProperty, value);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user