广告位联系
返回顶部
分享到

WPF实现雷达扫描图的绘制介绍

asp.net 来源:互联网 作者:酷站 发布时间:2022-05-10 18:28:00 人浏览
摘要

实现一个雷达扫描图。 源代码在TK_King/雷达 (gitee.com) https://gitee.com/TK_King/radar,自行下载就好了 制作思路 绘制圆形(或者称之轮) 绘制分割线 绘制扫描范围 添加扫描点 具体实现 首先

实现一个雷达扫描图。

源代码在TK_King/雷达 (gitee.com) https://gitee.com/TK_King/radar,自行下载就好了

制作思路

  • 绘制圆形(或者称之轮)
  • 绘制分割线
  • 绘制扫描范围
  • 添加扫描点

具体实现

首先我们使用自定义的控件。你可以使用vs自动添加,也可以手动创建类。注意手动创建时要创建Themes/Generic.xaml的文件路径哦。

控件继承自itemscontrol,取名叫做Radar。

我们第一步思考如何实现圆形或者轮,特别是等距的轮。

我们可以使用简单的itemscontrol的WPF控件,通过自定义ItemTemplate就可以简单的创建了。

因为要显示圆,所以使用Ellipse是最简单的事情。

又因为要在同一个区域内,显示同心圆,我们将面板改为Grid,利用叠加的特性去构造同心圆。

既然我们用了itemscontrol 来承载圈轮,直接让这个圈可自定义呢?

所以,我们构造一个集合依赖属性。关于集合依赖属性我们可以参考MSDN官方文档

1

2

3

4

5

6

7

8

9

10

11

12

13

14

/// <summary>

        /// 每圈的大小

        /// </summary>

        public FreezableCollection<RadarSize> RadarCircle

        {

            get { return (FreezableCollection<RadarSize>)GetValue(RadarCircleProperty); }

            set { SetValue(RadarCircleProperty, value); }

        }

 

        /// <summary>

        /// 每圈的大小

        /// </summary>

        public static readonly DependencyProperty RadarCircleProperty =

            DependencyProperty.Register("RadarCircle", typeof(FreezableCollection<RadarSize>), typeof(Radar), new PropertyMetadata(new PropertyChangedCallback(OnRadarCircelValueChanged)));

对应泛型类可以参考源代码,基本元素就是绑定ellipse的参数

1

2

3

4

5

6

7

8

9

10

11

12

<ItemsControl Grid.ColumnSpan="2" Grid.RowSpan="2" VerticalAlignment="Center" HorizontalAlignment="Center" x:Name="ic"  ItemsSource="{TemplateBinding RadarCircle }">

                            <ItemsControl.ItemsPanel>

                                <ItemsPanelTemplate>

                                    <Grid IsItemsHost="True"/>

                                </ItemsPanelTemplate>

                            </ItemsControl.ItemsPanel>

                            <ItemsControl.ItemTemplate>

                                <DataTemplate>

                                    <Ellipse Width="{Binding  Width}" Height="{Binding Height}"  Stroke="{Binding Color}"/>

                                </DataTemplate>

                            </ItemsControl.ItemTemplate>

    </ItemsControl>

哇啦,图像就出来了。

同理,我们创建分割线也是同样的过程。

对于分割线的切割算法,我们使用圆上点的坐标可以通过( rcos,rsin)=》(x,y) ,也就是极坐标。

关于此部分代码是放在布局块内ArrangeOverride,也可以放置在OnReader。

下面是局部代码,完整可以参考源代码

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

var angle = 180.0 / 6;

            circlesize = size.Height > size.Width ? size.Width : size.Height;

            RadarFillWidth = circlesize;

            var midx = circlesize / 2.0;

            var midy = circlesize / 2.0;

            circlesize = circlesize / 2;

            RadarRadius = circlesize;

            //默认为6个

            for (int i = 0; i < 6; i++)

            {

                var baseangel = angle * i;

                var l1 = new Point(midx + circlesize * Math.Cos(Rad(baseangel)), midy - circlesize * Math.Sin(Rad(baseangel)));

                var half = baseangel + 180;

                var l2 = new Point(midx + circlesize * Math.Cos(Rad(half)), midy - circlesize * Math.Sin(Rad(half)));

                RadarLineSize radarLine = new RadarLineSize();

                radarLine.Start = l1;

                radarLine.End = l2;

                radarLine.Color = RadarLineColor;

                RadarLine.Add(radarLine);

            }

            return size;

依赖属性

1

2

3

4

5

6

7

8

9

10

11

12

13

14

/// <summary>

        /// 雷达图的分割线,目前固定为6,可以自行修改

        /// </summary>

        public FreezableCollection<RadarLineSize> RadarLine

        {

            get { return (FreezableCollection<RadarLineSize>)GetValue(RadarLineProperty); }

            set { SetValue(RadarLineProperty, value); }

        }

 

        /// <summary>

        /// 雷达图的分割线,目前固定为6,可以自行修改

        /// </summary>

        public static readonly DependencyProperty RadarLineProperty =

            DependencyProperty.Register("RadarLine", typeof(FreezableCollection<RadarLineSize>), typeof(Radar));

xaml代码

1

2

3

4

5

6

7

8

9

10

11

12

<ItemsControl Grid.ColumnSpan="2" Grid.RowSpan="2"  VerticalAlignment="Center" HorizontalAlignment="Center"  x:Name="ic2"   ItemsSource="{TemplateBinding RadarLine }">

                            <ItemsControl.ItemsPanel>

                                <ItemsPanelTemplate>

                                    <Grid IsItemsHost="True"/>

                                </ItemsPanelTemplate>

                            </ItemsControl.ItemsPanel>

                            <ItemsControl.ItemTemplate>

                                <DataTemplate>

                                    <Line X1="{Binding Start.X}" Y1="{Binding Start.Y}" X2="{Binding End.X}" Y2="{Binding End.Y}"  Stroke="{Binding Color}"/>

                                </DataTemplate>

                            </ItemsControl.ItemTemplate>

                        </ItemsControl>

下一步就是扇形扫描了。

我们使用一个完整的圆,将其内部颜色填充为线性刷就可以得到一个效果不错的扫描了。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

/// <summary>

        /// 雷达扫描的颜色

        /// </summary>

        public Brush RadarColor

        {

            get { return (Brush)GetValue(RadarColorProperty); }

            set { SetValue(RadarColorProperty, value); }

        }

 

        /// <summary>

        /// 雷达扫描的颜色

        /// </summary>

        public static readonly DependencyProperty RadarColorProperty =

            DependencyProperty.Register("RadarColor", typeof(Brush), typeof(Radar));

为了更好的定义这个圆,我们将radar的template使用grid面板等距分成四个区域(其实没啥用,主要是为了扇形扫描时做圆心选择的line,也可以不分成四个)。

在考虑动画,只需要做圆形360的选择就可以了。为了更好应用,我们创一个paly的依赖属性来播放动画。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

/// <summary>

        /// 是否播放动画

        /// </summary>

        public bool Play

        {

            get { return (bool)GetValue(PlayProperty); }

            set { SetValue(PlayProperty, value); }

        }

 

        /// <summary>

        /// 是否播放动画

        /// </summary>

        public static readonly DependencyProperty PlayProperty =

            DependencyProperty.Register("Play", typeof(bool), typeof(Radar), new PropertyMetadata(false));

xaml代码( 部分)

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

<Style.Resources>

            <LinearGradientBrush x:Key="radarcolor" StartPoint="0,0" EndPoint="0,1">

                <GradientStop Offset="0" Color="Lime" />

                <GradientStop Offset="0.5" Color="Transparent" />

            </LinearGradientBrush>

        </Style.Resources>

  <Setter Property="Template">

            <Setter.Value>

                <ControlTemplate TargetType="{x:Type local:Radar}">

                    <Grid x:Name="grid"   >

                        <Grid.RowDefinitions>

                            <RowDefinition Height="2*"/>

                            <RowDefinition Height="2*"/>

                        </Grid.RowDefinitions>

                        <Grid.ColumnDefinitions>

                            <ColumnDefinition Width="2*"/>

                            <ColumnDefinition Width="2*"/>

                        </Grid.ColumnDefinitions>

                        <ItemsControl Grid.ColumnSpan="2" Grid.RowSpan="2" VerticalAlignment="Center" HorizontalAlignment="Center" x:Name="ic"  ItemsSource="{TemplateBinding RadarCircle }">

                            <ItemsControl.ItemsPanel>

                                <ItemsPanelTemplate>

                                    <Grid IsItemsHost="True"/>

                                </ItemsPanelTemplate>

                            </ItemsControl.ItemsPanel>

                            <ItemsControl.ItemTemplate>

                                <DataTemplate>

                                    <Ellipse Width="{Binding  Width}" Height="{Binding Height}"  Stroke="{Binding Color}"/>

                                </DataTemplate>

                            </ItemsControl.ItemTemplate>

                        </ItemsControl>

                        <ItemsControl Grid.ColumnSpan="2" Grid.RowSpan="2"  VerticalAlignment="Center" HorizontalAlignment="Center"  x:Name="ic2"   ItemsSource="{TemplateBinding RadarLine }">

                            <ItemsControl.ItemsPanel>

                                <ItemsPanelTemplate>

                                    <Grid IsItemsHost="True"/>

                                </ItemsPanelTemplate>

                            </ItemsControl.ItemsPanel>

                            <ItemsControl.ItemTemplate>

                                <DataTemplate>

                                    <Line X1="{Binding Start.X}" Y1="{Binding Start.Y}" X2="{Binding End.X}" Y2="{Binding End.Y}"  Stroke="{Binding Color}"/>

                                </DataTemplate>

                            </ItemsControl.ItemTemplate>

                        </ItemsControl>

                        <Ellipse Fill="{TemplateBinding RadarColor}"   Grid.ColumnSpan="2" Grid.RowSpan="2"  x:Name="ep" RenderTransformOrigin="0.5,0.5" Width="{TemplateBinding RadarFillWidth}" Height="{TemplateBinding RadarFillWidth}">

                            <Ellipse.RenderTransform>

                                <RotateTransform x:Name="rtf" />

                            </Ellipse.RenderTransform>

                        </Ellipse>

                    </Grid>

                    <ControlTemplate.Triggers>

                        <Trigger Property="Play" Value="True">

                            <Trigger.EnterActions>

                                <BeginStoryboard  x:Name="bs" >

                                    <Storyboard >

                                        <DoubleAnimation Storyboard.TargetName="rtf" Storyboard.TargetProperty="Angle"   From="0" To="360" Duration="0:0:2" RepeatBehavior="Forever"/>

                                    </Storyboard>

                                </BeginStoryboard>

                            </Trigger.EnterActions>

                        </Trigger>

                        <Trigger Property="Play" Value="False">

                            <Trigger.EnterActions>

                                <RemoveStoryboard BeginStoryboardName="bs"/>

                            </Trigger.EnterActions>

                        </Trigger>

                    </ControlTemplate.Triggers>

                </ControlTemplate>

            </Setter.Value>

效果

那么剩下就是扫描点的操作。

因为我们的控件是继承ItemsControl,我们到现在还没有利用ItemsSource这个属性。

所以我们要制作一个子控件来呈现扫描点。

由于子控件较为简单,只不过是一个圆而已。我们就让子控件继承Control就好了。

一切从简,我们不弄布局这一套了,直接在父控件中使用Canvas面板,子控件增加属性Left,Top这两个依赖属性。

重点说一下,子控件中存在一个linscar的方法,是为了将点如果在雷达外侧时,按照同角度缩放到最外层的方法。就是通过半径重新计算一边极坐标。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

/// <summary>

        /// 线性缩放

        /// </summary>

        /// <param name="size">半径</param>

        internal void LineScar(double size)

        {

            var midpoint = new Vector(size, size);

            var vp = new Vector(Left, Top);

            var sub = vp - midpoint;

            var angle = Vector.AngleBetween(sub, new Vector(size, 1));

            angle = angle > 0 ? angle : angle + 360;

            //距离大于半径,根据半径重新绘制

            if (sub.Length >= size)

            {

                Top = size - size * Math.Sin(Rad(angle)) - Width / 2;

                Left = size + size * Math.Cos(Rad(angle)) - Width / 2;

            }

        }

那么在父项中如何摆放呢?

我们刚才说父项使用canvas绘图,所以我们在radar中修改itempanel的面板属性,下面代码存在于父项xaml

1

2

3

4

5

6

7

<Setter Property="ItemsPanel">

            <Setter.Value>

                <ItemsPanelTemplate>

                    <Canvas IsItemsHost="True"/>

                </ItemsPanelTemplate>

            </Setter.Value>

        </Setter>

子项代码如下,比较少就贴了

xaml代码

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

<Style TargetType="local:RadarItem">

        <Setter Property="VerticalAlignment" Value="Top" />

        <Setter Property="HorizontalAlignment" Value="Left" />

        <Setter Property="Padding" Value="0" />

        <Setter Property="Margin" Value="0" />

        <Setter Property="Canvas.Top" Value="{Binding RelativeSource={RelativeSource Mode=Self},Path=Top}" />

        <Setter Property="Canvas.Left" Value="{Binding RelativeSource={RelativeSource Mode=Self},Path=Left}" />

        <Setter Property="Template">

            <Setter.Value>

                <ControlTemplate TargetType="local:RadarItem">

                    <Border  >

                        <Ellipse Width="{TemplateBinding Width}" Height="{TemplateBinding Height}" Fill="{TemplateBinding Color}" />

                    </Border>

                </ControlTemplate>

            </Setter.Value>

        </Setter>

    </Style>

radarItem

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

/// <summary>

    /// 雷达子项

    /// </summary>

    public class RadarItem : Control

    {

 

        static RadarItem()

        {

            DefaultStyleKeyProperty.OverrideMetadata(typeof(RadarItem), new FrameworkPropertyMetadata(typeof(RadarItem)));

        }

        public RadarItem()

        {

 

        }

 

        /// <summary>

        /// 转弧度

        /// </summary>

        /// <param name="val">角度</param>

        /// <returns>弧度制</returns>

        double Rad(double val)

        {

            return val * Math.PI / 180;

        }

        /// <summary>

        /// 线性缩放

        /// </summary>

        /// <param name="size">半径</param>

        internal void LineScar(double size)

        {

            var midpoint = new Vector(size, size);

            var vp = new Vector(Left, Top);

            var sub = vp - midpoint;

            var angle = Vector.AngleBetween(sub, new Vector(size, 1));

            angle = angle > 0 ? angle : angle + 360;

            //距离大于半径,根据半径重新绘制

            if (sub.Length >= size)

            {

                Top = size - size * Math.Sin(Rad(angle)) - Width / 2;

                Left = size + size * Math.Cos(Rad(angle)) - Width / 2;

            }

        }

 

        /// <summary>

        /// 顶部距离,用canvas.top绘制

        /// </summary>

        public double Top

        {

            get { return (double)GetValue(TopProperty); }

            set { SetValue(TopProperty, value); }

        }

 

        /// <summary>

        /// 顶部距离,用canvas.top绘制

        /// </summary>

        public static readonly DependencyProperty TopProperty =

            DependencyProperty.Register("Top", typeof(double), typeof(RadarItem), new PropertyMetadata(0.0));

 

 

        /// <summary>

        /// 左侧距离,用于canvas.left绘制

        /// </summary>

        public double Left

        {

            get { return (double)GetValue(LeftProperty); }

            set { SetValue(LeftProperty, value); }

        }

 

        /// <summary>

        /// 左侧距离,用于canvas.left绘制

        /// </summary>

        public static readonly DependencyProperty LeftProperty =

            DependencyProperty.Register("Left", typeof(double), typeof(RadarItem), new PropertyMetadata(0.0));

 

 

        /// <summary>

        /// 填充颜色

        /// </summary>

        public Brush Color

        {

            get { return (Brush)GetValue(ColorProperty); }

            set { SetValue(ColorProperty, value); }

        }

 

        /// <summary>

        /// 填充颜色

        /// </summary>

        public static readonly DependencyProperty ColorProperty =

            DependencyProperty.Register("Color", typeof(Brush), typeof(RadarItem), new PropertyMetadata(new SolidColorBrush(Colors.Red)));

    }

于是乎我们就得到了一个雷达扫描图


版权声明 : 本文内容来源于互联网或用户自行发布贡献,该文观点仅代表原作者本人。本站仅提供信息存储空间服务和不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权, 违法违规的内容, 请发送邮件至2530232025#qq.cn(#换@)举报,一经查实,本站将立刻删除。

您可能感兴趣的文章 :

原文链接 : https://www.cnblogs.com/T-ARF/p/16253121.html
相关文章
  • ASP.NET MVC使用Identity增删改查用户

    ASP.NET MVC使用Identity增删改查用户
    源码在这里:https://github.com/darrenji/UseIdentityCRUDUserInMVC,本地下载 在VS2013中创建一个MVC项目,用默认的无身份验证作为身份验证机制。 通过
  • WPF实现雷达扫描图的绘制介绍

    WPF实现雷达扫描图的绘制介绍
    实现一个雷达扫描图。 源代码在TK_King/雷达 (gitee.com) https://gitee.com/TK_King/radar,自行下载就好了 制作思路 绘制圆形(或者称之轮) 绘制分割
  • .Net Core之JWT授权介绍

    .Net Core之JWT授权介绍
    JSON Web令牌(JWT)是一个开放标准(RFC 7519),它定义了一种紧凑且自包含的方式,用于在各方之间安全地传输信息作为JSON对象。由于此信息
  • ASP.NET Core使用Middleware设置有条件允许访问路由

    ASP.NET Core使用Middleware设置有条件允许访问路由
    1.简介 有时,我们可能在Web API中包含一些具有调试功能的请求。比如我们上次的文章中为什么ASP.NETCore数据库连接串的值和appsettings.json配的
  • ASP.NET Core使用功能开关控制路由访问操作

    ASP.NET Core使用功能开关控制路由访问操作
    前言: 在前面的文章,我们介绍了使用Middleware有条件地允许访问路由(《ASP.NETCore使用Middleware设置有条件允许访问路由》)。 而对于一些
  • ASP.NET Core使用功能开关控制路由访问操作(续)

    ASP.NET Core使用功能开关控制路由访问操作(续)
    前言: 在前面的文章,我们介绍了? ?使用功能开关控制路由访问??。 但其实我们使用了2个条件做的判断: 1 2 3 4 var isDebugEndpoint = context.Re
  • 详解MediatR的使用
    环境: .NET 5 ASP.NET Core MVC (project) 1. MediatR MediatR .NET中的简单中介者模式实现,一种进程内消息传递机制(无其他外部依赖)。支持以同步或
  • .NET Core 3.0里新的JSON API介绍
    为什么需要新的 JSON API ? JSON.NET 大家都用过,老版本的 ASP.NET Core 也依赖于 JSON.NET 。 然而这个依赖就会引起一些版本问题:例如 ASP .NET
  • Net Core Web Api项目与在NginX下发布的方法
    前言 本文将介绍Net Core的一些基础知识和如何NginX下发布Net Core的WebApi项目。 测试环境 操作系统:windows 10 开发工具:visualstudio 2019 框架:
  • ASP.NET Core中的Http缓存使用
    Http响应缓存可减少客户端或代理对web服务器发出的请求数。响应缓存还减少了web服务器生成响应所需的工作量。响应缓存由Http请求中的he
  • 本站所有内容来源于互联网或用户自行发布,本站仅提供信息存储空间服务,不拥有版权,不承担法律责任。如有侵犯您的权益,请您联系站长处理!
  • Copyright © 2017-2022 F11.CN All Rights Reserved. F11站长开发者网 版权所有 | 苏ICP备2022031554号-1 | 51LA统计