it编程 > App开发 > Windows Phone

WPF实现自绘仪表盘Gauge

183人参与 2025-01-01 Windows Phone

自绘仪表盘 gauge

框架支持.net4 至 .net8;

visual studio 2022;

wpfdevelopers 1.1.0.3-preview 版本已正式发布!

该版本为预览版,欢迎各位开发者下载并体验。

来自 issue

逻辑实现

gauge 继承 rangebase,支持设置最大值、最小值以及当前值,并且可以显示一个动态的百分比值、标题、刻度。

1. 新增 gauge.cs

依赖属性

public string title
{
    get { return (string)getvalue(titleproperty); }
    set { setvalue(titleproperty, value); }
}
 public string valueformat
 {
     get { return (string)getvalue(valueformatproperty); }
     set { setvalue(valueformatproperty, value); }
 }
 public double thickness
 {
     get { return (double)getvalue(thicknessproperty); }
     set { setvalue(thicknessproperty, value); }
 }

rangebase 依赖属性

public gauge()
{
    setvalue(valueproperty, 0.0);
    setvalue(minimumproperty, 0.0);
    setvalue(maximumproperty, 100.0);
}

2. 重写 onrender 方法绘制控件

背景:使用 ellipse 作为仪表盘的背景,背景色通过 background 属性设置,默认为 #293950

指针:根据当前值(value),计算出指针的角度,并使用 drawline 绘制红色指针。

外边框:使用渐变颜色绘制仪表盘的外边框。

刻度和标签:绘制了从最小值到最大值的刻度线,并在每个刻度绘制了对应的刻度值。

当前值显示:根据 valueformat 属性格式化显示当前值,并显示在仪表盘的底部位置。

protected override void onrender(drawingcontext drawingcontext)
{
    base.onrender(drawingcontext);
    if (background == null)
        background = new solidcolorbrush((color)colorconverter.convertfromstring("#293950"));
    var width = actualwidth;
    var height = actualheight;
    var radius = math.min(width, height) / 2;
    drawingcontext.drawellipse(background, new pen(background, thickness), new point(width / 2, height / 2), radius, radius);
    var normalizedvalue = (value - minimum) / (maximum - minimum);
    var mappedangle = -220 + normalizedvalue * 260;
    var angleinradians = mappedangle * math.pi / 180;
    var pointerlength = radius * 0.7;
    var pointerx = width / 2 + pointerlength * math.cos(angleinradians);
    var pointery = height / 2 + pointerlength * math.sin(angleinradians);
    drawingcontext.drawline(new pen(brushes.red, 2), new point(width / 2, height / 2), new point(pointerx, pointery));
    drawingcontext.drawellipse(brushes.white, new pen(brushes.red, 2), new point(width / 2, height / 2), width / 20, width / 20);
    var pathgeometry = new pathgeometry();
    var startangle = -220;
    angleinradians = startangle * math.pi / 180;
    var startx = width / 2 + radius * math.cos(angleinradians);
    var starty = height / 2 + radius * math.sin(angleinradians);

    var pathfigure = new pathfigure()
    {
        startpoint = new point(startx, starty),
    };

    var endangle = 40;
    angleinradians = endangle * math.pi / 180;
    var endx = width / 2 + radius * math.cos(angleinradians);
    var endy = height / 2 + radius * math.sin(angleinradians);

    var islargearc = (endangle - startangle > 180);
    var arcsegment = new arcsegment()
    {
        point = new point(endx, endy),
        size = new size(radius, radius),
        rotationangle = 0,
        sweepdirection = sweepdirection.clockwise,
        islargearc = islargearc,
    };

    pathfigure.segments.add(arcsegment);
    pathgeometry.figures.add(pathfigure);
    if (borderbrush == null)
    {
        var gradientbrush = new lineargradientbrush
        {
            startpoint = new point(0, 0),
            endpoint = new point(1, 0)
        };
        gradientbrush.gradientstops.add(new gradientstop((color)colorconverter.convertfromstring("#37d2c2"), 0.0));
        gradientbrush.gradientstops.add(new gradientstop((color)colorconverter.convertfromstring("#5ad2b2"), 0.01));
        gradientbrush.gradientstops.add(new gradientstop((color)colorconverter.convertfromstring("#b77d29"), 0.49));
        gradientbrush.gradientstops.add(new gradientstop(colors.red, 1.0));
        gradientbrush.freeze();
        borderbrush = gradientbrush;
    }
    drawingcontext.drawgeometry(null, new pen(borderbrush, thickness), pathgeometry);
    var ticklength = radius * 0.1; 
    var step = (maximum - minimum) / 10;
    for (int i = 0; i <= 10; i++)
    {
        var angle = startangle + (i * (endangle - startangle) / 10);
        var tickstartx = width / 2 + (radius - ticklength) * math.cos(angle * math.pi / 180);
        var tickstarty = height / 2 + (radius - ticklength) * math.sin(angle * math.pi / 180);
        var tickendx = width / 2 + (radius + thickness / 2) * math.cos(angle * math.pi / 180);
        var tickendy = height / 2 + (radius + thickness / 2) * math.sin(angle * math.pi / 180);
        drawingcontext.drawline(new pen(brushes.white, 2), new point(tickstartx, tickstarty), new point(tickendx, tickendy));

        var labelvalue = minimum + step * i;
        var formattedtext = drawingcontexthelper.getformattedtext(labelvalue.tostring(),brushes.white, flowdirection.lefttoright,fontsize);

        var labelradius = radius - ticklength * 2;
        var labelx = width / 2 + labelradius * math.cos(angle * math.pi / 180) - formattedtext.width / 2;
        var labely = height / 2 + labelradius * math.sin(angle * math.pi / 180) - formattedtext.height / 2;
        drawingcontext.drawtext(formattedtext, new point(labelx, labely));
    }
    var formattedvalue = "{0:0}%";
    try
    {
        formattedvalue = string.format(valueformat, value);
    }
    catch (formatexception ex)
    {
        throw new invalidoperationexception("formatting failed ", ex);
    }
    var currentvaluetext = drawingcontexthelper.getformattedtext(formattedvalue, brushes.white, flowdirection.lefttoright, fontsize * 2);
    var valuex = width / 2 - currentvaluetext.width / 2;
    var valuey = height / 2 + radius * 0.4; 
    drawingcontext.drawtext(currentvaluetext, new point(valuex, valuey));
    var titlevalue = drawingcontexthelper.getformattedtext(title, brushes.white, flowdirection.lefttoright, fontsize);
    valuex = width / 2 - titlevalue.width / 2;
    valuey = height / 2 + radius * 0.8;
    drawingcontext.drawtext(titlevalue, new point(valuex, valuey));
}

xaml 示例

示例引入 wpfdevelopers 1.1.0.3-preview 的 nuget 正式包

<wrappanel horizontalalignment="center" verticalalignment="center">
    <stackpanel verticalalignment="bottom">
        <wd:gauge
            title="min"
            width="100"
            height="100"
            margin="10"
            horizontalalignment="center"
            verticalalignment="center"
            background="black"
            borderbrush="red"
            fontsize="8"
            maximum="90"
            minimum="30"
            thickness="3"
            valueformat="{}{0:0}值"
            value="{binding elementname=myslider2, path=value}" />
        <slider
            name="myslider2"
            width="200"
            margin="0,0,0,20"
            horizontalalignment="center"
            verticalalignment="bottom"
            maximum="90"
            minimum="30" />
    </stackpanel>

    <stackpanel>
        <wd:gauge
            title="反对率"
            width="200"
            height="200"
            margin="10"
            horizontalalignment="center"
            verticalalignment="center"
            valueformat="{}{0:0}%"
            value="{binding elementname=myslider, path=value}" />
        <slider
            name="myslider"
            width="200"
            margin="0,0,0,20"
            horizontalalignment="center"
            verticalalignment="bottom"
            maximum="100"
            minimum="0" />
    </stackpanel>
    <stackpanel verticalalignment="bottom">
        <wd:gauge
            title="max"
            width="100"
            height="100"
            margin="10"
            horizontalalignment="center"
            verticalalignment="center"
            background="black"
            borderbrush="dodgerblue"
            fontsize="8"
            maximum="90"
            minimum="30"
            thickness="3"
            valueformat="{}{0:0}值"
            value="{binding elementname=myslider3, path=value}" />
        <slider
            name="myslider3"
            width="200"
            margin="0,0,0,20"
            horizontalalignment="center"
            verticalalignment="bottom"
            maximum="90"
            minimum="30" />
    </stackpanel>
</wrappanel>

效果图

到此这篇关于wpf实现自绘仪表盘gauge的文章就介绍到这了,更多相关wpf自绘仪表盘内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!

(0)
打赏 微信扫一扫 微信扫一扫

您想发表意见!!点此发布评论

推荐阅读

wps调用Outlook 批量发送电子邮件时持续弹出警告框怎么办?

02-08

WPF数据绑定时出现StringFormat失效的原因和解决方法

12-11

WPF实现自定义控件的几种方法

12-07

WPF实现数据绑定的几种方法

12-07

WPF仿Tabcontrol实现切换多个不同View

12-01

WPF如何在圆形上优雅地添加刻度线

11-22

猜你喜欢

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论