//uc.xaml
<UserControl x:Class="WpfApp203.UCRotatedCircle"
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:WpfApp203"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<Grid>
<Ellipse x:Name="elp"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Width="{Binding UCElpWidth,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"
Height="{Binding UCElpHeight,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"
Stroke="Black"
StrokeThickness="5"
MouseDown="Ellipse_MouseDown"
MouseMove="Ellipse_MouseMove"
MouseUp="Ellipse_MouseUp"
RenderTransformOrigin="0.5,0.5">
<Ellipse.Fill>
<ImageBrush ImageSource="{Binding UCImgSource,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"/>
</Ellipse.Fill>
<Ellipse.RenderTransform>
<RotateTransform x:Name="elpRotater"
/>
</Ellipse.RenderTransform>
</Ellipse>
</Grid>
</UserControl>
//uc.xaml.cs
using System;
using System.Collections.Generic;
using System.Linq;
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.IO;
namespace WpfApp203
{
/// <summary>
/// Interaction logic for UCRotatedCircle.xaml
/// </summary>
public partial class UCRotatedCircle : UserControl
{
public UCRotatedCircle()
{
InitializeComponent();
this.DataContext = this;
this.Loaded += UCRotatedCircle_Loaded;
this.SizeChanged += UCRotatedCircle_SizeChanged;
}
private void UCRotatedCircle_SizeChanged(object sender, SizeChangedEventArgs e)
{
InitElp();
}
private void UCRotatedCircle_Loaded(object sender, RoutedEventArgs e)
{
InitElp();
}
private System.Timers.Timer tmr;
private void InitElp()
{
var ucContent = this.Content as FrameworkElement;
if (ucContent != null)
{
UCElpWidth = ucContent.ActualHeight;
UCElpHeight = ucContent.ActualHeight;
originPt = new Point(ucContent.ActualWidth / 2, ucContent.ActualHeight / 2);
UCCenterX = ucContent.ActualWidth / 2;
UCCenterY = ucContent.ActualHeight / 2;
}
if(tmr==null)
{
tmr=new System.Timers.Timer();
tmr.Elapsed += Tmr_Elapsed;
tmr.Interval = 100;
tmr.Start();
}
}
private void Tmr_Elapsed(object? sender, System.Timers.ElapsedEventArgs e)
{
Application.Current?.Dispatcher.BeginInvoke(new Action(() =>
{
if (UCIsMousePressed)
{
return;
}
elpRotater.Angle += 5;
UCRotatedAngleStr=Math.Round(elpRotater.Angle,2).ToString();
}));
}
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(UCRotatedCircle), new PropertyMetadata(0.0d));
public double UCElpHeight
{
get { return (double)GetValue(UCElpHeightProperty); }
set { SetValue(UCElpHeightProperty, value); }
}
// Using a DependencyProperty as the backing store for UCElpHeight. This enables animation, styling, binding, etc...
public static readonly DependencyProperty UCElpHeightProperty =
DependencyProperty.Register("UCElpHeight", typeof(double),
typeof(UCRotatedCircle), new PropertyMetadata(0.0d));
public bool UCIsMousePressed
{
get { return (bool)GetValue(UCIsMousePressedProperty); }
set { SetValue(UCIsMousePressedProperty, value); }
}
// Using a DependencyProperty as the backing store for UCIsMousePressed. This enables animation, styling, binding, etc...
public static readonly DependencyProperty UCIsMousePressedProperty =
DependencyProperty.Register("UCIsMousePressed", typeof(bool),
typeof(UCRotatedCircle), new PropertyMetadata(false));
public double UCCenterX
{
get { return (double)GetValue(UCCenterXProperty); }
set { SetValue(UCCenterXProperty, value); }
}
// Using a DependencyProperty as the backing store for UCCenterX. This enables animation, styling, binding, etc...
public static readonly DependencyProperty UCCenterXProperty =
DependencyProperty.Register("UCCenterX", typeof(double),
typeof(UCRotatedCircle), new PropertyMetadata(0.0d));
public double UCCenterY
{
get { return (double)GetValue(UCCenterYProperty); }
set { SetValue(UCCenterYProperty, value); }
}
// Using a DependencyProperty as the backing store for UCCenterY. This enables animation, styling, binding, etc...
public static readonly DependencyProperty UCCenterYProperty =
DependencyProperty.Register("UCCenterY", typeof(double),
typeof(UCRotatedCircle), new PropertyMetadata(0.0d));
public string UCRotatedAngleStr
{
get { return (string)GetValue(UCRotatedAngleStrProperty); }
set { SetValue(UCRotatedAngleStrProperty, value); }
}
// Using a DependencyProperty as the backing store for UCRotatedAngleStr. This enables animation, styling, binding, etc...
public static readonly DependencyProperty UCRotatedAngleStrProperty =
DependencyProperty.Register("UCRotatedAngleStr", typeof(string),
typeof(UCRotatedCircle), new PropertyMetadata(""));
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(UCRotatedCircle), new PropertyMetadata(GetDefaultImageSource()));
//D:\C\WpfApp203\WpfApp203\Images\1.jpg
private static ImageSource GetDefaultImageSource()
{
string imgUrl = @"../../../Images/1.jpg";
if (!File.Exists(imgUrl))
{
return null;
}
BitmapImage bmi = new BitmapImage();
bmi.BeginInit();
bmi.UriSource = new Uri(imgUrl, UriKind.RelativeOrAbsolute);
bmi.EndInit();
if (bmi.CanFreeze)
{
bmi.Freeze();
}
return bmi;
}
private Point prevPt;
private Point originPt;
private void Ellipse_MouseDown(object sender, MouseButtonEventArgs e)
{
if (e.ButtonState == MouseButtonState.Pressed)
{
elp.CaptureMouse();
prevPt = e.GetPosition(this);
UCIsMousePressed = true;
}
}
private void Ellipse_MouseMove(object sender, MouseEventArgs e)
{
if (e.LeftButton == MouseButtonState.Pressed)
{
Vector prevVec = Point.Subtract(prevPt, originPt);
Point newPt = e.GetPosition(this);
Vector newVec = Point.Subtract(newPt, originPt);
double deltaAngle = Vector.AngleBetween(prevVec, newVec);
elpRotater.Angle += deltaAngle;
prevPt = newPt;
UCRotatedAngleStr = Math.Round(elpRotater.Angle, 2).ToString();
}
}
private void Ellipse_MouseUp(object sender, MouseButtonEventArgs e)
{
if (e.LeftButton == MouseButtonState.Released)
{
elp.ReleaseMouseCapture();
UCIsMousePressed = false;
}
}
}
}
//window.xaml
<Window x:Class="WpfApp203.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:WpfApp203"
mc:Ignorable="d"
WindowState="Maximized"
Title="{Binding RotatedAngleStr,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"
Height="450" Width="800">
<Grid>
<local:UCRotatedCircle
UCRotatedAngleStr="{Binding DataContext.RotatedAngleStr,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged,
RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type Window}}}"/>
</Grid>
</Window>
//window.xaml.cs
using System.ComponentModel;
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.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace WpfApp203
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
var vm = new MainVM();
this.DataContext = vm;
}
}
public class MainVM : INotifyPropertyChanged
{
public MainVM()
{
}
public event PropertyChangedEventHandler? PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
var handler = PropertyChanged;
if(handler!=null)
{
handler?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
private string rotatedAngleStr;
public string RotatedAngleStr
{
get
{
return rotatedAngleStr;
}
set
{
if(value!=rotatedAngleStr)
{
rotatedAngleStr = value;
OnPropertyChanged(nameof(RotatedAngleStr));
}
}
}
}
}