WPF usercontrol customize triangle rotated around bigger circle and invoked in window

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

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

posted @ 2025-03-30 19:08  FredGrit  阅读(9)  评论(0)    收藏  举报