WPF上位机组件开发-设备状态运行图基础版

需求

实现一个设备的运行时间图,用四色表示设备运行状态,灰色为离线,黄色为待机,绿色为运行,红色为故障

基础实现

创建一个用户控件,在Canvas中用不同颜色的Rectangle方块,表示不同的状态;用Line绘制刻度尺。

刻度尺绘制

csharp 复制代码
/// <summary>
/// 刻度线长
/// </summary>
private int length = 20;

/// <summary>
/// 刻度间隔
/// </summary>
private int intervalNum = 2;
        
/// <summary>
/// 绘制刻度,根据canvas长度,分为24等分,对应24小时,刻度线长为length,最左边绘制0点
/// </summary>
void DrawScale()
{
    double interval = ScaleCanvas.ActualWidth / 24 * intervalNum;
    double height = ScaleCanvas.ActualHeight;
    for (int i = 0; i <= 24 / intervalNum; i++)
    {
        double y1 = (i * intervalNum) % AreaNum == 0 ? 0 : height - length;
        y1 = Math.Min(Math.Abs(y1), height);

        Line line = new Line
        {
            X1 = i * interval,
            X2 = i * interval,
            Y1 = y1,
            Y2 = height,
            Stroke = Brushes.Gray,
            StrokeThickness = 1
        };
        this.ScaleCanvas.Children.Add(line);
        TextBlock textBlock = new TextBlock
        {
            Text = (i * intervalNum).ToString(),
            FontSize = 12,
        };
        textBlock.SetValue(Canvas.LeftProperty, i * interval - 5);
        textBlock.SetValue(Canvas.TopProperty, height);
        this.ScaleCanvas.Children.Add(textBlock);
    }
    ;
}

状态控制

按比例计算方块的横坐标和长度,比例=Canvas实际宽度ActualHeight / 一天总分钟数(24*60)

宽度Width等于 时间间隔的分钟数x 比例

横坐标Canvas.Left等于 起始时间x 比例

csharp 复制代码
//绘制设备运行状态时间图
void DrawContent()
{
    if (ItemsSource == null)
    {
        return;
    }
    double unit = ScaleCanvas.ActualWidth / (24 * 60);
    //根据ItemsSource筛选出当天的时间集合,然后绘制不同颜色的Rectangle
    var items = ItemsSource
        .Where(w => w.Time.Date.Equals(Date.Date) && statusColors.ContainsKey(w.Status))
        .ToArray();

    for (int i = 0; i < items.Length - 1; i++)
    {
        int start = GetMinutes(items[i].Time);
        int end = GetMinutes(items[i + 1].Time);
        Rectangle rectangle = new Rectangle
        {
            Width = (end - start) * unit,
            Height = ScaleCanvas.ActualHeight,
            Fill = statusColors[items[i].Status]
        };
        rectangle.SetValue(Canvas.LeftProperty, start * unit);
        this.ScaleCanvas.Children.Add(rectangle);
    }
}

完整实现

控件EquipStatusTimeChart.xaml

xml 复制代码
<UserControl
    x:Class="WpfApp1.EquipStatusTimeChart"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:local="clr-namespace:WpfApp1"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    d:DesignHeight="65"
    d:DesignWidth="760"
    mc:Ignorable="d">
    <Grid>
        <Border
            MinWidth="400"
            MinHeight="20"
            Margin="10"
            BorderBrush="LightGray"
            BorderThickness="1">

            <Canvas Name="ScaleCanvas" />
        </Border>
    </Grid>
</UserControl>

EquipStatusTimeChart.xaml 的交互逻辑

csharp 复制代码
 public partial class EquipStatusTimeChart : UserControl
 {
     public DateTime Date
     {
         get { return (DateTime)GetValue(DateProperty); }
         set { SetValue(DateProperty, value); }
     }

     // Using a DependencyProperty as the backing store for Date.  This enables animation, styling, binding, etc...
     public static readonly DependencyProperty DateProperty = DependencyProperty.Register(
         "Date",
         typeof(DateTime),
         typeof(EquipStatusTimeChart),
         new PropertyMetadata(DateTime.Today)
     );

     private Dictionary<string, Brush> statusColors = new Dictionary<string, Brush>();

     public List<StatusItem> Statuses
     {
         get { return (List<StatusItem>)GetValue(StatusesProperty); }
         set { SetValue(StatusesProperty, value); }
     }

     // Using a DependencyProperty as the backing store for Statuses.  This enables animation, styling, binding, etc...
     public static readonly DependencyProperty StatusesProperty = DependencyProperty.Register(
         "Statuses",
         typeof(List<StatusItem>),
         typeof(EquipStatusTimeChart),
         new PropertyMetadata(
             new List<StatusItem>(),
             (sender, e) =>
             {
                 var chart = sender as EquipStatusTimeChart;
                 chart.StatusesChanged(sender, e);
             }
         )
     );

     void StatusesChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
     {
         if (e.NewValue != null)
         {
             statusColors = Statuses.ToDictionary(x => x.Status, x => x.ShowColor);
         }
     }

     public IEnumerable<StatusTimeItem> ItemsSource
     {
         get { return (IEnumerable<StatusTimeItem>)GetValue(ItemsSourceProperty); }
         set { SetValue(ItemsSourceProperty, value); }
     }

     // Using a DependencyProperty as the backing store for MyProperty.  This enables animation, styling, binding, etc...
     public static readonly DependencyProperty ItemsSourceProperty = DependencyProperty.Register(
         "ItemsSource",
         typeof(IEnumerable<StatusTimeItem>),
         typeof(EquipStatusTimeChart),
         new PropertyMetadata(null)
     );

     public EquipStatusTimeChart()
     {
         InitializeComponent();
     }

     public override void OnApplyTemplate()
     {
         base.OnApplyTemplate();
         statusColors = Statuses.ToDictionary(x => x.Status, x => x.ShowColor);
     }

     /// <summary>
     /// 刻度线长
     /// </summary>
     private int length = 20;

     /// <summary>
     /// 刻度间隔
     /// </summary>
     private int intervalNum = 2;

     private int AreaNum = 6;

     protected override void OnRender(DrawingContext drawingContext)
     {
         base.OnRender(drawingContext);
         this.ScaleCanvas.Children.Clear();
         DrawContent();
         DrawScale();
     }

     //绘制设备运行状态时间图
     void DrawContent()
     {
         if (ItemsSource == null)
         {
             return;
         }
         double unit = ScaleCanvas.ActualWidth / (24 * 60);
         //根据ItemsSource筛选出当天的时间集合,然后绘制不同颜色的Rectangle
         var items = ItemsSource
             .Where(w => w.Time.Date.Equals(Date.Date) && statusColors.ContainsKey(w.Status))
             .ToArray();

         for (int i = 0; i < items.Length - 1; i++)
         {
             int start = GetMinutes(items[i].Time);
             int end = GetMinutes(items[i + 1].Time);
             Rectangle rectangle = new Rectangle
             {
                 Width = (end - start) * unit,
                 Height = ScaleCanvas.ActualHeight,
                 Fill = statusColors[items[i].Status]
             };
             rectangle.SetValue(Canvas.LeftProperty, start * unit);
             this.ScaleCanvas.Children.Add(rectangle);
         }
     }

     int GetMinutes(DateTime time)
     {
         return time.Hour * 60 + time.Minute;
     }

     /// <summary>
     /// 绘制刻度,根据canvas长度,分为24等分,对应24小时,刻度线长为length,最左边绘制0点
     /// </summary>
     void DrawScale()
     {
         double interval = ScaleCanvas.ActualWidth / 24 * intervalNum;
         double height = ScaleCanvas.ActualHeight;
         for (int i = 0; i <= 24 / intervalNum; i++)
         {
             double y1 = (i * intervalNum) % AreaNum == 0 ? 0 : height - length;
             y1 = Math.Min(Math.Abs(y1), height);

             Line line = new Line
             {
                 X1 = i * interval,
                 X2 = i * interval,
                 Y1 = y1,
                 Y2 = height,
                 Stroke = Brushes.Gray,
                 StrokeThickness = 1
             };
             this.ScaleCanvas.Children.Add(line);
             TextBlock textBlock = new TextBlock
             {
                 Text = (i * intervalNum).ToString(),
                 FontSize = 12,
             };
             textBlock.SetValue(Canvas.LeftProperty, i * interval - 5);
             textBlock.SetValue(Canvas.TopProperty, height);
             this.ScaleCanvas.Children.Add(textBlock);
         }
         ;
     }
 }

控件状态项

表示状态选项

csharp 复制代码
  public class StatusItem : DependencyObject
  {
      public string Status
      {
          get { return (string)GetValue(StatusProperty); }
          set { SetValue(StatusProperty, value); }
      }

      // Using a DependencyProperty as the backing store for Status.  This enables animation, styling, binding, etc...
      public static readonly DependencyProperty StatusProperty = DependencyProperty.Register(
          "Status",
          typeof(string),
          typeof(StatusItem),
          new PropertyMetadata("")
      );

      public Brush ShowColor
      {
          get { return (Brush)GetValue(ShowColorProperty); }
          set { SetValue(ShowColorProperty, value); }
      }

      // Using a DependencyProperty as the backing store for ShowColor.  This enables animation, styling, binding, etc...
      public static readonly DependencyProperty ShowColorProperty = DependencyProperty.Register(
          "ShowColor",
          typeof(Brush),
          typeof(StatusItem),
          new PropertyMetadata(null)
      );
  }

使用

在MainWindow中添加控件,

xml 复制代码
    <Grid>
        <local:EquipStatusTimeChart x:Name="Chart" Margin="40,179,42,187">
            <local:EquipStatusTimeChart.Statuses>
                <local:StatusItem ShowColor="LightGray" Status="Offline" />
                <local:StatusItem ShowColor="#FFD400" Status="Wait" />
                <local:StatusItem ShowColor="#3DD573 " Status="Run" />
                <local:StatusItem ShowColor="Red" Status="Failt" />
            </local:EquipStatusTimeChart.Statuses>
        </local:EquipStatusTimeChart>

    </Grid>

增加一个数据绑定类

csharp 复制代码
    public class StatusTimeItem
    {
        public DateTime Time { get; set; }

        public string Status { get; set; }
    }

在后台交互逻辑中初始化数据,然后绑定到控件上

csharp 复制代码
 List<StatusTimeItem> items = new List<StatusTimeItem>();
 items.Add(new StatusTimeItem() { Status = "Offline", Time = DateTime.Today });
 items.Add(
     new StatusTimeItem()
     {
         Status = "Wait",
         Time = DateTime.Today.AddHours(1).AddMinutes(1),
     }
 );
 items.Add(new StatusTimeItem() { Status = "Run", Time = DateTime.Today.AddHours(2) });
 items.Add(new StatusTimeItem() { Status = "Run", Time = DateTime.Today.AddHours(2) });
 items.Add(new StatusTimeItem() { Status = "Wait", Time = DateTime.Today.AddHours(3) });
 items.Add(new StatusTimeItem() { Status = "Failt", Time = DateTime.Today.AddHours(4) });
 items.Add(
     new StatusTimeItem()
     {
         Status = "Run",
         Time = DateTime.Today.AddHours(5).AddMinutes(29),
     }
 );

 items.Add(
     new StatusTimeItem()
     {
         Status = "Run",
         Time = DateTime.Today.AddHours(9).AddMinutes(29),
     }
 );
 Chart.ItemsSource = items;

运行效果

相关推荐
看我干嘛!2 小时前
python第四次作业
开发语言·python
Coder_preston2 小时前
Java集合框架详解
java·开发语言
多多*2 小时前
2026年最新 测试开发工程师相关 Linux相关知识点
java·开发语言·javascript·算法·spring·java-ee·maven
2301_763472582 小时前
实时系统下的C++编程
开发语言·c++·算法
阿猿收手吧!2 小时前
【C++】深入理解C++ Atomic内存序:解决什么问题?怎么用?
开发语言·c++
小白学大数据2 小时前
Python爬虫实现无限滚动页面的自动点击与内容抓取
开发语言·爬虫·python·pandas
Andy Dennis2 小时前
一文漫谈设计模式之创建型模式(一)
java·开发语言·设计模式
兩尛2 小时前
c++遍历容器(vector、list、set、map
开发语言·c++
£漫步 云端彡2 小时前
Golang学习历程【第十三篇 并发入门:goroutine + channel 基础】
开发语言·学习·golang