ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

WPF-自定义控件-指针仪表(依赖属性)

2022-06-30 11:03:41  阅读:186  来源: 互联网

标签:控件 自定义 DependencyProperty int System using new WPF public


以下是学习笔记

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

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有