//xaml
<Window x:Class="WpfApp182.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:WpfApp182"
mc:Ignorable="d"
KeyDown="Window_KeyDown"
Title="MainWindow" Height="450" Width="800">
<Grid>
<Path x:Name="triPath" Fill="Red">
<Path.Data>
<PathGeometry>
<PathFigure StartPoint="0,0" IsClosed="True">
<LineSegment Point="20,0"/>
<LineSegment Point="10,50"/>
</PathFigure>
</PathGeometry>
</Path.Data>
<Path.RenderTransform>
<TransformGroup>
<TranslateTransform x:Name="translater"
X="{Binding TX,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"
Y="{Binding TY,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"/>
<RotateTransform x:Name="rotater"
CenterX="{Binding TCenterX,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"
CenterY="{Binding TCenterY,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"/>
</TransformGroup>
</Path.RenderTransform>
</Path>
<Ellipse Width="{Binding ElpRadius,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"
Height="{Binding ElpRadius,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"
Stroke="Blue"
StrokeThickness="5"
VerticalAlignment="Center"
HorizontalAlignment="Center"
Panel.ZIndex="-1"/>
<ItemsControl x:Name="ticksItemsControl">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Line X1="{Binding Line1StartX}"
Y1="{Binding Line1StartY}"
X2="{Binding Line1EndX}"
Y2="{Binding Line1EndY}"
Stroke="{Binding Line1Stroke}"
StrokeThickness="{Binding Line1StrokeThickness}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<ItemsControl x:Name="numbersItemsControl">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding NumStr}"
x:Name="numTbk"
VerticalAlignment="Center"
HorizontalAlignment="Center"
Width="Auto"
Foreground="Black"
FontSize="30"
FontWeight="Bold">
<TextBlock.RenderTransform>
<TranslateTransform X="{Binding NumX}"
Y="{Binding NumY}"/>
</TextBlock.RenderTransform>
</TextBlock>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
</Window>
//xaml.cs
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
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.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace WpfApp182
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window, INotifyPropertyChanged
{
public MainWindow()
{
InitializeComponent();
this.DataContext = this;
this.Loaded += MainWindow_Loaded;
this.SizeChanged += MainWindow_SizeChanged;
}
private void MainWindow_SizeChanged(object sender, SizeChangedEventArgs e)
{
Init();
}
private void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
Init();
}
private double tCenterX;
public double TCenterX
{
get
{
return tCenterX;
}
set
{
if (value != tCenterX)
{
tCenterX = value;
OnPropertyChanged(nameof(TCenterX));
}
}
}
private double tCenterY;
public double TCenterY
{
get
{
return tCenterY;
}
set
{
if (value != tCenterY)
{
tCenterY = value;
OnPropertyChanged(nameof(TCenterY));
}
}
}
private double tX;
public double TX
{
get
{
return tX;
}
set
{
if (value != tX)
{
tX = value;
OnPropertyChanged(nameof(TX));
}
}
}
private double tY;
public double TY
{
get
{
return tY;
}
set
{
if (value != tY)
{
tY = value;
OnPropertyChanged(nameof(TY));
}
}
}
private Point elpCenter;
public Point ElpCenter
{
get
{
return elpCenter;
}
set
{
if (value != elpCenter)
{
elpCenter = value;
OnPropertyChanged(nameof(ElpCenter));
}
}
}
private double elpRadius;
public double ElpRadius
{
get
{
return elpRadius;
}
set
{
if (value != elpRadius)
{
elpRadius = value;
OnPropertyChanged(nameof(ElpRadius));
}
}
}
private double line1Radius;
public double Line1Radius
{
get
{
return line1Radius;
}
set
{
if (value != line1Radius)
{
line1Radius = value;
OnPropertyChanged(nameof(Line1Radius));
}
}
}
private double numRadius;
public double NumRadius
{
get
{
return numRadius;
}
set
{
if (value != numRadius)
{
numRadius = value;
OnPropertyChanged(nameof(NumRadius));
}
}
}
private bool isPaused = false;
private double shortTickLength = 30;
private double longTickLength = 60;
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged([CallerMemberName] string propName = "")
{
var handler = PropertyChanged;
if (handler != null)
{
handler?.Invoke(this, new PropertyChangedEventArgs(propName));
}
}
private System.Timers.Timer tmr { get; set; }
private void Init()
{
var fe = this.Content as FrameworkElement;
if (fe != null)
{
TCenterX = fe.ActualWidth / 2;
TCenterY = fe.ActualHeight / 2;
TX = fe.ActualWidth / 2 - 15;
TY = 0;
ElpCenter = new Point(fe.ActualWidth / 2, fe.ActualHeight / 2);
ElpRadius = fe.ActualHeight - 50;
Line1Radius = fe.ActualHeight / 2 - 25;
NumRadius = fe.ActualHeight / 2 - 80;
}
InitTicks();
InitNumbers();
if (tmr != null)
{
return;
}
tmr = new System.Timers.Timer();
tmr.Interval = 1000;
tmr.Elapsed += Tmr_Elapsed;
tmr.Start();
}
private void InitNumbers()
{
var numTbk = this.FindName("numTbk") as TextBlock;
List<NumClass> numsList = new List<NumClass>();
double angleStep = 360 / 12;
for (int i = 1; i <= 12; i++)
{
double angleRadian = (angleStep * (i) - 90) * Math.PI / 180;
numsList.Add(new NumClass()
{
NumStr = i.ToString(),
NumX = TCenterX + NumRadius * Math.Cos(angleRadian)-15,
NumY = tCenterY + NumRadius * Math.Sin(angleRadian)-25
});
}
numbersItemsControl.ItemsSource = numsList;
}
private void InitTicks()
{
int ticksCount = 60;
double angleStep = 360 / 60;
List<Line1Tick> linesList = new List<Line1Tick>();
for (int i = 0; i < ticksCount; i++)
{
double angleRadian = (i * angleStep - 90) * Math.PI / 180;
double tickLength = i % 5 == 0 ? longTickLength : shortTickLength;
Brush lineStroke = i % 5 == 0 ? Brushes.Red : Brushes.Black;
double lineThickness = i % 5 == 0 ? 5 : 2;
linesList.Add(new Line1Tick()
{
Line1StartX = TCenterX + Line1Radius * Math.Cos(angleRadian),
Line1StartY = TCenterY + Line1Radius * Math.Sin(angleRadian),
Line1EndX = TCenterX + (Line1Radius - tickLength) * Math.Cos(angleRadian),
Line1EndY = TCenterY + (Line1Radius - tickLength) * Math.Sin(angleRadian),
Line1Stroke = lineStroke,
Line1StrokeThickness = lineThickness
});
}
ticksItemsControl.ItemsSource = linesList;
}
private void Tmr_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
if (isPaused)
{
return;
}
try
{
Application.Current?.Dispatcher.BeginInvoke(new Action(() =>
{
rotater.Angle += 10;
if (rotater.Angle >= 360)
{
rotater.Angle = 0;
}
this.Title = rotater.Angle.ToString();
Console.WriteLine(Mouse.GetPosition(triPath).X + "," + Mouse.GetPosition(triPath).Y);
}));
}
catch (Exception ex)
{
MessageBox.Show(ex.Message + "\n" + ex.StackTrace.ToString());
}
}
private void Window_KeyDown(object sender, KeyEventArgs e)
{
isPaused = !isPaused;
}
}
public class Line1Tick
{
public double Line1StartX { get; set; }
public double Line1StartY { get; set; }
public double Line1EndX { get; set; }
public double Line1EndY { get; set; }
public Brush Line1Stroke { get; set; }
public double Line1StrokeThickness { get; set; }
}
public class NumClass
{
public string NumStr { get; set; }
public double NumX { get; set; }
public double NumY { get; set; }
}
}
![]()
![]()