//usercontrol.xaml
<UserControl x:Class="WpfApp187.UCRotateTriangle"
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"
xmlns:local="clr-namespace:WpfApp187"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<Grid>
<Path Panel.ZIndex="-1"
Stroke="Black"
StrokeThickness="5">
<Path.Data>
<EllipseGeometry Center="{Binding UCCenter,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"
RadiusX="{Binding UCCircleRadius,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"
RadiusY="{Binding UCCircleRadius,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"/>
</Path.Data>
<Path.Fill>
<ImageBrush ImageSource="{Binding UCImgSource,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"/>
</Path.Fill>
</Path>
<ItemsControl x:Name="ticksItemsControl"
Panel.ZIndex="0">
<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 LineStroke}"
StrokeThickness="{Binding LineThickness}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<Path Panel.ZIndex="1" Fill="Red">
<Path.Data>
<PathGeometry>
<PathFigure StartPoint="0,0" IsClosed="True">
<LineSegment Point="30,0"/>
<LineSegment Point="15,50"/>
</PathFigure>
</PathGeometry>
</Path.Data>
<Path.RenderTransform>
<TransformGroup>
<TranslateTransform X="{Binding UCTriangleX,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"
Y="{Binding UCTriangleY,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"/>
<RotateTransform x:Name="triangleRotater"
CenterX="{Binding UCTriangleCenterX,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"
CenterY="{Binding UCTriangleCenterY,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"
Angle="{Binding UCTriangleAngle,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" />
</TransformGroup>
</Path.RenderTransform>
</Path>
</Grid>
</UserControl>
//usercontrol.xaml.cs
using System;
using System.Collections.Generic;
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 WpfApp187
{
/// <summary>
/// Interaction logic for UCRotateTriangle.xaml
/// </summary>
public partial class UCRotateTriangle : UserControl
{
public UCRotateTriangle()
{
InitializeComponent();
this.Loaded += UCRotateTriangle_Loaded;
this.SizeChanged += UCRotateTriangle_SizeChanged;
this.DataContext = this;
}
private void UCRotateTriangle_SizeChanged(object sender, SizeChangedEventArgs e)
{
Init();
}
private void UCRotateTriangle_Loaded(object sender, RoutedEventArgs e)
{
Init();
}
private void Init()
{
if(UCCenter==default)
{
UCCenter = new Point(this.ActualWidth / 2, this.ActualHeight / 2);
}
UCCircleRadius = Math.Min(this.ActualWidth, this.ActualHeight) / 2 - 50;
UCTriangleX = this.ActualWidth / 2 - 15;
UCTriangleY = 0;
UCTriangleCenterX = this.ActualWidth / 2;
UCTriangleCenterY = this.ActualHeight / 2;
UCTriangleAngle = 0;
InitTicks();
}
private System.Timers.Timer tmr { get; set; }
private void InitTicks()
{
double tickLength = 0;
Brush tickStroke = Brushes.Black;
double angleStep = 360 / 60;
double angleRadian = 0.0d;
double tickRadius = this.ActualHeight / 2 - 50;
double tickThickness = 0;
List<LineClass> linesList = new List<LineClass>();
for (int i = 0; i < 60; i++)
{
tickLength = i % 5 == 0 ? 50 : 20;
tickStroke = i % 5 == 0 ? Brushes.Red : Brushes.Black;
angleRadian = angleStep * i * Math.PI / 180;
tickThickness = i % 5 == 0 ? 5 : 1;
linesList.Add(new LineClass()
{
StartX = UCTriangleCenterX + (tickRadius) * Math.Cos(angleRadian),
StartY = UCTriangleCenterY + tickRadius * Math.Sin(angleRadian),
EndX = UCTriangleCenterX + (tickRadius - tickLength) * Math.Cos(angleRadian),
EndY = UCTriangleCenterY + (tickRadius - tickLength) * Math.Sin(angleRadian),
LineStroke = tickStroke,
LineThickness = tickThickness
});
}
ticksItemsControl.ItemsSource = linesList;
}
public Point UCCenter
{
get { return (Point)GetValue(UCCenterProperty); }
set { SetValue(UCCenterProperty, value); }
}
// Using a DependencyProperty as the backing store for UCCenter. This enables animation, styling, binding, etc...
public static readonly DependencyProperty UCCenterProperty =
DependencyProperty.Register("UCCenter", typeof(Point),
typeof(UCRotateTriangle), new PropertyMetadata(new Point(0, 0)));
public double UCCircleRadius
{
get { return (double)GetValue(UCCircleRadiusProperty); }
set { SetValue(UCCircleRadiusProperty, value); }
}
// Using a DependencyProperty as the backing store for UCCircleRadius. This enables animation, styling, binding, etc...
public static readonly DependencyProperty UCCircleRadiusProperty =
DependencyProperty.Register("UCCircleRadius", typeof(double),
typeof(UCRotateTriangle), new PropertyMetadata(0.0d));
public ImageSource UCImgSource
{
get { return (ImageSource)GetValue(UCImgSourceProperty); }
set { SetValue(UCImgSourceProperty, value); }
}
// Using a DependencyProperty as the backing store for UCImgSource. This enables animation, styling, binding, etc...
public static readonly DependencyProperty UCImgSourceProperty =
DependencyProperty.Register("UCImgSource", typeof(ImageSource),
typeof(UCRotateTriangle), new PropertyMetadata(null));
public double UCTriangleX
{
get { return (double)GetValue(UCTriangleXProperty); }
set { SetValue(UCTriangleXProperty, value); }
}
// Using a DependencyProperty as the backing store for UCTriangleX. This enables animation, styling, binding, etc...
public static readonly DependencyProperty UCTriangleXProperty =
DependencyProperty.Register("UCTriangleX", typeof(double),
typeof(UCRotateTriangle), new PropertyMetadata(0.0d));
public double UCTriangleY
{
get { return (double)GetValue(UCTriangleYProperty); }
set { SetValue(UCTriangleYProperty, value); }
}
// Using a DependencyProperty as the backing store for UCTriangleY. This enables animation, styling, binding, etc...
public static readonly DependencyProperty UCTriangleYProperty =
DependencyProperty.Register("UCTriangleY", typeof(double),
typeof(UCRotateTriangle), new PropertyMetadata(0.0d));
public double UCTriangleCenterX
{
get { return (double)GetValue(UCTriangleCenterXProperty); }
set { SetValue(UCTriangleCenterXProperty, value); }
}
// Using a DependencyProperty as the backing store for UCTriangleCenterX. This enables animation, styling, binding, etc...
public static readonly DependencyProperty UCTriangleCenterXProperty =
DependencyProperty.Register("UCTriangleCenterX", typeof(double),
typeof(UCRotateTriangle), new PropertyMetadata(0.0d));
public double UCTriangleCenterY
{
get { return (double)GetValue(UCTriangleCenterYProperty); }
set { SetValue(UCTriangleCenterYProperty, value); }
}
// Using a DependencyProperty as the backing store for UCTriangleCenterY. This enables animation, styling, binding, etc...
public static readonly DependencyProperty UCTriangleCenterYProperty =
DependencyProperty.Register("UCTriangleCenterY", typeof(double),
typeof(UCRotateTriangle), new PropertyMetadata(0.0d));
public double UCTriangleAngle
{
get { return (double)GetValue(UCTriangleAngleProperty); }
set { SetValue(UCTriangleAngleProperty, value); }
}
// Using a DependencyProperty as the backing store for UCTriangleAngle. This enables animation, styling, binding, etc...
public static readonly DependencyProperty UCTriangleAngleProperty =
DependencyProperty.Register("UCTriangleAngle", typeof(double),
typeof(UCRotateTriangle), new PropertyMetadata(0.0d));
}
public class LineClass
{
public double StartX { get; set; }
public double StartY { get; set; }
public double EndX { get; set; }
public double EndY { get; set; }
public Brush LineStroke { get; set; }
public double LineThickness { get; set; }
}
}
//window.xaml
<Window x:Class="WpfApp187.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:WpfApp187"
mc:Ignorable="d"
WindowState="Maximized"
Title="MainWindow" Height="450" Width="800">
<Grid>
<local:UCRotateTriangle
UCTriangleAngle="{Binding DataContext.TriangleAngle,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged,
RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type Window}}}"/>
</Grid>
</Window>
//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 WpfApp187
{
/// <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 { get; set; }
public MainVM(Window winValue)
{
win = winValue;
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 { get; set; }
private void Init()
{
var fe = win.Content as FrameworkElement;
if(fe!=null)
{
ElpCenter=new Point(fe.ActualWidth/4, fe.ActualHeight/4);
if(tmr!=null)
{
return;
}
tmr=new System.Timers.Timer();
tmr.Elapsed += Tmr_Elapsed;
tmr.Interval = 1000;
tmr.Start();
}
}
private void Tmr_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
try
{
TriangleAngle += 30;
if (TriangleAngle >= 360)
{
TriangleAngle = 0;
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
var handler = PropertyChanged;
if (handler != null)
{
handler?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
private double triangleAngle;
public double TriangleAngle
{
get
{
return triangleAngle;
}
set
{
if(triangleAngle!=value)
{
triangleAngle = value;
OnPropertyChanged();
OnTriangleAngleChanged();
}
}
}
private void OnTriangleAngleChanged()
{
Application.Current?.Dispatcher.BeginInvoke(new Action(() =>
{
win.Title=TriangleAngle.ToString();
}));
}
private Point elpCenter;
public Point ElpCenter
{
get
{
return elpCenter;
}
set
{
if (value != elpCenter)
{
elpCenter = value;
OnPropertyChanged();
}
}
}
}
}