WPF自制動畫 儀表盤控件 Gauge

public class ArcGauge : Control
    {
        public ArcGauge()
        {
            Width = 200;
            Height = 200;
            SetCurrentValue(ValueProperty, 0d);
            SetCurrentValue(MinValueProperty, 0d);
            SetCurrentValue(MaxValueProperty, 100d);
        }

        private void InitTick()
        {
            // 畫大刻度
            for (int i = 0; i < 9; i++)
            {
                Line line = new Line();
                line.X1 = 0;
                line.Y1 = 0;
                line.X2 = 0;
                line.Y2 = 12;
                line.Stroke = Brushes.White;
                line.StrokeThickness = 2;
                line.HorizontalAlignment = HorizontalAlignment.Center;
                line.RenderTransformOrigin = new Point(0.5, 0.5);
                line.RenderTransform = new RotateTransform() { Angle = -140 + i * 35 };
                bdGrid.Children.Add(line);
                DrawText();
            }

            // 畫小刻度
            for (int i = 0; i < 8; i++)
            {
                var start = -140 + 35 * i + 3.5;
                for (int j = 0; j < 9; j++)
                {
                    Line line = new Line();
                    line.X1 = 0;
                    line.Y1 = 0;
                    line.X2 = 0;
                    line.Y2 = 6;
                    line.Stroke = Brushes.White;
                    line.StrokeThickness = 1;
                    line.HorizontalAlignment = HorizontalAlignment.Center;
                    line.RenderTransformOrigin = new Point(0.5, 0.5);
                    line.RenderTransform = new RotateTransform() { Angle = start + j * 3.5 };
                    bdGrid.Children.Add(line);
                }
            }
        }

        List<TextBlock> textLabels = new List<TextBlock>();
        private void DrawText()
        {
            foreach (var item in textLabels)
            {
                bdGrid.Children.Remove(item);
            }
            textLabels.Clear();

            var per = MaxValue / 8;
            for (int i = 0; i < 9; i++)
            {
                TextBlock textBlock = new TextBlock();
                textBlock.Text = $"{MinValue + (per * i)}";
                textBlock.HorizontalAlignment = HorizontalAlignment.Center;
                textBlock.RenderTransformOrigin = new Point(0.5, 0.5);
                textBlock.RenderTransform = new RotateTransform() { Angle = -140 + i * 35 };
                textBlock.Margin = new Thickness(12);
                textBlock.Foreground = Brushes.White;
                bdGrid.Children.Add(textBlock);
                textLabels.Add(textBlock);
            }
        }

        static ArcGauge()
        {
            DefaultStyleKeyProperty.OverrideMetadata(typeof(ArcGauge), new FrameworkPropertyMetadata(typeof(ArcGauge)));
        }

        RotateTransform rotateTransform;
        Grid bdGrid;
        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();
            rotateTransform = GetTemplateChild("PointRotate") as RotateTransform;
            bdGrid = GetTemplateChild("bdGrid") as Grid;
            Refresh();
            InitTick();
        }
        
        [Category("值設定")]
        public double Value
        {
            get { return (double)GetValue(ValueProperty); }
            set { SetValue(ValueProperty, value); }
        }
        public static readonly DependencyProperty ValueProperty =
            DependencyProperty.Register("Value", typeof(double), typeof(ArcGauge), new PropertyMetadata(0d, OnValueChanged));

        private static void OnValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) => (d as ArcGauge)?.Refresh();

        [Category("值設定")]
        public double MinValue
        {
            get { return (double)GetValue(MinValueProperty); }
            set { SetValue(MinValueProperty, value); }
        }
        public static readonly DependencyProperty MinValueProperty =
            DependencyProperty.Register("MinValue", typeof(double), typeof(ArcGauge), new PropertyMetadata(0d, OnValueChanged));

        [Category("值設定")]
        public double MaxValue
        {
            get { return (double)GetValue(MaxValueProperty); }
            set { SetValue(MaxValueProperty, value); }
        }

        public static readonly DependencyProperty MaxValueProperty =
            DependencyProperty.Register("MaxValue", typeof(double), typeof(ArcGauge), new PropertyMetadata(0d, OnValueChanged));


        private void Refresh()
        {
            if (rotateTransform == null)
                return;
            DoubleAnimation da = new DoubleAnimation();
            da.Duration = new Duration(TimeSpan.FromMilliseconds(350));
            da.EasingFunction = new CubicEase() { EasingMode = EasingMode.EaseOut };

            if (Value > MaxValue)
            {
                rotateTransform.Angle = 140;
                da.To = 140;
            }
            else if (Value < MinValue)
            {
                rotateTransform.Angle = -140;
                da.To = -140;
            }
            else
            {
                var range = MaxValue - MinValue;
                var process = Value / range;
                var tAngle = process * 280 - 140;
                rotateTransform.Angle = tAngle;
                da.To = tAngle;
            }

            rotateTransform.BeginAnimation(RotateTransform.AngleProperty, da);
        }
    }

  

<Style TargetType="{x:Type ctrl:ArcGauge}">
        <Setter Property="Background" Value="#646464"/>
        <Setter Property="Foreground" Value="Black"/>
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type ctrl:ArcGauge}">
                    <Border Margin="10">
                        <Grid Width="{Binding RelativeSource={RelativeSource Self},Path=ActualHeight}">
                            <Ellipse Fill="#FF3B3B3B"/>
                            <Grid RenderTransformOrigin="0.5,0.5" Margin="2">
                                <Grid.RenderTransform>
                                    <TransformGroup>
                                        <RotateTransform Angle="{Binding Path=Angle,ElementName=PointRotate}"/>
                                    </TransformGroup>
                                </Grid.RenderTransform>
                                <Ellipse Width="16" Height="14" Fill="Orange" VerticalAlignment="Top" >
                                    <Ellipse.Effect>
                                        <BlurEffect Radius="12"/>
                                    </Ellipse.Effect>
                                </Ellipse>
                            </Grid>
                          
                            <Grid x:Name="bdGrid" Margin="12" UseLayoutRounding="True" ClipToBounds="True">
                                <Ellipse>
                                    <Ellipse.Fill>
                                        <RadialGradientBrush>
                                            <GradientStop Color="#4D000000"/>
                                        </RadialGradientBrush>
                                    </Ellipse.Fill>
                                </Ellipse>

                                <Grid>
                                    <Grid.ColumnDefinitions>
                                        <ColumnDefinition/>
                                        <ColumnDefinition Width="2*"/>
                                        <ColumnDefinition/>
                                    </Grid.ColumnDefinitions>
                                    <Grid.RowDefinitions>
                                        <RowDefinition/>
                                        <RowDefinition Height="2*"/>
                                        <RowDefinition/>
                                    </Grid.RowDefinitions>
                                    <Ellipse Stroke="#464646" StrokeThickness="1" Grid.Column="1" Grid.Row="1"/>
                                    <Ellipse Stroke="#959595" Margin="4" StrokeThickness="6" Grid.Column="1" Grid.Row="1"/>
                                    <Ellipse Stroke="#464646" Margin="14" StrokeThickness="1" Grid.Column="1" Grid.Row="1"/>
                                </Grid>

                                <Grid>
                                    <Grid.RowDefinitions>
                                        <RowDefinition/>
                                        <RowDefinition/>
                                    </Grid.RowDefinitions>
                                    <Path Data="M5,0 5,0 10,120 0,120z" Fill="#0FA9CE" Stretch="Uniform" Margin="0 30 0 0" RenderTransformOrigin="0.5,1" HorizontalAlignment="Center">
                                        <Path.RenderTransform>
                                            <TransformGroup>
                                                <RotateTransform x:Name="PointRotate"/>
                                            </TransformGroup>
                                        </Path.RenderTransform>
                                    </Path>
                                </Grid>


                                <Ellipse Width="28" Height="28" Fill="Black">
                                    <Ellipse.Effect>
                                        <DropShadowEffect Color="#0FA9CE" ShadowDepth="0" Direction="0" BlurRadius="16"/>
                                    </Ellipse.Effect>
                                </Ellipse>

                                <Border VerticalAlignment="Bottom" BorderBrush="#10ABD1" BorderThickness="2" Margin="0 0 0 12" Background="Black" Padding="8 2" HorizontalAlignment="Center">
                                    <TextBlock Text="{Binding Value,RelativeSource={RelativeSource Mode=TemplatedParent}}" FontSize="16" Width="30" TextAlignment="Center" Foreground="White" FontWeight="Bold"/>
                                </Border>
                            </Grid>
                        </Grid>
                    </Border>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

  

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章