WPF circle rotated around the center automatically and when mouse down paused while mouse up restored

//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));
                }
            }
        }
    }
}

 

 

 

 

 

 

 

 

 

posted @ 2025-04-13 15:55  FredGrit  阅读(8)  评论(0)    收藏  举报