//usercontrol.xaml
<UserControl x:Class="WpfApp195.UCElpRotateFivePolygon"
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:WpfApp195"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<Grid>
<!--<Ellipse Width="{Binding UCElpWidth,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"
Height="{Binding UCElpHeight,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"
Stroke="Black"
StrokeThickness="3"
HorizontalAlignment="Center"
VerticalAlignment="Center"/>-->
<Path Stroke="Black"
StrokeThickness="3">
<Path.Data>
<EllipseGeometry Center="{Binding UCCenterPt,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"
RadiusX="{Binding UCRadiusX,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"
RadiusY="{Binding UCRadiusY,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"/>
</Path.Data>
</Path>
<Ellipse Width="20"
Height="20"
StrokeThickness="5"
Stroke="Black"
HorizontalAlignment="Center"
VerticalAlignment="Center"/>
<ItemsControl x:Name="ticksItemsControl"
>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Line X1="{Binding UCStartX,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"
Y1="{Binding UCStartY,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"
X2="{Binding UCEndX,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"
Y2="{Binding UCEndY,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"
Stroke="{Binding UCTickStroke,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"
StrokeThickness="{Binding UCTickStrokeThickness,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<Path Fill="Blue">
<Path.Data>
<PathGeometry>
<PathFigure StartPoint="0,5" IsClosed="True">
<LineSegment Point="50,5"/>
<LineSegment Point="50,25"/>
<LineSegment Point="25,50"/>
<LineSegment Point="0,25"/>
<LineSegment Point="0,5"/>
</PathFigure>
</PathGeometry>
</Path.Data>
<Path.RenderTransform>
<TransformGroup>
<TranslateTransform X="{Binding UCTX,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"
Y="{Binding UCTY,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"/>
<RotateTransform x:Name="polygonRotater"
CenterX="{Binding UCRotaterX,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"
CenterY="{Binding UCRotaterY,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"/>
</TransformGroup>
</Path.RenderTransform>
</Path>
<Line
Stroke="Red"
StrokeThickness="5"
StrokeEndLineCap="Triangle"
X1="{Binding UCLineStartX,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"
Y1="{Binding UCLineStartY,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"
X2="{Binding UCLineEndX,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"
Y2="{Binding UCLineEndY,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}">
<Line.RenderTransform>
<RotateTransform x:Name="lineRotater"
CenterX="{Binding UCRotaterX,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"
CenterY="{Binding UCRotaterY,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"/>
</Line.RenderTransform>
</Line>
</Grid>
</UserControl>
//usercontrol.xaml.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
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;
using System.Reflection;
using System.Windows.Ink;
namespace WpfApp195
{
/// <summary>
/// Interaction logic for UCElpRotateFivePolygon.xaml
/// </summary>
public partial class UCElpRotateFivePolygon : UserControl
{
public UCElpRotateFivePolygon()
{
InitializeComponent();
this.DataContext = this;
this.Loaded += UCElpRotateFivePolygon_Loaded;
this.SizeChanged += UCElpRotateFivePolygon_SizeChanged;
}
private void UCElpRotateFivePolygon_SizeChanged(object sender, SizeChangedEventArgs e)
{
Init();
}
private void UCElpRotateFivePolygon_Loaded(object sender, RoutedEventArgs e)
{
Init();
}
private System.Timers.Timer rotaterTmr;
private void Init()
{
//var instance = Activator.CreateInstance(typeof(UCElpRotateFivePolygon)) as UCElpRotateFivePolygon;
//if (instance != null)
//{
// var content = instance.Content as FrameworkElement;
// if (content != null)
// {
// UCElpHeight = content.ActualHeight;
// UCElpWidth = content.ActualWidth;
// }
//}
var content = this.Content as FrameworkElement;
if (content != null)
{
UCElpWidth = content.ActualHeight;
UCElpHeight = content.ActualHeight;
UCCenterPt = new Point(content.ActualWidth / 2, content.ActualHeight / 2);
UCRadiusX = content.ActualHeight / 2 - 50;
UCRadiusY = content.ActualHeight / 2 - 50;
UCTX = content.ActualWidth / 2 - 25;
UCRotaterX = content.ActualWidth / 2;
UCRotaterY = content.ActualHeight / 2;
UCLineStartX=content.ActualWidth / 2;
UCLineStartY=content.ActualHeight / 2;
UCLineEndX=content.ActualWidth / 2;
UCLineEndY = 105;
InitTickItemsControl();
InitPolygonRotateTimer();
}
}
private void InitPolygonRotateTimer()
{
if(rotaterTmr!=null)
{
return;
}
rotaterTmr=new System.Timers.Timer();
rotaterTmr.Elapsed += RotaterTmr_Elapsed;
rotaterTmr.Interval = 1000;
rotaterTmr.Start();
}
private void RotaterTmr_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
Application.Current?.Dispatcher.BeginInvoke(new Action(() =>
{
polygonRotater.Angle += 10;
if (polygonRotater.Angle >= 360)
{
polygonRotater.Angle = 0;
}
lineRotater.Angle += 10;
UCRotatedStr = $"lineRotater.Angle:{lineRotater.Angle},polygonRotater.Angle:{polygonRotater.Angle}";
}));
}
private void InitTickItemsControl()
{
List<LineTick> ticksList = new List<LineTick>();
double tickLength = 20;
Brush lineStroke = Brushes.Black;
double tickThickness = 0.0d;
double angleStep = 360 / 360;
double angleRadian = 0.0d;
for (int i = 0; i < 360; i++)
{
tickLength = i % 10 == 0 ? 50 : 20;
lineStroke = i % 10 == 0 ? Brushes.Red : Brushes.Black;
tickThickness = i % 10 == 0 ? 3 : 1;
angleRadian = (i * angleStep - 90) * Math.PI / 180;
ticksList.Add(new LineTick()
{
UCStartX = UCCenterPt.X + UCRadiusX * Math.Cos(angleRadian),
UCStartY = UCCenterPt.Y + UCRadiusY * Math.Sin(angleRadian),
UCEndX = UCCenterPt.X + (UCRadiusX - tickLength) * Math.Cos(angleRadian),
UCEndY = UCCenterPt.Y + (UCRadiusY - tickLength) * Math.Sin(angleRadian),
UCTickStroke = lineStroke,
UCTickStrokeThickness = tickThickness
});
}
ticksItemsControl.ItemsSource = ticksList;
}
public Point UCCenterPt
{
get { return (Point)GetValue(UCCenterPtProperty); }
set { SetValue(UCCenterPtProperty, value); }
}
// Using a DependencyProperty as the backing store for UCCenterPt. This enables animation, styling, binding, etc...
public static readonly DependencyProperty UCCenterPtProperty =
DependencyProperty.Register("UCCenterPt", typeof(Point),
typeof(UCElpRotateFivePolygon), new PropertyMetadata(new Point(0, 0)));
public double UCRadiusX
{
get { return (double)GetValue(UCRadiusXProperty); }
set { SetValue(UCRadiusXProperty, value); }
}
// Using a DependencyProperty as the backing store for UCRadiusX. This enables animation, styling, binding, etc...
public static readonly DependencyProperty UCRadiusXProperty =
DependencyProperty.Register("UCRadiusX", typeof(double),
typeof(UCElpRotateFivePolygon), new PropertyMetadata(0.0d));
public double UCRadiusY
{
get { return (double)GetValue(UCRadiusYProperty); }
set { SetValue(UCRadiusYProperty, value); }
}
// Using a DependencyProperty as the backing store for UCRadiusY. This enables animation, styling, binding, etc...
public static readonly DependencyProperty UCRadiusYProperty =
DependencyProperty.Register("UCRadiusY", typeof(double),
typeof(UCElpRotateFivePolygon), new PropertyMetadata(0.0d));
public double UCElpWidth
{
get { return (double)GetValue(UCElpWidthProperty); }
set { SetValue(UCElpWidthProperty, value); }
}
// Using a DependencyProperty as the backing store for UCElpWidth. This enables animation, styling, binding, etc...
public static readonly DependencyProperty UCElpWidthProperty =
DependencyProperty.Register("UCElpWidth", typeof(double),
typeof(UCElpRotateFivePolygon), new PropertyMetadata(GetDefaultElpWidth()));
private static double GetDefaultElpWidth()
{
var content = Assembly.GetExecutingAssembly().GetType().GetProperties();
return 800.0d;
}
public double UCElpHeight
{
get { return (double)GetValue(ElpHeightProperty); }
set { SetValue(ElpHeightProperty, value); }
}
// Using a DependencyProperty as the backing store for ElpHeight. This enables animation, styling, binding, etc...
public static readonly DependencyProperty ElpHeightProperty =
DependencyProperty.Register("ElpHeight", typeof(double), typeof(UCElpRotateFivePolygon),
new PropertyMetadata(GetDefaultElpHeight()));
private static double GetDefaultElpHeight()
{
return 800.0d;
}
public double UCTX
{
get { return (double)GetValue(UCTXProperty); }
set { SetValue(UCTXProperty, value); }
}
// Using a DependencyProperty as the backing store for UCTX. This enables animation, styling, binding, etc...
public static readonly DependencyProperty UCTXProperty =
DependencyProperty.Register("UCTX", typeof(double),
typeof(UCElpRotateFivePolygon), new PropertyMetadata(0.0d));
public double UCTY
{
get { return (double)GetValue(UCTYProperty); }
set { SetValue(UCTYProperty, value); }
}
// Using a DependencyProperty as the backing store for UCTY. This enables animation, styling, binding, etc...
public static readonly DependencyProperty UCTYProperty =
DependencyProperty.Register("UCTY", typeof(double),
typeof(UCElpRotateFivePolygon), new PropertyMetadata(0.0d));
public double UCRotaterX
{
get { return (double)GetValue(UCRotaterXProperty); }
set { SetValue(UCRotaterXProperty, value); }
}
// Using a DependencyProperty as the backing store for UCRotaterX. This enables animation, styling, binding, etc...
public static readonly DependencyProperty UCRotaterXProperty =
DependencyProperty.Register("UCRotaterX", typeof(double),
typeof(UCElpRotateFivePolygon), new PropertyMetadata(0.0d));
public double UCRotaterY
{
get { return (double)GetValue(UCRotaterYProperty); }
set { SetValue(UCRotaterYProperty, value); }
}
// Using a DependencyProperty as the backing store for UCRotaterY. This enables animation, styling, binding, etc...
public static readonly DependencyProperty UCRotaterYProperty =
DependencyProperty.Register("UCRotaterY", typeof(double),
typeof(UCElpRotateFivePolygon), new PropertyMetadata(0.0d));
public double UCLineStartX
{
get { return (double)GetValue(UCLineStartXProperty); }
set { SetValue(UCLineStartXProperty, value); }
}
// Using a DependencyProperty as the backing store for UCLineStartX. This enables animation, styling, binding, etc...
public static readonly DependencyProperty UCLineStartXProperty =
DependencyProperty.Register("UCLineStartX", typeof(double),
typeof(UCElpRotateFivePolygon), new PropertyMetadata(0.0d));
public double UCLineStartY
{
get { return (double)GetValue(UCLineStartYProperty); }
set { SetValue(UCLineStartYProperty, value); }
}
// Using a DependencyProperty as the backing store for UCLineStartY. This enables animation, styling, binding, etc...
public static readonly DependencyProperty UCLineStartYProperty =
DependencyProperty.Register("UCLineStartY", typeof(double),
typeof(UCElpRotateFivePolygon), new PropertyMetadata(0.0d));
public double UCLineEndX
{
get { return (double)GetValue(UCLineEndXProperty); }
set { SetValue(UCLineEndXProperty, value); }
}
// Using a DependencyProperty as the backing store for UCLineEndX. This enables animation, styling, binding, etc...
public static readonly DependencyProperty UCLineEndXProperty =
DependencyProperty.Register("UCLineEndX", typeof(double),
typeof(UCElpRotateFivePolygon), new PropertyMetadata(0.0d));
public double UCLineEndY
{
get { return (double)GetValue(UCLineEndYProperty); }
set { SetValue(UCLineEndYProperty, value); }
}
// Using a DependencyProperty as the backing store for UCLineEndY. This enables animation, styling, binding, etc...
public static readonly DependencyProperty UCLineEndYProperty =
DependencyProperty.Register("UCLineEndY", typeof(double),
typeof(UCElpRotateFivePolygon), new PropertyMetadata(0.0d));
public string UCRotatedStr
{
get { return (string)GetValue(UCRotatedStrProperty); }
set { SetValue(UCRotatedStrProperty, value); }
}
// Using a DependencyProperty as the backing store for UCRotatedStr. This enables animation, styling, binding, etc...
public static readonly DependencyProperty UCRotatedStrProperty =
DependencyProperty.Register("UCRotatedStr", typeof(string),
typeof(UCElpRotateFivePolygon), new PropertyMetadata(""));
}
public class LineTick
{
public double UCStartX { get; set; }
public double UCStartY { get; set; }
public double UCEndX { get; set; }
public double UCEndY { get; set; }
public Brush UCTickStroke { get; set; }
public double UCTickStrokeThickness { get; set; }
}
}
//window.xaml
<Window x:Class="WpfApp195.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:WpfApp195"
WindowState="Maximized"
mc:Ignorable="d"
Title="{Binding DataContext.RotatedStr,Mode=TwoWay}"
Height="450" Width="800">
<Grid>
<local:UCElpRotateFivePolygon
UCRotatedStr="{Binding DataContext.RotatedStr,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 WpfApp195
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window,INotifyPropertyChanged
{
public MainWindow()
{
InitializeComponent();
this.DataContext = this;
}
private string rotatedStr="";
public string RotatedStr
{
get
{
return rotatedStr;
}
set
{
if(value != rotatedStr)
{
rotatedStr = value;
OnPropertyChanged();
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged([CallerMemberName]string propName="")
{
var handler = PropertyChanged;
if(handler!=null)
{
handler?.Invoke(this, new PropertyChangedEventArgs(propName));
}
}
}
}