• 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"/>
    

     

     

  • 相关阅读:
    java web 自定义错误页面 完整jsp错误页面代码(同时写错误日志) error.jsp
    linux ps命令详解
    Cocos2d-x MultipleTouch & CCControllButton's confusion
    android 处理图片之--bitmap处理
    JBoss 目录结构
    如何将Android默认的Camra程序导入到eclipse中
    SGU107——987654321 problem
    解析Systemtap
    csdn 刷分小工具(刷了1000多被封了,慎用)
    OpenCV——基本图形绘制(椭圆、圆、多边形、直线、矩形)
  • 原文地址:https://www.cnblogs.com/baozi789654/p/16426045.html
Copyright © 2020-2023  润新知