gpt4 book ai didi

c# - WPF 创建单个 block 的虚线椭圆

转载 作者:行者123 更新时间:2023-11-30 17:34:58 34 4
gpt4 key购买 nike

是否有任何简单的方法来创建由单个水平虚线组成的虚线椭圆,其中虚线大小一致,并且可以指定它们的数量?

像这样:

enter image description here

我希望能够单独控制每个破折号,例如更改其颜色或将其绑定(bind)到我的 View 模型中的操作。

我能想到的实现这一点的唯一方法是创建一个自定义控件,其中包含每个破折号的路径元素,共同构成一个椭圆形,必须根据破折号的数量和大小计算路径数据椭圆的。

最佳答案

我现在回到这个问题上,并设法以一种非常灵活和通用的方式解决了它。从那以后需求发生了一些变化,不需要绑定(bind),但是可以很容易地添加。

注意这是一个圆圈,这就是我想要的。这个问题真的应该说圆而不是椭圆,即使圆是椭圆,但我离题了......

这是我想出的 UserControl:

StatusRing.xaml.cs

public partial class StatusRing
{
#region Dependency Property registrations

public static readonly DependencyProperty DashesProperty = DependencyProperty.Register("Dashes",
typeof(int), typeof(StatusRing), new PropertyMetadata(32, DashesChanged));

public static readonly DependencyProperty DiameterProperty = DependencyProperty.Register("Diameter",
typeof(double), typeof(StatusRing), new PropertyMetadata(150.00, DiameterChanged));

public static readonly DependencyProperty DashHeightProperty = DependencyProperty.Register("DashHeight",
typeof(double), typeof(StatusRing), new PropertyMetadata(20.00, DashHeightChanged));

public static readonly DependencyProperty DashWidthProperty = DependencyProperty.Register("DashWidth",
typeof(double), typeof(StatusRing), new PropertyMetadata(5.00, DashWidthChanged));

public static readonly DependencyProperty DashFillProperty = DependencyProperty.Register("DashFill",
typeof(SolidColorBrush), typeof(StatusRing), new PropertyMetadata(Brushes.DimGray, DashFillChanged));

public static readonly DependencyProperty DashAccentFillProperty = DependencyProperty.Register("DashAccentFill",
typeof(SolidColorBrush), typeof(StatusRing), new PropertyMetadata(Brushes.White, DashAnimationFillChanged));

public static readonly DependencyProperty TailSizeProperty = DependencyProperty.Register("TailSize",
typeof(int), typeof(StatusRing), new PropertyMetadata(10, TailSizeChanged));

public static readonly DependencyProperty AnimationSpeedProperty = DependencyProperty.Register("AnimationSpeed",
typeof(double), typeof(StatusRing), new PropertyMetadata(50.00, AnimationSpeedChanged));

public static readonly DependencyProperty IsPlayingProperty = DependencyProperty.Register("IsPlaying",
typeof(bool), typeof(StatusRing), new PropertyMetadata(false, IsPlayingChanged));

#endregion Dependency Property registrations

private readonly Storyboard glowAnimationStoryBoard = new Storyboard();

public StatusRing()
{
Loaded += OnLoaded;
InitializeComponent();
}

#region Dependency Properties

public int Dashes
{
get => (int)GetValue(DashesProperty);
set => SetValue(DashesProperty, value);
}

public double Diameter
{
get => (double)GetValue(DiameterProperty);
set => SetValue(DiameterProperty, value);
}

public double Radius => Diameter / 2;

public double DashHeight
{
get => (double)GetValue(DashHeightProperty);
set => SetValue(DashHeightProperty, value);
}

public double DashWidth
{
get => (double)GetValue(DashWidthProperty);
set => SetValue(DashWidthProperty, value);
}

public Brush DashFill
{
get => (SolidColorBrush)GetValue(DashFillProperty);
set => SetValue(DashFillProperty, value);
}

public Brush DashAccentFill
{
get => (SolidColorBrush)GetValue(DashAccentFillProperty);
set => SetValue(DashAccentFillProperty, value);
}

public int TailSize
{
get => (int)GetValue(TailSizeProperty);
set => SetValue(TailSizeProperty, value);
}

public double AnimationSpeed
{
get => (double)GetValue(AnimationSpeedProperty);
set => SetValue(AnimationSpeedProperty, value);
}

public bool IsPlaying
{
get => (bool)GetValue(IsPlayingProperty);
set => SetValue(IsPlayingProperty, value);
}

#endregion Dependency Properties

private void OnLoaded(object sender, RoutedEventArgs e)
{
var thisControl = sender as StatusRing;
Recreate(thisControl);
}

#region Dependency Property callbacks

private static void DashesChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var thisControl = d as StatusRing;
Recreate(thisControl);
}

private static void DiameterChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var thisControl = d as StatusRing;
Recreate(thisControl);
}

private static void DashHeightChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var thisControl = d as StatusRing;
Recreate(thisControl);
}

private static void DashWidthChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var thisControl = d as StatusRing;
Recreate(thisControl);
}

private static void DashFillChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var thisControl = d as StatusRing;
Recreate(thisControl);
}

private static void DashAnimationFillChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var thisControl = d as StatusRing;
Recreate(thisControl);
}

private static void TailSizeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var thisControl = d as StatusRing;
Recreate(thisControl);
}

private static void AnimationSpeedChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var thisControl = d as StatusRing;
if (thisControl.IsLoaded)
{
thisControl.glowAnimationStoryBoard.Stop();
thisControl.glowAnimationStoryBoard.Children.Clear();

ApplyAnimations(thisControl);

thisControl.glowAnimationStoryBoard.Begin();
}
}

private static void IsPlayingChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var thisControl = d as StatusRing;
if (thisControl.IsLoaded)
{
var isPlaying = (bool)e.NewValue;
if (isPlaying)
{
thisControl.glowAnimationStoryBoard.Begin();
}
else
{
thisControl.glowAnimationStoryBoard.Stop();
}
}
}

#endregion Dependency Property callbacks

private static void Recreate(StatusRing thisControl)
{
if (thisControl.IsLoaded)
{
thisControl.glowAnimationStoryBoard.Stop();
thisControl.glowAnimationStoryBoard.Children.Clear();
thisControl.RootCanvas.Children.Clear();

Validate(thisControl);
BuildRing(thisControl);

ApplyAnimations(thisControl);

if (thisControl.IsPlaying)
{
thisControl.glowAnimationStoryBoard.Begin();
}
else
{
thisControl.glowAnimationStoryBoard.Stop();
}
}
}

private static void Validate(StatusRing thisControl)
{
if (thisControl.TailSize > thisControl.Dashes)
{
throw new Exception("TailSize cannot be larger than amount of dashes");
}
}

private static void BuildRing(StatusRing thisControl)
{
var angleStep = (double)360 / thisControl.Dashes;

for (double i = 0; i < 360; i = i + angleStep)
{
var rect = new Rectangle
{
Fill = thisControl.DashFill,
Height = thisControl.DashHeight,
Width = thisControl.DashWidth
};

//Rotate dash to follow circles circumference
var centerY = thisControl.Radius;
var centerX = thisControl.DashWidth / 2;
var rotateTransform = new RotateTransform(i, centerX, centerY);
rect.RenderTransform = rotateTransform;

var offset = thisControl.Radius - thisControl.DashWidth / 2;
rect.SetValue(Canvas.LeftProperty, offset);

thisControl.RootCanvas.Children.Add(rect);
}

thisControl.RootCanvas.Width = thisControl.Diameter;
thisControl.RootCanvas.Height = thisControl.Diameter;
}

private static void ApplyAnimations(StatusRing thisControl)
{
var baseColor = ((SolidColorBrush)thisControl.DashFill).Color;
var animatedColor = ((SolidColorBrush)thisControl.DashAccentFill).Color;

var dashes = thisControl.RootCanvas.Children.OfType<Rectangle>().ToList();

double animationPeriod = thisControl.AnimationSpeed;
double glowDuration = animationPeriod * thisControl.TailSize;

for (int i = 0; i < dashes.Count; i++)
{
var beginTime = TimeSpan.FromMilliseconds(animationPeriod * i);

var colorAnimation = new ColorAnimationUsingKeyFrames
{
BeginTime = beginTime,
RepeatBehavior = RepeatBehavior.Forever
};

var toFillColor = new LinearColorKeyFrame(animatedColor, TimeSpan.Zero);
colorAnimation.KeyFrames.Add(toFillColor);

var dimToBase = new LinearColorKeyFrame(baseColor, TimeSpan.FromMilliseconds(glowDuration));
colorAnimation.KeyFrames.Add(dimToBase);

var restingTime = animationPeriod * dashes.Count;
var delay = new LinearColorKeyFrame(baseColor, TimeSpan.FromMilliseconds(restingTime));
colorAnimation.KeyFrames.Add(delay);

Storyboard.SetTarget(colorAnimation, dashes[i]);
Storyboard.SetTargetProperty(colorAnimation, new PropertyPath("(Fill).(SolidColorBrush.Color)"));

thisControl.glowAnimationStoryBoard.Children.Add(colorAnimation);
}
}
}

状态环.xaml:

<UserControl x:Class="WpfPlayground.StatusRing"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<Canvas x:Name="RootCanvas" />

用法:

<local:StatusRing Diameter="250" 
Dashes="32"
TailSize="16"
IsPlaying="True" />

结果:

StatusRing in action

短划线的数量、动画的长度和速度等......都是可配置的。依赖属性的命名可能会更好......

享受 :-)

关于c# - WPF 创建单个 block 的虚线椭圆,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41746734/

34 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com