//xaml
<Window x:Class="WpfApp220.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"
WindowState="Maximized"
xmlns:local="clr-namespace:WpfApp220"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>
<Ellipse x:Name="elp"
Stroke="Black"
StrokeThickness="10"
Width="{Binding ElpWidth,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"
Height="{Binding ElpHeight,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"
RenderTransformOrigin="0.5,0.5" >
<Ellipse.Fill>
<!--<LinearGradientBrush StartPoint="0,0" EndPoint="0,0.2" SpreadMethod="Reflect">
<GradientStop Offset="0.0" Color="Red"/>
<GradientStop Offset="0.1" Color="Black"/>
<GradientStop Offset="0.2" Color="Red"/>
<GradientStop Offset="0.3" Color="Black"/>
<GradientStop Offset="0.4" Color="Red"/>
<GradientStop Offset="0.5" Color="Black"/>
<GradientStop Offset="0.6" Color="Red"/>
<GradientStop Offset="0.7" Color="Black"/>
<GradientStop Offset="0.8" Color="Red"/>
<GradientStop Offset="0.9" Color="Black"/>
<GradientStop Offset="1" Color="Red"/>
</LinearGradientBrush>-->
<ImageBrush ImageSource="{Binding ImgSource,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"/>
</Ellipse.Fill>
<Ellipse.RenderTransform>
<RotateTransform x:Name="elpRotater"/>
</Ellipse.RenderTransform>
</Ellipse>
<ItemsControl x:Name="ticksItemsControl"
>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Line X1="{Binding TickStartX}"
Y1="{Binding TickStartY}"
X2="{Binding TickEndX}"
Y2="{Binding TickEndY}"
Stroke="{Binding TickStroke}"
StrokeThickness="{Binding TickThickness}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.RenderTransform>
<RotateTransform CenterX="{Binding CenterX,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"
CenterY="{Binding CenterY,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"/>
</ItemsControl.RenderTransform>
</ItemsControl>
<Ellipse Width="100"
Height="100"
Stroke="Black"
StrokeThickness="10"
HorizontalAlignment="Center"
VerticalAlignment="Center"/>
<Line Stroke="Red"
StrokeThickness="10"
StrokeStartLineCap="Round"
StrokeEndLineCap="Triangle"
X1="{Binding CenterX,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"
Y1="{Binding CenterY,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"
X2="{Binding LineEndX,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"
Y2="{Binding LineEndY,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"
RenderTransformOrigin="0.5,0.5">
<Line.RenderTransform>
<RotateTransform x:Name="lineRotater"/>
</Line.RenderTransform>
</Line>
</Grid>
</Window>
//cs
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Animation;
namespace WpfApp220
{
/// <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 RotateTransform elpRotater;
private DoubleAnimation animation;
private RotateTransform lineRotater;
public MainVM(Window winValue)
{
win = winValue;
if (win != null)
{
win.Loaded += Win_Loaded;
win.SizeChanged += Win_SizeChanged;
}
}
private void Win_SizeChanged(object sender, SizeChangedEventArgs e)
{
Init();
}
private void Win_Loaded(object sender, RoutedEventArgs e)
{
Init();
}
private System.Timers.Timer tmr;
private void Init()
{
var fe = win.Content as FrameworkElement;
if (fe != null)
{
ElpWidth = fe.ActualHeight;
ElpHeight = fe.ActualHeight;
TickRadius = fe.ActualHeight / 2 - 50;
CenterX = fe.ActualWidth / 2;
CenterY = fe.ActualHeight / 2;
LineEndX = fe.ActualWidth / 2;
LineEndY = 15;
}
if (tmr == null)
{
tmr = new System.Timers.Timer();
tmr.Elapsed += Tmr_Elapsed;
tmr.Interval = 1000;
tmr.Start();
}
//var tempRotater = win.FindName("elpRotater") as RotateTransform;
//if (tempRotater != null)
//{
// elpRotater = tempRotater;
// animation = new DoubleAnimation();
// animation.From = 0;
// animation.To = 360;
// animation.RepeatBehavior = RepeatBehavior.Forever;
// animation.Duration = TimeSpan.FromSeconds(12);
// elpRotater.BeginAnimation(RotateTransform.AngleProperty, animation);
//}
InitTicks();
var tempLineRotater = win.FindName("lineRotater") as RotateTransform;
if(tempLineRotater != null)
{
lineRotater = tempLineRotater;
animation = new DoubleAnimation();
animation.From = 0;
animation.To = 360;
animation.RepeatBehavior = RepeatBehavior.Forever;
animation.Duration = TimeSpan.FromSeconds(12);
lineRotater.BeginAnimation(RotateTransform.AngleProperty, animation);
}
}
private void InitTicks()
{
int ticksCount = 60;
Brush tempTickStroke = Brushes.Black;
double tempTickThickess = 2;
double tickLength = 30;
double angleStep = 360 / 60;
double angleRadian = 0;
TicksList = new List<TickClass>();
for (int i = 0; i < ticksCount; i++)
{
angleRadian = (i * angleStep - 90) * Math.PI / 180;
tickLength = i % 5 == 0 ? 50 : 30;
tempTickStroke = i % 5 == 0 ? Brushes.Red : Brushes.Black;
tempTickThickess = i % 5 == 0 ? 5 : 2;
TicksList.Add(new TickClass()
{
TickStartX = CenterX + TickRadius * Math.Cos(angleRadian),
TickStartY = CenterY + TickRadius * Math.Sin(angleRadian),
TickEndX = CenterX + (TickRadius + tickLength) * Math.Cos(angleRadian),
TickEndY = CenterY + (TickRadius + tickLength) * Math.Sin(angleRadian),
TickStroke = tempTickStroke,
TickThickness = tempTickThickess
});
}
var itemsControl = win.FindName("ticksItemsControl") as ItemsControl;
if(itemsControl != null)
{
itemsControl.ItemsSource = TicksList;
}
}
private void Tmr_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
}
private double lineEndX;
public double LineEndX
{
get
{
return lineEndX;
}
set
{
if (value != lineEndX)
{
lineEndX = value;
OnPropertyChanged(nameof(LineEndX));
}
}
}
private double lineEndY;
public double LineEndY
{
get
{
return lineEndY;
}
set
{
if (value != lineEndY)
{
lineEndY = value;
OnPropertyChanged(nameof(LineEndY));
}
}
}
private List<TickClass> ticksList;
public List<TickClass> TicksList
{
get
{
return ticksList;
}
set
{
if (ticksList != value)
{
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));
}
}
}
private ImageSource imgSource;
public ImageSource ImgSource
{
get
{
return imgSource;
}
set
{
if (value != imgSource)
{
imgSource = value;
OnPropertyChanged(nameof(ImgSource));
}
}
}
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));
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged([CallerMemberName] string propertyName = "")
{
var handler = PropertyChanged;
if (handler != null)
{
handler?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}
public class TickClass
{
public double TickStartX { get; set; }
public double TickStartY { get; set; }
public double TickEndX { get; set; }
public double TickEndY { get; set; }
public Brush TickStroke { get; set; }
public double TickThickness { get; set; }
}
}
![]()
![]()