WPF-自定义控件-指针仪表(依赖属性)
作者:互联网
以下是学习笔记
https://www.bilibili.com/video/BV1gq4y1D76d?p=50&spm_id_from=pageDriver&vd_source=3f21d2e208ef0bf2c49a9be7560735e5
效果:
指针动画的思路:用3点画一个水平方向的指针,旋转指针的角度来实现动画。
1,新建-WPF-用户控件(WPF)
【1.1】xaml
<UserControl x:Class="JasonWPFControls.Instrument" 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:JasonWPFControls" mc:Ignorable="d" d:DesignHeight="450" d:DesignWidth="800"> <Grid> <Ellipse Fill="{Binding PlateBackground,RelativeSource={RelativeSource AncestorType=UserControl,Mode=FindAncestor}}" Name="backEllipse"></Ellipse> <Canvas Name="mainCanvas" Width="{Binding Width,ElementName=backEllipse}" Height="{Binding Height,ElementName=backEllipse}"/> <Path Name="circle" Data="" Stroke="White" StrokeThickness="4" Width="{Binding Width,ElementName=backEllipse}" Height="{Binding Height,ElementName=backEllipse}" RenderTransformOrigin="0.5,0.5"> <Path.RenderTransform> <RotateTransform Angle="-45"/> </Path.RenderTransform> </Path> <!--指针--> <Path Name="pointer" Data="" Fill="{Binding PointerBrush,RelativeSource={RelativeSource AncestorType=UserControl,Mode=FindAncestor}}" StrokeThickness="4" Width="{Binding Width,ElementName=backEllipse}" Height="{Binding Height,ElementName=backEllipse}" RenderTransformOrigin="0.5,0.5"> <Path.RenderTransform> <!--实际上是改变这个角度达到动画的效果--> <RotateTransform Angle="0" x:Name="rtPointer"/> </Path.RenderTransform> </Path> <Border Width="20" Height="20" CornerRadius="10"> <Border.Background> <RadialGradientBrush> <GradientStop Color="White" Offset="0.583"/> <GradientStop Color="Gray" Offset="1"/> </RadialGradientBrush> </Border.Background> </Border> </Grid> </UserControl>
【1.2】
using System; using System.Collections.Generic; using System.ComponentModel; 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.Animation; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; namespace JasonWPFControls { /// <summary> /// Instrument.xaml 的交互逻辑 /// </summary> public partial class Instrument : UserControl { //依赖属性,依赖对象【很重要的理念】 public int Value { get { return (int)GetValue(ValueProperty); }//依赖对象才有这个方法this.GetValue,普通对象没有这个方法 set { SetValue(ValueProperty, value); }//依赖对象才有这个方法this.SetValue,普通对象没有这个方法 } //参数1,名称。参数2,什么类型的数据。参数3,这个属性属于谁 public static readonly DependencyProperty ValueProperty = DependencyProperty.Register("Value", typeof(int), typeof(Instrument), new PropertyMetadata(default(int), new PropertyChangedCallback(OnPropetyChanged))); //propdp快捷新建依赖属性 public int MinValue { get { return (int)GetValue(MinValueProperty); } set { SetValue(MinValueProperty, value); } } // Using a DependencyProperty as the backing store for MinValue. This enables animation, styling, binding, etc... public static readonly DependencyProperty MinValueProperty = DependencyProperty.Register("MinValue", typeof(int), typeof(Instrument), new PropertyMetadata(default(int), new PropertyChangedCallback(OnPropetyChanged))); public int MaxValue { get { return (int)GetValue(MaxValueProperty); } set { SetValue(MaxValueProperty, value); } } // Using a DependencyProperty as the backing store for MaxValue. This enables animation, styling, binding, etc... public static readonly DependencyProperty MaxValueProperty = DependencyProperty.Register("MaxValue", typeof(int), typeof(Instrument), new PropertyMetadata(default(int), new PropertyChangedCallback(OnPropetyChanged))); /// <summary> /// 分成几个大格 /// </summary> public int Interval { get { return (int)GetValue(IntervalProperty); } set { SetValue(IntervalProperty, value); } } // Using a DependencyProperty as the backing store for Interval. This enables animation, styling, binding, etc... public static readonly DependencyProperty IntervalProperty = DependencyProperty.Register("Interval", typeof(int), typeof(Instrument), new PropertyMetadata(default(int), new PropertyChangedCallback(OnPropetyChanged))); /// <summary> /// 字体大小 /// </summary> public int ScaleTextSize { get { return (int)GetValue(ScaleTextSizeProperty); } set { SetValue(ScaleTextSizeProperty, value); } } // Using a DependencyProperty as the backing store for ScaleTextSize. This enables animation, styling, binding, etc... public static readonly DependencyProperty ScaleTextSizeProperty = DependencyProperty.Register("ScaleTextSize", typeof(int), typeof(Instrument), new PropertyMetadata(default(int), new PropertyChangedCallback(OnPropetyChanged))); /// <summary> /// 字体和刻度的颜色 /// </summary> public Brush SacleBrush { get { return (Brush)GetValue(SacleBrushProperty); } set { SetValue(SacleBrushProperty, value); } } // Using a DependencyProperty as the backing store for SacleBrush. This enables animation, styling, binding, etc... public static readonly DependencyProperty SacleBrushProperty = DependencyProperty.Register("SacleBrush", typeof(Brush), typeof(Instrument), new PropertyMetadata(default(Brush), new PropertyChangedCallback(OnPropetyChanged))); //值发生变化,触发这个委托 public static void OnPropetyChanged(DependencyObject d,DependencyPropertyChangedEventArgs e) { (d as Instrument).Refresh(); } /// <summary> /// 底盘的背景颜色(这个属性不用触发委托方法了,在页面上绑定就可以) /// </summary> public Brush PlateBackground { get { return (Brush)GetValue(PlateBackgroundProperty); } set { SetValue(PlateBackgroundProperty, value); } } // Using a DependencyProperty as the backing store for PlateBackground. This enables animation, styling, binding, etc... public static readonly DependencyProperty PlateBackgroundProperty = DependencyProperty.Register("PlateBackground", typeof(Brush), typeof(Instrument), new PropertyMetadata(default(Brush))); /// <summary> /// 指针颜色 /// </summary> public Brush PointerBrush { get { return (Brush)GetValue(PointerBrushProperty); } set { SetValue(PointerBrushProperty, value); } } // Using a DependencyProperty as the backing store for PointerBrush. This enables animation, styling, binding, etc... public static readonly DependencyProperty PointerBrushProperty = DependencyProperty.Register("PointerBrush", typeof(Brush), typeof(Instrument), new PropertyMetadata(default(Brush))); public Instrument() { InitializeComponent(); this.SizeChanged += Instrument_SizeChanged; } private void Instrument_SizeChanged(object sender, SizeChangedEventArgs e) { //设置背景高度和宽度一直,呈现圆形 double minSize = Math.Min(this.RenderSize.Width, this.RenderSize.Height); this.backEllipse.Width = minSize; this.backEllipse.Height = minSize; } private void Refresh() { double radius = this.backEllipse.Width / 2; if(double.IsNaN(radius))return; //每次绘制之前都清空 this.mainCanvas.Children.Clear(); //double min = 0, max = 100; //几个区,几个大格 //double scaleAreaCount = 10; //一共有多少步 double step = 270.0 / (this.MaxValue - this.MinValue); for (int i = 0; i < this.MaxValue - this.MinValue; i++) { Line lineScale = new Line(); lineScale.X1 = radius - (radius - 13) * Math.Cos((i * step - 45) * Math.PI / 180); lineScale.Y1 = radius - (radius - 13) * Math.Sin((i * step - 45) * Math.PI / 180); lineScale.X2 = radius - (radius - 8) * Math.Cos((i * step - 45) * Math.PI / 180); lineScale.Y2 = radius - (radius - 8) * Math.Sin((i * step - 45) * Math.PI / 180); //线的颜色 lineScale.Stroke = this.SacleBrush; //线的宽度 lineScale.StrokeThickness = 2; this.mainCanvas.Children.Add(lineScale); } //画大刻度 step = 270.0 / this.Interval; int scaleText = (int)this.MinValue; for (int i = 0; i <= this.Interval; i++) { Line lineScale = new Line(); lineScale.X1 = radius - (radius - 20) * Math.Cos((i * step - 45) * Math.PI / 180); lineScale.Y1 = radius - (radius - 20) * Math.Sin((i * step - 45) * Math.PI / 180); lineScale.X2 = radius - (radius - 8) * Math.Cos((i * step - 45) * Math.PI / 180); lineScale.Y2 = radius - (radius - 8) * Math.Sin((i * step - 45) * Math.PI / 180); //线的颜色 lineScale.Stroke = this.SacleBrush; //线的宽度 lineScale.StrokeThickness = 2; this.mainCanvas.Children.Add(lineScale); //刻度文本 TextBlock textScale = new TextBlock(); textScale.Width = 34; textScale.TextAlignment = TextAlignment.Center; textScale.FontSize = this.ScaleTextSize; textScale.Text = (scaleText + (this.MaxValue - this.MinValue) / this.Interval * i).ToString(); Canvas.SetLeft(textScale, radius - (radius - 30) * Math.Cos((i * step - 45) * Math.PI / 180)-17); Canvas.SetTop(textScale, radius - (radius - 30) * Math.Sin((i * step - 45) * Math.PI / 180)-10); textScale.Foreground =this.SacleBrush; this.mainCanvas.Children.Add(textScale); } //画圆弧 string sData = "M{0} {1} A{0} {0} 0 1 1 {1} {2}"; sData = string.Format(sData, radius / 2,radius,radius*1.5); var convert = TypeDescriptor.GetConverter(typeof(Geometry)); this.circle.Data = (Geometry)convert.ConvertFrom(sData); //改变指针旋转的角度达到动画效果 step = 270.0 / (this.MaxValue - this.MinValue); //this.rtPointer.Angle = this.Value * step - 45;//到这步,更改页面“value”的值,指针会瞬间调到指定的位置 //动画过度(缓慢转动的效果) DoubleAnimation da = new DoubleAnimation((this.Value-this.MinValue) * step - 45, new Duration(TimeSpan.FromMilliseconds(200))); this.rtPointer.BeginAnimation(RotateTransform.AngleProperty, da); //画指针(三角形,三点的坐标就可以画出来) sData = "M{0} {1} ,{1} {2} , {1} {3}"; sData = string.Format(sData,radius*0.3,radius, radius-5, radius+5); this.pointer.Data = (Geometry)convert.ConvertFrom(sData); } } }
2,动画刷新的代码
public class FirstPageViewModel:NotifyBase { private int _instrumentValue=0; public int InstrumentValue { get { return _instrumentValue; } set { _instrumentValue = value; this.DoNotify(); } } Random random=new Random(); /// <summary> /// 线程的开关 /// </summary> private bool taskSwitch=true; /// <summary> /// 开启的线程集合 /// </summary> List<Task> tasklList=new List<Task>(); public FirstPageViewModel() { this.RefreshInstrumentValue(); } void RefreshInstrumentValue() { var task = Task.Factory.StartNew(new Action(async () => { while (taskSwitch) { //0和100是最大值和最小值,这里为了演示效果是写死的,后期可用变量替代。 InstrumentValue = random.Next(Math.Max(this.InstrumentValue - 5,0), Math.Min( this.InstrumentValue + 5,100)); //停顿1秒刷新 await Task.Delay(1000); } })); tasklList.Add(task); } public void Dispose() { try { taskSwitch = false; //等待所有线程结束 Task.WaitAll(this.tasklList.ToArray()); } catch (Exception e) { } } }
3,调用
【3.1】引入命名控件
xmlns:jasonc="clr-namespace:JasonWPFControls;assembly=JasonWPFControls"
【3.2】使用
<jasonc:Instrument Margin="0,20,0,40" Value="{Binding InstrumentValue}" MinValue="0" MaxValue="100" Interval="9" PlateBackground="Orange" ScaleTextSize="14" SacleBrush="White" PointerBrush="Green"/>
标签:控件,自定义,DependencyProperty,int,System,using,new,WPF,public 来源: https://www.cnblogs.com/baozi789654/p/16426045.html