动态修改XAML中的路径、几何图形、形状

wi3ka0sx  于 2022-12-07  发布在  其他
关注(0)|答案(2)|浏览(164)

I am stuck. I want to do some sophisticated animations in XAML, in which the geometry of the image gets modified at runtime. I want to start out simple, and then make something far more interesting, but nothing seems to work. For now, all I want to do is draw an arc on the screen using a for-loop, and this arc will be modified by a slider. The slider will control the starting point of the arc. So, if I have the slider at Pi/4, it will draw an arc from Pi/4 to 2Pi.
I've tried so many different ways. Right now I've created a class of the type Shape , and tried to modify the DefiningGeometry which is a property of the Shape class. The startRadians gets modified by the slider, so that part works OK, I got the binding to work. But, after startRadians gets changed (it is a DependencyProperty , btw) I want the class to re-calculate the geometry of the circle. (Like a cherry pie that is missing a bigger piece as startRadians gets changed.) The real problem is that DefiningGeometry is a read-only property, so I can't change it on the fly. (Am I right about this?) Even if I could, I don't know the way to write the line of code so that DrawMyArc fires again, and the results get reloaded into DefiningGeometry .
OK, so I need some guidance. Should I change the parent class, so that I have an easily modifiable Path/geometry? I am at a loss here.
Should I use an entirely different approach, like where you dynamically make/delete the geometry using StreamGeometry?
Here's the relevant code:

using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Media;
using System.Windows.Shapes;

namespace January14noon
{
    public class myCircle : Shape
    {

        public double startRadians
        {
            get { return (double)GetValue(startRadiansProperty); }
            set { SetValue(startRadiansProperty, value); }
        }

        protected override Geometry DefiningGeometry
        {
            get
            {
                return DrawMyArc(100, 200, true, 40, 40, 360, startRadians, 2 * Math.PI);
            }
        }
        // Using a DependencyProperty as the backing store for startRadians.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty startRadiansProperty =
            DependencyProperty.Register("startRadians", typeof(double), typeof(myCircle), new PropertyMetadata(Math.PI / 4, new PropertyChangedCallback(startRadians_PropertyChanged)));

        private static void startRadians_PropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            //
            //    
        }
        public PathGeometry DrawMyArc(double centX, double centY, bool CW, double radiusX, double radiusY, int numberOfSegs, double startRad, double endRad)
        {
            double[,] rawPoints = new double[numberOfSegs, 2];
            List<LineSegment> segments = new List<LineSegment>();
            double arcLength;

            
                arcLength = endRad - startRad;
                for (int i = 0; i < numberOfSegs; i++)
                {
                    rawPoints[i, 0] = radiusX * Math.Sin(i * (arcLength / numberOfSegs) + startRad) + centX;
                    rawPoints[i, 1] = radiusY * -Math.Cos(i * (arcLength / numberOfSegs) + startRad) + centY;

                    segments.Add(new LineSegment(new Point(rawPoints[i, 0], rawPoints[i, 1]), true));
                }
            
            LineSegment[] segArray = segments.ToArray();
            PathFigure figure = new PathFigure(new Point(centX, centY), segments, false);
            PathGeometry myGeometry = new PathGeometry();
            myGeometry.Figures.Add(figure);
            return myGeometry;
        }
    }
}

And XAML:

<Window x:Class="January14noon.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:local="clr-namespace:January14noon"
    mc:Ignorable="d"
        
    Title="MainWindow" Height="350" Width="525">
    <Window.Resources>
        <local:myCircle x:Key="myCircleDataSource" d:IsDataSource="True"/>
    </Window.Resources>
    <Grid DataContext="{Binding Source={StaticResource myCircleDataSource}}">
        <Slider x:Name="slider" VerticalAlignment="Top" Margin="0,0,163.898,0" Value="{Binding startRadians, Mode=OneWayToSource}"/>
        <TextBox x:Name="myTextBox" HorizontalAlignment="Right" Height="23" TextWrapping="Wrap" Text="{Binding startRadians}" VerticalAlignment="Top" Width="147.797"/>
        <!--<local:myCircle x:Name="instanceOfCircle" />-->
        <local:myCircle Stroke="Black" StrokeThickness="2"/>
    </Grid>
</Window>

Any help would be appreciated. Just general approach, something specific even words of encouragement.
TYIA

u5rb5r59

u5rb5r591#

Any dependency property that affects visual appearance of a control might be registered with appropriate FrameworkPropertyMetadataOptions , e.g. AffectsMeasure , AffectsArrange or AffectsRender .
Note also that class, property and method names in C# are supposed to use Pascal Casing, i.e. start with an uppercase letter

public static readonly DependencyProperty StartRadiansProperty =
    DependencyProperty.Register(nameof(StartRadians), typeof(double), typeof(MyCircle),
    new FrameworkPropertyMetadata(Math.PI / 4,
        FrameworkPropertyMetadataOptions.AffectsMeasure, StartRadiansPropertyChanged));

public double StartRadians
{
    get { return (double)GetValue(StartRadiansProperty); }
    set { SetValue(StartRadiansProperty, value); }
}

private static void StartRadiansPropertyChanged(
    DependencyObject d, DependencyPropertyChangedEventArgs e)
{
    ...
}
lkaoscv7

lkaoscv72#

确保将可见圆绑定到数据源:

<Window.Resources>
    <local:myCircle x:Key="myCircleDataSource" d:IsDataSource="True"/>
</Window.Resources>
<Grid DataContext="{Binding Source={StaticResource myCircleDataSource}}">
    <Slider x:Name="slider" VerticalAlignment="Top" Margin="0,0,163.898,0" Value="{Binding startRadians, Mode=OneWayToSource}"/>
    <TextBox x:Name="myTextBox" HorizontalAlignment="Right" Height="23" TextWrapping="Wrap" Text="{Binding startRadians}" VerticalAlignment="Top" Width="147.797"/>
    <!--<local:myCircle x:Name="instanceOfCircle" />-->
    <local:myCircle Stroke="Black" StrokeThickness="2" startRadians="{Binding startRadians}"/>

</Grid>

并在属性更改时使视觉对象无效:

private static void startRadians_PropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
    var circle = (myCircle)d;  
    circle.InvalidateVisual(); // <-- Invalidate!
}

无效操作将通知引擎重新呈现此视觉效果,这将调用您DrawMyArc()方法

相关问题