WPF 仪表盘

根据网上的资源更新一个仪表盘。

去除依赖Arc,根据ArcSegment画圆弧。不多说,上效果。

还有些地方没有完善,有需要的小伙伴自行下载完善。

地址:DashboardDemo 

Dashboard.cs代码

using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;

namespace AppWpfControls;

/// <summary>
/// 仪表盘=》Dashboard
/// </summary>
public class Dashboard : Control
{
    //public const string ArcName = "arckd";
    //public const string ValueTxtName = "valueTxt";
    /// <summary>
    /// 
    /// </summary>
    public const string CanvasName = "canvasPlate";
    /// <summary>
    /// 
    /// </summary>
    public const string ZZRotateName = "rtPointer";

    //private static double StartAngle = -135.5;

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

    //Arc arckd;
    //TextBlock valueTxt;
    Canvas canvasPlate;
    RotateTransform rtPointer;
    /// <summary>
    /// 
    /// </summary>
    public override void OnApplyTemplate()
    {
        base.OnApplyTemplate();
        //arckd ??= GetTemplateChild(ArcName) as Arc;
        //valueTxt ??= GetTemplateChild(ValueTxtName) as TextBlock;
        canvasPlate ??= GetTemplateChild(CanvasName) as Canvas;
        rtPointer ??= GetTemplateChild(ZZRotateName) as RotateTransform;
        DrawScale();
        DrawAngle();
        //arckd.EndAngle = StartAngle + (-StartAngle * 2d) * ((double)Value / Maximum);
        //valueTxt.Text = Value.ToString();
    }

    #region 值、最大值、最小值

    /// <summary>
    /// 值
    /// </summary>
    public double Value
    {
        get { return (double)GetValue(ValueProperty); }
        set
        {
            SetValue(ValueProperty, value);
        }
    }
    /// <summary>
    /// 值
    /// </summary>
    public static readonly DependencyProperty ValueProperty =
        DependencyProperty.Register("Value", typeof(double), typeof(Dashboard),
            new PropertyMetadata(default(double), new PropertyChangedCallback(OnValuePropertyChanged)));

    /// <summary>
    /// 值单位
    /// </summary>
    public string ValueUnit
    {
        get { return (string)GetValue(ValueUnitProperty); }
        set
        {
            SetValue(ValueUnitProperty, value);
        }
    }
    /// <summary>
    /// 值单位
    /// </summary>
    public static readonly DependencyProperty ValueUnitProperty =
        DependencyProperty.Register("ValueUnit", typeof(string), typeof(Dashboard),
            new PropertyMetadata(default(string)));


    /// <summary>
    /// 最小值
    /// </summary>
    public double Minimum
    {
        get { return (double)GetValue(MinimumProperty); }
        set { SetValue(MinimumProperty, value); }
    }
    /// <summary>
    /// 最小值
    /// </summary>
    public static readonly DependencyProperty MinimumProperty =
        DependencyProperty.Register("Minimum", typeof(double), typeof(Dashboard),
            new PropertyMetadata(double.NaN, new PropertyChangedCallback(OnPropertyChanged)));

    /// <summary>
    /// 最大值
    /// </summary>
    public double Maximum
    {
        get { return (double)GetValue(MaximumProperty); }
        set { SetValue(MaximumProperty, value); }
    }
    /// <summary>
    /// 最大值
    /// </summary>
    public static readonly DependencyProperty MaximumProperty =
        DependencyProperty.Register("Maximum", typeof(double), typeof(Dashboard),
            new PropertyMetadata(double.NaN, new PropertyChangedCallback(OnPropertyChanged)));

    #endregion 值、最大值、最小值

    /// <summary>
    /// 框背景颜色
    /// </summary>
    public Brush PlateBackground
    {
        get { return (Brush)GetValue(PlateBackgroundProperty); }
        set { SetValue(PlateBackgroundProperty, value); }
    }
    /// <summary>
    /// 框背景颜色
    /// </summary>
    public static readonly DependencyProperty PlateBackgroundProperty =
        DependencyProperty.Register("PlateBackground", typeof(Brush), typeof(Dashboard), null);

    /// <summary>
    /// 框边框颜色
    /// </summary>
    public Brush PlateBorderBrush
    {
        get { return (Brush)GetValue(PlateBorderBrushProperty); }
        set { SetValue(PlateBorderBrushProperty, value); }
    }
    /// <summary>
    /// 框边框颜色
    /// </summary>
    public static readonly DependencyProperty PlateBorderBrushProperty =
        DependencyProperty.Register("PlateBorderBrush", typeof(Brush), typeof(Dashboard), null);

    /// <summary>
    /// 框边框大小
    /// </summary>
    public Thickness PlateBorderThickness
    {
        get { return (Thickness)GetValue(PlateBorderThicknessProperty); }
        set { SetValue(PlateBorderThicknessProperty, value); }
    }
    /// <summary>
    /// 框边框大小
    /// </summary>
    public static readonly DependencyProperty PlateBorderThicknessProperty =
        DependencyProperty.Register("PlateBorderThickness", typeof(Thickness), typeof(Dashboard), null);

    /// <summary>
    /// 
    /// </summary>
    /// <param name="d"></param>
    /// <param name="e"></param>
    public static void OnPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        (d as Dashboard).DrawScale();
    }
    /// <summary>
    /// 
    /// </summary>
    /// <param name="d"></param>
    /// <param name="e"></param>
    public static void OnValuePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        Dashboard dashboard = d as Dashboard;
        dashboard.DrawAngle();
        //if (dashboard.arckd == null)
        //    return;
        //dashboard.arckd.EndAngle = StartAngle + (-StartAngle * 2d) * ((double)dashboard.Value / dashboard.Maximum);
        //dashboard.valueTxt.Text = dashboard.Value.ToString();
    }

    /// <summary>
    /// 画表盘的刻度
    /// </summary>
    private void DrawScale()
    {
        if (this.canvasPlate == null) return;
        this.canvasPlate.Children.Clear();

        for (double i = 0; i <= this.Maximum - this.Minimum; i++)
        {
            //添加刻度线
            Line lineScale = new();
            //画笔颜色
            Color color = (Color)ColorConverter.ConvertFromString("#6c6974");
           
            if (i % 10 == 0)
            {
                //注意Math.Cos和Math.Sin的参数是弧度,记得将角度转为弧度制
                lineScale.X1 = 200 - 170 * Math.Cos(i * (270 / (this.Maximum - this.Minimum)) * Math.PI / 180);
                lineScale.Y1 = 200 - 170 * Math.Sin(i * (270 / (this.Maximum - this.Minimum)) * Math.PI / 180);
                //lineScale.Stroke = new SolidColorBrush(Colors.White);
                lineScale.Stroke = new SolidColorBrush(color);
                lineScale.StrokeThickness = 2;

                //添加刻度值
                TextBlock txtScale = new()
                {
                    Text = (i + this.Minimum).ToString(),
                    Width = 34,
                    TextAlignment = TextAlignment.Center,
                    //Foreground = new SolidColorBrush(Colors.White);
                    //Color color = (Color)ColorConverter.ConvertFromString("#6c6974");
                    Foreground = new SolidColorBrush(color),
                    RenderTransform = new RotateTransform() { Angle = 45, CenterX = 17, CenterY = 8 },
                    FontSize = 18
                };

                Canvas.SetLeft(txtScale, 200 - 155 * Math.Cos(i * (270 / (this.Maximum - this.Minimum)) * Math.PI / 180) - 17);
                Canvas.SetTop(txtScale, 200 - 155 * Math.Sin(i * (270 / (this.Maximum - this.Minimum)) * Math.PI / 180) - 10);

                this.canvasPlate.Children.Add(txtScale);
            }
            else
            {
                lineScale.X1 = 200 - 180 * Math.Cos(i * (270 / (this.Maximum - this.Minimum)) * Math.PI / 180);
                lineScale.Y1 = 200 - 180 * Math.Sin(i * (270 / (this.Maximum - this.Minimum)) * Math.PI / 180);
                //lineScale.Stroke = new SolidColorBrush(Colors.White);
                lineScale.Stroke = new SolidColorBrush(color);
                lineScale.StrokeThickness = 1;
                lineScale.Opacity = 0.5;
            }

            lineScale.X2 = 200 - 190 * Math.Cos(i * (270 / (this.Maximum - this.Minimum)) * Math.PI / 180);
            lineScale.Y2 = 200 - 190 * Math.Sin(i * (270 / (this.Maximum - this.Minimum)) * Math.PI / 180);
            
            this.canvasPlate.Children.Add(lineScale);
        }
    }

    private void DrawAngle()
    {
        if (rtPointer == null)
            return;
        double step = 270.0 / (this.Maximum - this.Minimum);
        double reject = 0;
        if (this.Minimum <0)
        {
            reject = step * (-this.Minimum);
        }
        else if(this.Minimum >= 0)
        {
            reject = step * (-this.Minimum);
        }

        DoubleAnimation da = new(this.Value * step+ reject - 45, new Duration(TimeSpan.FromMilliseconds(200)));
        this.rtPointer.BeginAnimation(RotateTransform.AngleProperty, da);
    }
}

Xaml样式

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                     xmlns:control="clr-namespace:AppWpfControls">
    
    <Style TargetType="{x:Type control:Dashboard}">
        <Setter Property="Value" Value="0"/>
        <Setter Property="Maximum" Value="120"/>
        <Setter Property="Minimum" Value="0"/>
        <Setter Property="PlateBackground" Value="Transparent"/>
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type control:Dashboard}">
                    <Grid >
                        <Viewbox>
                            <Border CornerRadius="220" ClipToBounds="True" 
                                BorderBrush="{TemplateBinding PlateBorderBrush}" 
                                BorderThickness="{TemplateBinding PlateBorderThickness}" 
                                Background="{TemplateBinding PlateBackground}">
                                <Grid>
                                    <!--  Start 刻度盘  -->
                                    <Canvas Name="canvasPlate" Width="400" Height="400"  Background="Transparent" Margin="14">
                                        <Canvas.RenderTransform>
                                            <RotateTransform Angle="-45" CenterX="200" CenterY="200"/>
                                        </Canvas.RenderTransform>
                                    </Canvas>
                                    <!-- End 刻度盘  -->

                                    <!--  Start 刻度盘当前值对应的圆弧  -->
                                    <Path Stroke="#45246B" Opacity="0.5" 
                                         StrokeThickness="20">
                                            <!--StrokeStartLineCap="Round" 
                                            StrokeEndLineCap="Round"-->
                                        <Path.Data>
                                            <PathGeometry>
                                                <PathFigure IsClosed="False" StartPoint="73,358">
                                                    <ArcSegment IsLargeArc="True"
                                                        Point="355,358"
                                                        Size="201,201" 
                                                        SweepDirection="Clockwise" />
                                                </PathFigure>
                                            </PathGeometry>
                                        </Path.Data>
                                    </Path>
                                    <!-- End 刻度盘当前值对应的圆弧  -->

                                    <!-- Start 指针   -->
                                    <!--<Path Name="pointer" Data="M0,195 0,205 15,200 0,195" RenderTransformOrigin="0.5,0.5" Margin="14">-->
                                    <Path Name="pointer" Data="M200 205,40 200,200 195,200 205" RenderTransformOrigin="0.5,0.5" Margin="14">
                                        <Path.Fill>
                                            <RadialGradientBrush RadiusX="1" RadiusY="1">
                                                <GradientStop Color="#00C9FF" Offset="0.3"/>
                                                <GradientStop Color="#CE97EF" Offset="0.4"/>
                                                <GradientStop Color="#091851" Offset="1"/>
                                            </RadialGradientBrush>
                                        </Path.Fill>
                                        
                                        <Path.RenderTransform>
                                            <RotateTransform x:Name="rtPointer" Angle="-45"/>
                                        </Path.RenderTransform>
                                    </Path>
                                    <!-- End 指针  -->

                                    <!-- Start 圆心  -->
                                    <Border Width="50" Height="50" CornerRadius="40"  Margin="14">
                                        <Border.Background>
                                            <RadialGradientBrush RadiusX="1" RadiusY="1">
                                                <GradientStop Color="#45246B" Offset="0.2"/>
                                                <GradientStop Color="#2E1C5C" Offset="0.3"/>
                                                <GradientStop Color="#253192" Offset="0.5"/>
                                            </RadialGradientBrush>
                                        </Border.Background>
                                        <!--<StackPanel Orientation="Vertical" VerticalAlignment="Center" HorizontalAlignment="Center">
                                            <TextBlock x:Name="valueTxt" FontSize="30" VerticalAlignment="Center" HorizontalAlignment="Center"/>
                                            <TextBlock Text="km/h" FontSize="12" VerticalAlignment="Center" HorizontalAlignment="Center"/>
                                        </StackPanel>-->
                                    </Border>
                                    <!-- End 圆心  -->

                                    <Grid Width="150" Height="60"  Margin="0,0,0,100" VerticalAlignment="Bottom" HorizontalAlignment="Center">
                                        <StackPanel  HorizontalAlignment="Center" Orientation="Vertical">
                                            <!--<TextBlock Margin="0,4" Text="DOWNLOAD" />-->
                                            <TextBlock Margin="0,4" HorizontalAlignment="Center" FontSize="26" Opacity="0.8"
                                                            Text="{Binding Value,RelativeSource={RelativeSource AncestorType=control:Dashboard}}" Foreground="White" />
                                            <TextBlock Margin="0,4" HorizontalAlignment="Center" 
                                                       Foreground="#6c6974" 
                                                       Text="{Binding ValueUnit,RelativeSource={RelativeSource AncestorType=control:Dashboard}}" />
                                            <!--<Border Margin="0,10,0,0" BorderBrush="#929093" BorderThickness="1" Height="25" CornerRadius="12" Padding="0,3">
                                                <TextBlock HorizontalAlignment="Center" Text="RETRY" VerticalAlignment="Center" />
                                            </Border>-->
                                        </StackPanel>
                                        
                                    </Grid>
                                </Grid>
                            </Border>
                        </Viewbox>
                    </Grid>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

    <Style x:Key="DashboardArc" TargetType="{x:Type control:Dashboard}">
        <Setter Property="Value" Value="0"/>
        <Setter Property="Maximum" Value="120"/>
        <Setter Property="Minimum" Value="0"/>
        <Setter Property="PlateBackground" Value="Transparent"/>
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type control:Dashboard}">
                    <Grid >
                        <Viewbox>
                            <Border CornerRadius="220" ClipToBounds="True" 
                            BorderBrush="{TemplateBinding PlateBorderBrush}" 
                            BorderThickness="{TemplateBinding PlateBorderThickness}" 
                            Background="{TemplateBinding PlateBackground}">
                                <Grid>
                                    <!--  Start 刻度盘  -->
                                    <Canvas Name="canvasPlate" Width="400" Height="400"  Background="Transparent" Margin="14">
                                        <Canvas.RenderTransform>
                                            <RotateTransform Angle="-45" CenterX="200" CenterY="200"/>
                                        </Canvas.RenderTransform>
                                    </Canvas>
                                    <!-- End 刻度盘  -->

                                    <!--  Start 刻度盘当前值对应的圆弧  -->
                                    <Path Stroke="#45246B" Opacity="0.5" 
                                     StrokeThickness="20">
                                        <!--StrokeStartLineCap="Round" 
                                        StrokeEndLineCap="Round"-->
                                        <Path.Data>
                                            <PathGeometry>
                                                <PathFigure IsClosed="False" StartPoint="73,358">
                                                    <ArcSegment IsLargeArc="True"
                                                    Point="355,358"
                                                    Size="201,201" 
                                                    SweepDirection="Clockwise" />
                                                </PathFigure>
                                            </PathGeometry>
                                        </Path.Data>
                                    </Path>
                                    <!-- End 刻度盘当前值对应的圆弧  -->

                                    <!-- Start 指针   -->
                                    <!--<Path Name="pointer" Data="M200 205,40 200,200 195,200 205" RenderTransformOrigin="0.5,0.5" Margin="14">-->
                                    <Path Name="pointer" Data="M-7,194 -8,197 -9,200 -8,203 -7,206 15,200 -7,194" RenderTransformOrigin="0.5,0.5" Margin="14">
                                        <Path.Fill>
                                            <RadialGradientBrush RadiusX="1" RadiusY="1">
                                                <GradientStop Color="#00C9FF" Offset="0.3"/>
                                                <GradientStop Color="#CE97EF" Offset="0.4"/>
                                                <GradientStop Color="#091851" Offset="1"/>
                                            </RadialGradientBrush>
                                        </Path.Fill>

                                        <Path.RenderTransform>
                                            <RotateTransform x:Name="rtPointer" Angle="-45"/>
                                        </Path.RenderTransform>
                                    </Path>
                                    <!-- End 指针  -->

                                    <Grid Width="150" Height="130"   VerticalAlignment="Center" HorizontalAlignment="Center">
                                        <StackPanel  HorizontalAlignment="Center" Orientation="Vertical">
                                            <TextBlock Margin="0,4" Text="DOWNLOAD" FontSize="16" Foreground="#6c6974" />
                                            <TextBlock Margin="0,4" HorizontalAlignment="Center" FontSize="26" Opacity="0.6"
                                                        Text="{Binding Value,RelativeSource={RelativeSource AncestorType=control:Dashboard}}" Foreground="White" />
                                            <TextBlock Margin="0,4" HorizontalAlignment="Center" 
                                                       Foreground="#6c6974" 
                                                       Text="{Binding ValueUnit,RelativeSource={RelativeSource AncestorType=control:Dashboard}}" />
                                            <Border Margin="0,10,0,0" BorderBrush="#6c6974" BorderThickness="1" Height="25" CornerRadius="12" Padding="0,3">
                                                <TextBlock HorizontalAlignment="Center" Foreground="#6c6974" Text="RETRY" VerticalAlignment="Center" />
                                            </Border>
                                        </StackPanel>

                                    </Grid>
                                </Grid>
                            </Border>
                        </Viewbox>
                    </Grid>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</ResourceDictionary>

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 在.NET平台上,有几个开源仪表盘项目可用于WPF版本的开发。 首先,有一个流行的开源仪表盘项目叫做"LiveCharts"。它是一个跨平台的图表和仪表盘库,支持WPF、WinForms和Xamarin等技术。LiveCharts提供了丰富的图表类型和自定义选项,可以方便地创建出漂亮而有用的仪表盘。 另一个开源仪表盘项目是"DyamicDashboard"。它是一个基于WPF的可自定义的仪表盘框架,可以提供各种仪表和控件,如计时器、进度条、图表等,用于构建定制化的仪表盘应用。 此外,还有一个名为"MahApps.Metro"的开源项目,虽然它不是一个专门为仪表盘设计的库,但它提供了一套漂亮的WPF样式和控件,可以用于创建现代化和有吸引力的仪表盘界面。 最后,不仅局限于.NET平台,还有其他开源仪表盘项目也可以用于WPF版本的开发。例如,JavaScript语言的"Dashing"仪表盘项目非常流行,可以用于构建跨平台的多功能仪表盘。 综上所述,有多个开源仪表盘项目可用于在.NET平台上开发WPF版本的应用程序,这些项目都提供了丰富的功能和选项,可以帮助开发者轻松构建出仪表盘应用。 ### 回答2: 在.NET平台上,WPF(Windows Presentation Foundation)是一个强大且开源的UI框架,用于构建用户友好的Windows应用程序。在WPF框架中,开发人员可以通过XAML(可扩展应用程序标记语言)和C#等编程语言创建仪表盘应用。 开源的WPF仪表盘库有很多选择。其中一种常用的是MahApps.Metro库,它是一个流行的开源库,提供了一组现代化的控件和主题样式,可以帮助开发人员快速构建漂亮且功能强大的仪表盘应用。MahApps.Metro库不仅支持WPF平台,还支持其他.NET平台,如UWP(通用Windows平台)和WinForms(Windows窗体应用程序)等。 使用MahApps.Metro库,开发人员可以轻松创建出具有现代化外观和触摸友好的仪表盘应用。该库提供了许多预定义的控件和布局,可以直接使用或者进行个性化定制。开发人员可以利用这些控件和布局创建出仪表盘中常见的图表、指示器、仪表等元素,以实现丰富的数据可视化效果。 同时,MahApps.Metro库还提供了丰富的主题样式,可以根据应用的需求进行定制,使仪表盘应用更加吸引人。开发人员可以自定义颜色、字体和其他视觉元素,以满足用户界面设计的要求。 总之,.NET平台的WPF版本提供了许多开源的仪表盘库供开发人员使用。MahApps.Metro库是其中一个常见的选择,它通过提供现代化的控件和主题样式,帮助开发人员轻松构建美观且功能丰富的仪表盘应用。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值