WPF Ellipse rotate via doubleanimation in mvvm
//xaml <Window x:Class="WpfApp7.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:WpfApp7" mc:Ignorable="d" Title="MainWindow" Height="450" Width="800"> <Grid> <Ellipse x:Name="elp" HorizontalAlignment="Center" VerticalAlignment="Center" Width="{Binding ElpWidth,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" Height="{Binding ElpHeight,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" StrokeThickness="20" RenderTransformOrigin="0.5,0.5"> <Ellipse.Stroke> <LinearGradientBrush StartPoint="0,0" EndPoint="1,1" > <GradientStop Color="Black" Offset="0.0"/> <GradientStop Color="Red" Offset="0.1"/> <GradientStop Color="Black" Offset="0.2"/> <GradientStop Color="Red" Offset="0.3"/> <GradientStop Color="Black" Offset="0.4"/> <GradientStop Color="Red" Offset="0.5"/> <GradientStop Color="Black" Offset="0.6"/> <GradientStop Color="Red" Offset="0.7"/> <GradientStop Color="Black" Offset="0.8"/> <GradientStop Color="Red" Offset="0.9"/> <GradientStop Color="Black" Offset="1.0"/> </LinearGradientBrush> </Ellipse.Stroke> <Ellipse.Fill> <ImageBrush ImageSource="{Binding ElpImgSource,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"/> </Ellipse.Fill> <Ellipse.RenderTransform> <RotateTransform x:Name="elpRotateTransform"/> </Ellipse.RenderTransform> </Ellipse> <ItemsControl x:Name="ticksControl" ItemsSource="{Binding TicksList,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" > <ItemsControl.ItemsPanel> <ItemsPanelTemplate> <Canvas/> </ItemsPanelTemplate> </ItemsControl.ItemsPanel> <ItemsControl.ItemTemplate> <DataTemplate> <Line X1="{Binding StartX}" Y1="{Binding StartY}" X2="{Binding EndX}" Y2="{Binding EndY}" Stroke="{Binding TickStroke}" StrokeThickness="{Binding TickStrokeThickness}"/> </DataTemplate> </ItemsControl.ItemTemplate> </ItemsControl> </Grid> </Window> //cs using System.Collections.ObjectModel; using System.ComponentModel; using System.IO; using System.Text; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Animation; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; namespace WpfApp7 { /// <summary> /// Interaction logic for MainWindow.xaml /// </summary> public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); var vm = new MainVM(this); this.DataContext = vm; } } public class MainVM : INotifyPropertyChanged { private Window win; private FrameworkElement fe; private int ticksCount = 60; private ItemsControl ticksControl; private Ellipse elp; private RotateTransform elpRotateTransform; public MainVM(Window winValue) { win = winValue; win.SizeChanged += Win_SizeChanged; win.Loaded += Win_Loaded; } private void Win_Loaded(object sender, RoutedEventArgs e) { Init(); } private void Win_SizeChanged(object sender, SizeChangedEventArgs e) { Init(); } private void Init() { var tempFe = win.Content as FrameworkElement; if (tempFe != null) { fe = tempFe; CenterX = fe.ActualWidth / 2; CenterY = fe.ActualHeight / 2; TickRadius = Math.Min(fe.ActualWidth, fe.ActualHeight) / 2 - 50; ElpHeight = fe.ActualHeight; ElpWidth = fe.ActualHeight; string imgUrl=@"../../../Images/10.jpg"; if(File.Exists(imgUrl)) { ElpImgSource = new BitmapImage(new Uri(imgUrl, UriKind.RelativeOrAbsolute)); } else { MessageBox.Show("Image not found!"); } } var tempTicksControl = win.FindName("ticksControl") as ItemsControl; if (tempTicksControl != null) { ticksControl = tempTicksControl; } else { MessageBox.Show("TicksControl not found!"); } var tempElp = win.FindName("elp") as Ellipse; if (tempElp != null) { elp = tempElp; } else { MessageBox.Show("Ellipse not found!"); } var tempElpRotateTransform = win.FindName("elpRotateTransform") as RotateTransform; if (tempElpRotateTransform != null) { elpRotateTransform = tempElpRotateTransform; } else { MessageBox.Show("Ellipse RotateTransform not found!"); } InitTicks(); InitElpRotationDoubleAnimation(); } DoubleAnimation animation; Storyboard animationBoard; private void InitElpRotationDoubleAnimation() { animationBoard = new Storyboard(); animation = new DoubleAnimation(); animation.From = 0; animation.To = 360; animation.Duration = TimeSpan.FromSeconds(10); animation.RepeatBehavior = RepeatBehavior.Forever; elpRotateTransform.BeginAnimation(RotateTransform.AngleProperty, animation); //Storyboard.SetTargetProperty(elp,new PropertyPath("(Ellipse.RenderTransform).(RotateTransform.Angle)")); //Storyboard.SetTarget(animation, elp); //animationBoard.Children.Add(animation); //animationBoard.Begin(); } private void InitTicks() { List<TickClass> ticksList = new List<TickClass>(); double angleStep = (double)(360.0 / ticksCount); double tickLength = 30; Brush tickStroke = Brushes.Black; double tickStrokeThickness = 1; for (int i = 0; i < ticksCount; i++) { tickLength = i % 5 == 0 ? 50 : 30; tickStroke = i % 5 == 0 ? Brushes.Red : Brushes.Black; tickStrokeThickness = i % 5 == 0 ? 3 : 1; double angle = (double)(angleStep * i - 90); ticksList.Add(new TickClass() { StartX = CenterX + TickRadius * Math.Cos((angle) * Math.PI / 180), StartY = CenterY + TickRadius * Math.Sin((angle) * Math.PI / 180), EndX = CenterX + (TickRadius + tickLength) * Math.Cos((angle) * Math.PI / 180), EndY = CenterY + (TickRadius + tickLength) * Math.Sin((angle) * Math.PI / 180), TickStroke = tickStroke, TickStrokeThickness = tickStrokeThickness }); } TicksList = new ObservableCollection<TickClass>(ticksList); } public event PropertyChangedEventHandler? PropertyChanged; protected void OnPropertyChanged(string propertyName) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } private ImageSource elpImgSource; public ImageSource ElpImgSource { get { return elpImgSource; } set { if(value!= elpImgSource) { elpImgSource = value; OnPropertyChanged(nameof(ElpImgSource)); } } } private double elpWidth; public double ElpWidth { get { return elpWidth; } set { if (value != elpWidth) { elpWidth = value; OnPropertyChanged(nameof(ElpWidth)); } } } private double elpHeight; public double ElpHeight { get { return elpHeight; } set { if (value != elpHeight) { elpHeight = value; OnPropertyChanged(nameof(ElpHeight)); } } } private ObservableCollection<TickClass> ticksList; public ObservableCollection<TickClass> TicksList { get { return ticksList; } set { if (value != ticksList) { ticksList = value; OnPropertyChanged(nameof(TicksList)); } } } private double centerX; public double CenterX { get { return centerX; } set { if (value != centerX) { centerX = value; OnPropertyChanged(nameof(CenterX)); } } } private double centerY; public double CenterY { get { return centerY; } set { if (value != centerY) { centerY = value; OnPropertyChanged(nameof(CenterY)); } } } private double tickRadius; public double TickRadius { get { return tickRadius; } set { if (value != tickRadius) { tickRadius = value; OnPropertyChanged(nameof(TickRadius)); } } } } public class TickClass { public double StartX { get; set; } public double StartY { get; set; } public double EndX { get; set; } public double EndY { get; set; } public Brush TickStroke { get; set; } public double TickStrokeThickness { get; set; } } }