基于WPF的数据可视化大屏展示(石油类专业 含测井曲线的绘制 代码详细注解)

仅作自己学习适用


0 引言

      搞完这个小项目后,看着自己的成果感觉非常有成就感,但是如果是仅跟一次是没有什么效果的,还需要继续努力。

1 程序运行效果图

页面布局:
页面布局

运行效果:
运行效果

2 代码结构

      不同的绘图区域,需要有不同的数据结构。如全国油气产量前十名,就对应着油气田名称和油气当量;又如石油价格走势,对应着交易日期,开盘价、收市价、最高价和最低价,因次需要单独定义一个参数类,用于对这些存储结构进行定义;在地图部分,为了显示油田在地图中的位置,需要记录油田的坐标信息,因此定义了油田类型:

/// <summary>
/// 油田信息
/// </summary>
public List<OilField> OilFields { get; set; }

在右边绘制曲线的时候,是在参数类里是定义的一个曲线字典如下:

 /// <summary>
 /// 曲线信息字典
 /// </summary>
 public Dictionary<string,LogCurve> LogCurveDic { get; set; }

那么曲线这个类型也是自定义的类型,因次再创建一个类型用于定义曲线类。整个项目的文件结构如下:
代码结构

3 完整代码

完整的项目下载链接:数据可视化大屏课程设计
也可以直接复制这里的代码,只有图标和背景图片的文件没有了,其他都可以完全复现。

3.1 UI部分(XAML)

这里需要添加外部包,右击项目,选择"管理NuGet程序包",下载一些程序包:
在这里插入图片描述
需要下载如下几个程序包,用于绘图和地图部分的显示:
在这里插入图片描述
其Xaml代码如下

<Window x:Class="LoggingScreen.MainWindow"
        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:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:LoggingScreen" xmlns:oxy="http://oxyplot.org/wpf" xmlns:windowspresentation="clr-namespace:GMap.NET.WindowsPresentation;assembly=GMap.NET.WindowsPresentation"
        mc:Ignorable="d"
        Title="LoggingScreen V1.0" Height="500" Width="900">
    <Window.Background>
        <ImageBrush ImageSource="D:\GiteeRepository\csharpe-sutdy\LoggingScreen\bin\Debug\Data\background.png" Stretch="UniformToFill"/>
    </Window.Background>
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="45"/>
            <RowDefinition Height="*"/>
            <RowDefinition Height="*"/>
            <RowDefinition Height="*"/>
            <RowDefinition Height="*"/>
            <RowDefinition Height="30"/>
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="4*"/>
            <ColumnDefinition Width="3*"/>
            <ColumnDefinition Width="3*"/>
            <ColumnDefinition Width="*"/>
            <ColumnDefinition Width="*"/>
            <ColumnDefinition Width="*"/>
            <ColumnDefinition Width="*"/>
        </Grid.ColumnDefinitions>

        <!--<Image Grid.RowSpan="6" Grid.ColumnSpan="7" Source="C:\Users\JTL_1\Desktop\background.jpeg" Opacity="0.6"></Image>-->

        <Label Grid.ColumnSpan="3" FontSize="30" HorizontalAlignment="Center" Foreground="Green" >
            <Label.Background>
                <ImageBrush/>
            </Label.Background>
            全国油气田实时监测大屏
        </Label>

        <DockPanel Grid.Row="1">
            <!--pv1-->
            <!--第一个图形-->
            <oxy:PlotView Name="pv_production" MouseDoubleClick="pv_production_MouseDoubleClick">
                <oxy:PlotView.Background>
                    <SolidColorBrush  Opacity="0.5"/>
                </oxy:PlotView.Background>
            </oxy:PlotView>
        </DockPanel>
        <DockPanel Grid.Row="2">
            <!--<Label>第二个图像</Label>-->
            <!--pv2-->
            <oxy:PlotView Name="pv_HChart">
                <oxy:PlotView.Background>
                    <SolidColorBrush Opacity="0.5"/>
                </oxy:PlotView.Background>
            </oxy:PlotView>
        </DockPanel>
        <DockPanel Grid.Row="3">
            <!--<Label>第三个图像</Label>-->
            <oxy:PlotView Name="pv_KLine">
                <oxy:PlotView.Background>
                    <SolidColorBrush Opacity="0.5"/>
                </oxy:PlotView.Background>
            </oxy:PlotView>
        </DockPanel>
        <DockPanel Grid.Row="4">
            <!--<Label>第四个图像</Label>-->
            <oxy:PlotView Name="pv_isoLine">
                <oxy:PlotView.Background>
                    <SolidColorBrush Opacity="0.5"/>
                </oxy:PlotView.Background>
            </oxy:PlotView>
        </DockPanel>
        <Label Grid.Row="5" Grid.ColumnSpan="3" FontSize="15" HorizontalAlignment="Left" Foreground="PaleVioletRed">
            第29分队 || 队长:袁朗 队员:许三多 吴哲 成才 || 装备:班用95轻机枪  88狙击枪 
        </Label>
        <DockPanel Grid.Row="1" Grid.Column="1">
            <Label Foreground="Orange" DockPanel.Dock="Top" FontSize="10">第①个监控</Label>
            <MediaElement Name="media1" MediaEnded="MediaEnd_Replay" ></MediaElement>
        </DockPanel>
        <DockPanel Grid.Row="1" Grid.Column="2">
            <Label Foreground="Orange" DockPanel.Dock="Top" FontSize="10">第②个监控</Label>
            <MediaElement Name="media2" MediaEnded="MediaEnd_Replay" ></MediaElement>
        </DockPanel>
        <DockPanel Grid.Row="2" Grid.Column="1">
            <Label Foreground="Orange" DockPanel.Dock="Top" FontSize="10">第③个监控</Label>
            <MediaElement Name="media3" MediaEnded="MediaEnd_Replay"></MediaElement>
        </DockPanel>
        <DockPanel Grid.Row="2" Grid.Column="2">
            <Label Foreground="Orange" DockPanel.Dock="Top" FontSize="10">第④个监控</Label>
            <MediaElement Name="media4" MediaEnded="MediaEnd_Replay"></MediaElement>
        </DockPanel>
        <DockPanel Grid.Row="3" Grid.RowSpan="2" Grid.Column="1" Grid.ColumnSpan="2">
            <Label Foreground="Orange" DockPanel.Dock="Top" HorizontalAlignment="Left" >中国油气田分布</Label>
            <windowspresentation:GMapControl Name="gmap">

            </windowspresentation:GMapControl>
        </DockPanel>
        <DockPanel Grid.Row="1" Grid.Column="3"  Grid.ColumnSpan="4" Grid.RowSpan="4">
            <Label Name="Label_oilInfo" Foreground="Orange" DockPanel.Dock="Top" HorizontalAlignment="Center" FontSize="12">
                随钻测井曲线监控
            </Label>
            <Grid>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="2*"/>
                    <ColumnDefinition Width="5*"/>
                    <ColumnDefinition Width="5*"/>
                    <ColumnDefinition Width="5*"/>
                </Grid.ColumnDefinitions>
                <oxy:PlotView Name="pv_depth" Grid.Column="0">
                    <oxy:PlotView.Background>
                        <SolidColorBrush Color="White" Opacity="0"/>
                    </oxy:PlotView.Background>
                </oxy:PlotView>
                <oxy:PlotView Name="pv_log1" Grid.Column="1">
                    <oxy:PlotView.Background>
                        <SolidColorBrush Color="White" Opacity="0"/>
                    </oxy:PlotView.Background>
                </oxy:PlotView>
                <oxy:PlotView Name="pv_log2" Grid.Column="2">
                    <oxy:PlotView.Background>
                        <SolidColorBrush Color="White" Opacity="0"/>
                    </oxy:PlotView.Background>
                </oxy:PlotView>
                <oxy:PlotView Name="pv_log3" Grid.Column="3">
                    <oxy:PlotView.Background>
                        <SolidColorBrush Color="White" Opacity="0"/>
                    </oxy:PlotView.Background>
                </oxy:PlotView>
            </Grid>
        </DockPanel>
        <Button x:Name="button_recover" Grid.Column="3" Click="button_recover_Click" Content="" BorderThickness="0,0,0,0" Margin="10">
            <Button.Background>
                <ImageBrush ImageSource="/恢复.png" Stretch="Uniform" Opacity="0.6"/>
            </Button.Background>
            <Button.Foreground>
                <SolidColorBrush Color="Black" Opacity="0.5"/>
            </Button.Foreground>
        </Button>
        <Button x:Name="button_miniSize" Grid.Column="4" Click="button_miniSize_Click" Content="" BorderThickness="0,0,0,0" Margin="10">
            <Button.Background>
                <ImageBrush ImageSource="/最小化.png" Stretch="Uniform" Opacity="0.6"/>
            </Button.Background>
        </Button>
        <Button x:Name="button_fullScreen" Grid.Column="5" Click="button_fullScreen_Click" Content="" BorderThickness="0,0,0,0" Margin="10">
            <Button.Background>
                <ImageBrush ImageSource="/最大化.png" Stretch="Uniform" Opacity="0.6"/>
            </Button.Background>
        </Button>
        <Button x:Name="button_close" Grid.Column="6" Click="button_close_Click" Content="" BorderThickness="0,0,0,0" Margin="10">
            <Button.Background>
                <ImageBrush ImageSource="/关闭.png" Stretch="Uniform" Opacity="0.6"/>
            </Button.Background>
        </Button>

        <Button x:Name="button_connect" Grid.Row="5" Grid.Column="3" Click="button_connect_Click" Content="" BorderThickness="0,0,0,0">
            <Button.Background>
                <ImageBrush ImageSource="/连接.png" Stretch="Uniform" Opacity="0.59"/>
            </Button.Background>
        </Button>
        <Button x:Name="button7" Grid.Row="5" Grid.Column="4" Content=""  BorderThickness="0">
            <Button.Background>
                <ImageBrush ImageSource="/未完成.png" Stretch="Uniform" Opacity="0.6"/>
            </Button.Background>
        </Button>
        <Button x:Name="button8" Grid.Row="5" Grid.Column="5" Content="" BorderThickness="0">
            <Button.Background>
                <ImageBrush ImageSource="/未完成.png" Opacity="0.6" Stretch="Uniform"/>
            </Button.Background>
        </Button>
        <Button x:Name="button9" Grid.Row="5" Grid.Column="6" Content="" BorderThickness="0">
            <Button.Background>
                <ImageBrush Stretch="Uniform" ImageSource="/未完成.png" Opacity="0.6"/>
            </Button.Background>
        </Button>
    </Grid>
</Window>

3.2 参数模型类(DataPars.cs)

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace LoggingScreen
{
    /// <summary>
    /// 专门用于存储数据的类
    /// </summary>
    public class DataPars
    {
        /// <summary>
        /// 油田名称列表
        /// </summary>
        public string[] FieldNames { get; set; }

        /// <summary>
        /// 油气当量列表
        /// </summary>
        public double[] Productions {  get; set; }

        /// <summary>
        /// 油气类型
        /// </summary>
        public string[] TypeNames { get; set; }

        /// <summary>
        /// 类型占比
        /// </summary>
        public double[] TypePersents { get; set; }

        /// <summary>
        /// 交易日期
        /// </summary>
        public DateTime[] ShareData { get; set; }

        /// <summary>
        /// 开盘价
        /// </summary>
        public double[] OpenPrice { get; set; }

        /// <summary>
        /// 收盘价
        /// </summary>
        public double[] ClosePrice { get; set; }    

        /// <summary>
        /// 最高价
        /// </summary>
        public double[] HighPrice { get; set; }

        /// <summary>
        /// 最低价
        /// </summary>
        public double[] LowPrice { get; set;}

        /// <summary>
        /// 等值线 X坐标位置
        /// </summary>
        public double[] XScale { get; set; }

        /// <summary>
        /// 等值线y坐标位置
        /// </summary>
        public double[] YScale { get; set;}

        /// <summary>
        /// 饱和度值 等值线坐标(x,y)上的值
        /// </summary>
        public double[,] Values { get; set; }

        /// <summary>
        /// 油田信息
        /// </summary>
        public List<OilField> OilFields { get; set; }

        /// <summary>
        /// 曲线信息字典
        /// </summary>
        public Dictionary<string,LogCurve> LogCurveDic { get; set; }
    }
}

3.3 曲线信息类(LogCurve.cs)

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace LoggingScreen
{
    /// <summary>
    /// 描述测井曲线信息类
    /// </summary>
    public class LogCurve
    {
        /// <summary>
        /// 曲线名字
        /// </summary>
        public string Name { get; set; }

        /// <summary>
        /// 曲线单位
        /// </summary>
        public string Unit { get; set; }

        /// <summary>
        /// 起始深度
        /// </summary>
        public double TopDep { get; set; }

        /// <summary>
        /// 终止深度
        /// </summary>
        public double BotDep { get; set; }

        /// <summary>
        /// 采样间隔
        /// </summary>
        public double Spacing { get; set; }

        /// <summary>
        /// 采样点数
        /// </summary>
        public int PointsNum { get; set; }

        /// <summary>
        /// 曲线值
        /// </summary>
        public double[] Values { get; set; }
    }
}

3.4 油田信息类(OilField)

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace LoggingScreen
{
    /// <summary>
    /// 油田信息类
    /// </summary>
    public class OilField
    {
        /// <summary>
        /// 油田名字
        /// </summary>
        public string Name { get; set; }

        /// <summary>
        /// 油田所在中心城市名字
        /// </summary>
        public string CityName { get; set; }

        /// <summary>
        /// 油田位置经度坐标
        /// </summary>
        public double Longitude { get; set; }
        
        /// <summary>
        /// 油田位置纬度坐标
        /// </summary>
        public double Latitude { get; set; }

        /// <summary>
        /// 油田的当期产量
        /// </summary>
        public double Production { get; set; }
    }
}

3.5 UI后台代码(MainWindow.xaml.cs)

using GMap.NET;
using GMap.NET.MapProviders;
using GMap.NET.WindowsPresentation;
using OxyPlot;
using OxyPlot.Axes;
using OxyPlot.Legends;
using OxyPlot.Series;
using OxyPlot.Wpf;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace LoggingScreen
{
    /// <summary>
    /// MainWindow.xaml 的交互逻辑
    /// </summary>
    public partial class MainWindow : Window
    {
        // 全局参数对象
        DataPars dp = new DataPars();

        public MainWindow()
        {
            InitializeComponent();
        }

        /// <summary>
        /// 连接数据按钮
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void button_connect_Click(object sender, RoutedEventArgs e)
        {
            // 绘制第一个图形 横向柱状图, 全国油气产量前10名
            RankingPlot();
            // 绘制第二个图形,油气类型占比环形图
            DoughnutChartPlot();
            // 绘制第三个图形,原油价格走势k线图 (蜡烛图)
            KLineChartPlot();
            // 绘制第四个图形,等值线图
            IsoLinePlot();
            // 显示监控设备
            ShowMedia();
            // 显示地图
            MapPlot();
        }

        /// <summary>
        /// 绘制地图
        /// </summary>
        private void MapPlot()
        {
            // 指定地图的缓存地址
            string basePath = System.AppDomain.CurrentDomain.BaseDirectory;
            string cachePath = basePath + "MapData";
            gmap.CacheLocation = cachePath;

            // 指定地图的提供着(用的内置的微软的卫星地图) BingSatelLiteMap
            gmap.MapProvider = GMapProviders.CzechGeographicMap;
            // 设置地图初始化信息
            gmap.MinZoom = 2; // 最小缩放级别
            gmap.MaxZoom = 16; // 最大缩放级别
            gmap.Zoom = 4; // 当前缩放级别
            gmap.ShowCenter = true; // 设置显示中心十字
            gmap.DragButton = MouseButton.Right; // 指定拖拽的键(鼠标右键拖拽)
            gmap.Position = new PointLatLng(34.8, 104.7); // 指定当前位置

            // 加载表格数据
            string filePath = AppDomain.CurrentDomain.BaseDirectory + @"Data\\油田位置及当期产量.csv";
            string[] strArray = File.ReadAllLines(filePath, Encoding.Default);
            dp.OilFields = new List<OilField>();
            for (int i = 0; i < strArray.Length - 1; i++)
            {
                string[] strings = strArray[i + 1].Split(',');
                OilField of = new OilField();
                of.Name = strings[0];
                of.Longitude = double.Parse(strings[1]);
                of.Latitude = double.Parse(strings[2]);
                of.Production = double.Parse(strings[3]);
                of.CityName = strings[4];
                dp.OilFields.Add(of);
            }

            // 标注数据到地图上
            // 首先清空原来的数据
            gmap.Markers.Clear();

            // 定义显示的点的直径的最大值和最小值
            double showMin = 5.0;
            double showMax = 30.0;
            double proMin = 999999; // 定义产量的最小值
            double proMax = -999999; // 定义产量的最大值
            for (int i = 0; i < dp.OilFields.Count; i++)
            {
                if (dp.OilFields[i].Production < proMin)
                {
                    // 更新最小值
                    proMin = dp.OilFields[i].Production;
                }
                if (dp.OilFields[i].Production > proMax)
                {
                    // 更新最大值
                    proMax = dp.OilFields[i].Production;
                }
            }

            double step = (showMax - showMin) / (proMax - proMin); //步长

            // 2. 通过循环添加每个油田的信息
            for (int i = 0; i < dp.OilFields.Count; i++)
            {
                // 计算显示的大小
                double showSize = showMin + (dp.OilFields[i].Production - proMin) * step;
                // 注意参数的顺序,这里是先纬度再经度
                PointLatLng pll = new PointLatLng(dp.OilFields[i].Latitude, dp.OilFields[i].Longitude);
                GMapMarker gmm = new GMapMarker(pll);
                // 设置显示的形状 以⚪的方式显示
                Ellipse e = new Ellipse();
                e.Width = showSize;
                e.Height = showSize;
                e.Fill = new RadialGradientBrush(Colors.Blue, Colors.Red); // 设置填充色
                // 设置当鼠标停留在点上时候,显示的信心
                e.ToolTip = dp.OilFields[i].Name + ": " + dp.OilFields[i].Production.ToString() + "万吨";
                // 添加鼠标左键单击点时的事件,绑定
                e.MouseDown += E_MouseDown;
                // 设置鼠标移动到点上时鼠标的形状改变
                e.Cursor = Cursors.Hand;
                // 设置选中一个点后,其他数据区实时显示这个油田的信息(Binding)
                e.Tag = dp.OilFields[i];
                gmm.Shape = e;
                gmap.Markers.Add(gmm);  
            }

        }

        /// <summary>
        /// 点击信息点时的操作
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void E_MouseDown(object sender, MouseButtonEventArgs e)
        {
            // 当点击另外一个点时,恢复上一个点的状态
            for (int i = 0; i < gmap.Markers.Count; i++)
            {
                (gmap.Markers[i].Shape as Ellipse).StrokeThickness = 0;
            }
            Ellipse eNow = sender as Ellipse;
            eNow.Stroke = Brushes.GreenYellow;
            eNow.StrokeThickness = 3;

            OilField ofNow = (OilField)eNow.Tag;
            Label_oilInfo.Content = ofNow.Name + ":随钻测井曲线监控";

            // 画出当前选中油田的测井曲线
            PlotLWDCurves(ofNow);
        }

        /// <summary>
        /// 绘制ofNow的随钻测井曲线
        /// </summary>
        /// <param name="ofNow">OilField ofNow</param>
        private void PlotLWDCurves(OilField ofNow)
        {
            // step1 初始化测井曲线字典
            dp.LogCurveDic = new Dictionary<string, LogCurve>();
            // step2 获取文件所在路径
            string basePath = AppDomain.CurrentDomain.BaseDirectory;
            string filePath = "";
            // step3 判断选中的油田,
            switch (ofNow.Name)
            {
                case "塔里木油田":
                    {
                        filePath = basePath + @"Data\\测井曲线_塔里木油田.csv";
                    };
                    break;
                case "长庆油田":
                    {
                        filePath = basePath + @"Data\\测井曲线_长庆油田.csv";
                    };
                    break;
                case "南海西部油田":
                    {
                        filePath = basePath + @"Data\\测井曲线_南海西部油田.csv";
                    };
                    break;
                case "南海东部油田":
                    {
                        filePath = basePath + @"Data\\测井曲线_南海东部油田.csv";
                    };
                    break;
                case "胜利油田":
                    {
                        filePath = basePath + @"Data\\测井曲线_胜利油田.csv";
                    };
                    break;
                case "延长石油":
                    {
                        filePath = basePath + @"Data\\测井曲线_延长石油.csv";
                    };
                    break;
                case "渤海油田":
                    {
                        filePath = basePath + @"Data\\测井曲线_渤海油田.csv";
                    };
                    break;
                case "新疆油田":
                    {
                        filePath = basePath + @"Data\\测井曲线_新疆油田.csv";
                    };
                    break;
                case "西南油气田":
                    {
                        filePath = basePath + @"Data\\测井曲线_西南油气田.csv";
                    };
                    break;
                case "大庆油田":
                    {
                        filePath = basePath + @"Data\\测井曲线_大庆油田.csv";
                    };
                    break;
                default:
                    filePath = basePath + @"Data\\测井曲线_默认.csv";
                    break;
            }
            // step4 取出文件中数据
            string[] strArray = File.ReadAllLines(filePath, Encoding.UTF8);
            string[] nameStr = strArray[0].Split(','); // 拆分第一行,第一行是曲线的名字
            for (int i = 0; i < nameStr.Length-1; i++)
            {
                LogCurve lc = new LogCurve();
                lc.Name = nameStr[i+1];
                lc.Values = new double[strArray.Length - 1]; // 已经知道了值的个数,就先分配内存了
                lc.TopDep = double.Parse(strArray[1].Split(',').First()); // 第二行第一个是起始深度
                lc.BotDep = double.Parse(strArray.Last().Split(',').First()); // 最后一行第一个是起始深度
                lc.PointsNum = strArray.Length - 1;  // 采样点数 = 文件总行数 - 1
                lc.Spacing = (lc.BotDep - lc.TopDep) / (lc.PointsNum - 1); // 采样间隔

                // 添加到曲线字典中
                dp.LogCurveDic.Add(lc.Name, lc); 
            }

            // step5 读出数据
            for (int i = 0; i < strArray.Length-1; i++)
            {
                string[] valueStr = strArray[i + 1].Split(',');
                double depNow = double.Parse(valueStr[0]);  // 每一行的第一个数就是当前深度
                // 开始读取每一行的值(重点检查)
                for (int j = 0; j < valueStr.Length-1; j++)
                {
                    LogCurve lcNow =  dp.LogCurveDic.ElementAt(j).Value;
                    lcNow.Values[i] = double.Parse(valueStr[j + 1]);
                }
            }

            // 深度绘制
            pv_depth.Model = new PlotModel();  // 1 初始化一个model
            //pv_depth.Model.PlotAreaBorderThickness = new OxyThickness(1);  // 去除边框
            pv_depth.Model.PlotAreaBorderColor = OxyColors.GreenYellow;
            
            LinearAxis la = new LinearAxis();  // 2 只用添加一个线性的y轴
            double depMin = dp.LogCurveDic.First().Value.TopDep; // 获取起始深度
            double depMax = dp.LogCurveDic.First().Value.BotDep; // 获取终止深度
            la.Position = AxisPosition.Right; // 设置放置的位置在右边
            la.Minimum = depMin;  // 设置轴的最小深度
            la.Maximum = depMax;  // 设置轴的最大深度
            la.StartPosition = 1;  // 因为深度是往下变大的,0是正常向上,1是往下变大
            la.EndPosition = 0;
            la.TicklineColor = OxyColors.GreenYellow; // 设置轴的颜色
            la.TextColor = OxyColors.GreenYellow; // 设置标识的颜色
            
            pv_depth.Model.Axes.Add(la);  // 把轴放到model里
            pv_depth.Model.InvalidatePlot(true); // 刷新一下

            // 岩性组曲线绘制(GR SP CAL)
            pv_log1.Model = new PlotModel();
            pv_log1.Model.PlotAreaBorderThickness = new OxyThickness(0); // 去除绘图的边框
            LinearAxis la1 = new LinearAxis();  // 添加岩性组的y轴
            la1.Position = AxisPosition.Right; 
            la1.StartPosition = 1;
            la1.EndPosition = 0;
            la1.IsAxisVisible = false; //左边已经有深度轴了,就不让这个轴显示
            pv_log1.Model.Axes.Add(la1);

            PlotLogCurve(pv_log1, dp.LogCurveDic["GR"], true, OxyColors.AliceBlue);
            PlotLogCurve(pv_log1, dp.LogCurveDic["SP"], true, OxyColors.Red);
            PlotLogCurve(pv_log1, dp.LogCurveDic["CAL"], true, OxyColors.Yellow);

            // 添加图例
            Legend l1 = new Legend() { LegendPosition = LegendPosition.TopCenter,LegendTextColor= OxyColors.GreenYellow };
            l1.LegendPlacement = LegendPlacement.Outside; // 把图例放置在外边
            pv_log1.Model.Legends.Add(l1);

            pv_log1.Model.InvalidatePlot(true);

            // 孔隙度组曲线绘制(GR SP CAL)
            pv_log2.Model = new PlotModel();
            pv_log2.Model.PlotAreaBorderThickness = new OxyThickness(0); // 去除绘图的边框
            LinearAxis la2 = new LinearAxis();  // 添加岩性组的y轴
            la2.Position = AxisPosition.Right;
            la2.StartPosition = 1;
            la2.EndPosition = 0;
            la2.IsAxisVisible = false; //左边已经有深度轴了,就不让这个轴显示
            pv_log2.Model.Axes.Add(la2);

            PlotLogCurve(pv_log2, dp.LogCurveDic["DEN"], true,OxyColors.Blue);
            PlotLogCurve(pv_log2, dp.LogCurveDic["AC"], true,OxyColors.Red);
            PlotLogCurve(pv_log2, dp.LogCurveDic["CNL"], true,OxyColors.Orange);

            Legend l2 = new Legend() { LegendPosition = LegendPosition.TopCenter, LegendTextColor = OxyColors.GreenYellow };
            l2.LegendPlacement = LegendPlacement.Outside; // 把图例放置在外边
            pv_log2.Model.Legends.Add(l2);
            pv_log2.Model.InvalidatePlot(true);

            // 电阻率组曲线绘制(GR SP CAL)
            pv_log3.Model = new PlotModel();
            pv_log3.Model.PlotAreaBorderThickness = new OxyThickness(0); // 去除绘图的边框
            LinearAxis la3 = new LinearAxis();  // 添加岩性组的y轴
            la3.Position = AxisPosition.Right;
            la3.StartPosition = 1;
            la3.EndPosition = 0;
            la3.IsAxisVisible = false; //左边已经有深度轴了,就不让这个轴显示
            pv_log3.Model.Axes.Add(la3);

            PlotLogCurve(pv_log3, dp.LogCurveDic["RILD"], false,0.1,100,OxyColors.Brown);
            PlotLogCurve(pv_log3, dp.LogCurveDic["RILM"], false, 0.1, 100, OxyColors.MistyRose);
            PlotLogCurve(pv_log3, dp.LogCurveDic["RFOC"], false, 0.1, 100, OxyColors.Chartreuse);

            Legend l3 = new Legend() { LegendPosition = LegendPosition.TopCenter, LegendTextColor = OxyColors.GreenYellow };
            l3.LegendPlacement = LegendPlacement.Outside; // 把图例放置在外边
            pv_log3.Model.Legends.Add(l3);
            pv_log3.Model.InvalidatePlot(true);
        }

        /// <summary>
        /// 绘制测井曲线的方法
        /// </summary>
        /// <param name="pv_log">绘图位置</param>
        /// <param name="logCurve">曲线</param>
        /// <param name="isLinear">是否为线性轴</param>
        /// <param name="color">绘图颜色</param>
        private void PlotLogCurve(PlotView pv_log, LogCurve logCurve, bool isLinear,OxyColor color)
        {
            // 添加x轴
            if (isLinear)
            {
                LinearAxis la = new LinearAxis();  // 线性轴
                la.Position = AxisPosition.Bottom;
                la.Key = logCurve.Name; // 因为不同的曲线有不同的刻度,所以要对刻度进行一个标识
                la.IsAxisVisible = false; // 隐藏
                pv_log.Model.Axes.Add(la);
            }
            else
            {
                LogarithmicAxis la = new LogarithmicAxis();  // 对数轴
                la.Position = AxisPosition.Bottom;
                la.Key = logCurve.Name; // 因为不同的曲线有不同的刻度,所以要对刻度进行一个标识
                la.IsAxisVisible = false; // 隐藏
                pv_log.Model.Axes.Add(la);
            }
            // 添加曲线
            LineSeries ls = new LineSeries();
            ls.Title = logCurve.Name;
            ls.Color = color;
            for (int i = 0; i < logCurve.PointsNum; i++)
            {
                double x = logCurve.Values[i]; // x坐标上是值
                double y = logCurve.TopDep + logCurve.Spacing * i; // y坐标上是深度 = 起始深度+采样间隔×点数
                DataPoint dp = new DataPoint(x, y);
                ls.Points.Add(dp);
            }
            ls.XAxisKey = logCurve.Name;
            pv_log.Model.Series.Add(ls);
        }

        /// <summary>
        /// 绘制测井曲线的方法重载(适用于电阻率,含指定的x轴的最大最小刻度)
        /// </summary>
        /// <param name="pv_log">画板</param>
        /// <param name="logCurve">曲线</param>
        /// <param name="isLinear">是否为线性</param>
        /// <param name="xMin">x轴最小刻度</param>
        /// <param name="xMax">x轴最大刻度</param>
        /// <param name="color">绘图颜色</param>
        private void PlotLogCurve(PlotView pv_log, LogCurve logCurve, bool isLinear,double xMin,double xMax,OxyColor color)
        {
            // 添加x轴
            if (isLinear)
            {
                LinearAxis la = new LinearAxis();  // 线性轴
                la.Position = AxisPosition.Bottom;
                la.Key = logCurve.Name; // 因为不同的曲线有不同的刻度,所以要对刻度进行一个标识
                la.IsAxisVisible = false; // 隐藏
                la.Minimum = xMin;
                la.Maximum = xMax;
                pv_log.Model.Axes.Add(la);
            }
            else
            {
                LogarithmicAxis la = new LogarithmicAxis();  // 对数轴
                la.Position = AxisPosition.Bottom;
                la.Key = logCurve.Name; // 因为不同的曲线有不同的刻度,所以要对刻度进行一个标识
                la.IsAxisVisible = false; // 隐藏
                la.Minimum = xMin;
                la.Maximum = xMax;
                pv_log.Model.Axes.Add(la);
            }
            // 添加曲线
            LineSeries ls = new LineSeries();
            ls.Title = logCurve.Name;
            ls.Color = color;
            for (int i = 0; i < logCurve.PointsNum; i++)
            {
                double x = logCurve.Values[i]; // x坐标上是值
                double y = logCurve.TopDep + logCurve.Spacing * i; // y坐标上是深度 = 起始深度+采样间隔×点数
                DataPoint dp = new DataPoint(x, y);
                ls.Points.Add(dp);
            }
            ls.XAxisKey = logCurve.Name;
            pv_log.Model.Series.Add(ls);
        }

        /// <summary>
        /// 横向柱状图的绘制(棒状图)
        /// </summary>
        private void RankingPlot()
        {
            // step1 获取程序的启动路径,就是debug里边
            string basePath = System.AppDomain.CurrentDomain.BaseDirectory;
            // 由程序启动路径获得csv文件的路径
            string csvfile = basePath + @"Data\\油气产量前10名.csv";

            // step2 读取数据
            string[] strArray = File.ReadAllLines(csvfile, Encoding.Default); // 把csv文件中的数据一行一行的读取出来
            dp.FieldNames = new string[strArray.Length - 1];  // 存放油田名称的数组  第一行是油田名称 油田当量等等..所以总行数减1是油田个数
            dp.Productions = new double[strArray.Length - 1]; // 存放油田产量的数组
            // 开始取数据,循环的次数就是元素的个数
            for (int n = 0; n < strArray.Length - 1; n++)
            {
                // 取出当前行
                string tempStr = strArray[n + 1];
                // 对当前行进行拆分
                string[] strings = tempStr.Split(','); // 长庆油田  6565  万吨
                // 油田名 是拆分后的第一个元素
                dp.FieldNames[n] = strings[0];
                // 油气当量 是拆分后的第二个元素
                dp.Productions[n] = double.Parse(strings[1]);
            }

            // step3 绘制图形
            // 首先给pv_production初始化一个model
            pv_production.Model = new PlotModel();
            // pv_production.Model.Title = "油气当量前10名";
            pv_production.Model.PlotAreaBorderThickness = new OxyPlot.OxyThickness(0); //设置边框为0,就是不显示边框
            // 设置背景颜色,可以在UI界面更改
            // pv_production.Model.Background = OxyColors.Black;
            // pv_production.Background = new SolidColorBrush(Colors.Blue); //设置背景颜色
            pv_production.Background.Opacity = 0; // 设置背景颜色的透明度
            
            

            // 分类轴
            CategoryAxis ca = new CategoryAxis();
            // 指定轴的位置
            ca.Position = AxisPosition.Left;
            // 设置分类轴的标签(Labels是可读的属性,只能通过这样塞进去,不能直接赋值) ca.Labels = dp.FieldNames.ToList(); 这样是错误的
            for (int n = 0; n < dp.FieldNames.Length; n++)
            {
                ca.Labels.Add(dp.FieldNames[n]);
            }
            // 设置不显示轴的刻度,但是要显示名称
            // ca.IsAxisVisible = false; // 这种方式会隐藏刻度,也会隐藏名称
            ca.TickStyle = TickStyle.None;
            // 设置y轴显示的颜色
            ca.TextColor = OxyColors.GreenYellow;
            // 把调整好的轴添加到pv_production里边
            pv_production.Model.Axes.Add(ca);

            // 线性轴,用于标注产量的值
            LinearAxis la = new LinearAxis();
            la.Position = AxisPosition.Bottom;
            la.IsAxisVisible = false; // 设置不显示轴的刻度
            
            pv_production.Model.Axes.Add(la);

            // 放数据进去(横状的柱状图)
            BarSeries bs = new BarSeries();
            for (int n = 0; n < dp.FieldNames.Length; n++)
            {
                BarItem bt = new BarItem(dp.Productions[n]);
                bs.Items.Add(bt);
            }
            bs.LabelFormatString = "{0} 吨"; // 设置横向柱状图信息
            bs.LabelPlacement = LabelPlacement.Inside;
            bs.FillColor = OxyColors.CadetBlue;
            bs.TextColor = OxyColors.MediumVioletRed;
            pv_production.Model.Series.Add(bs);

            // step5 刷新一下视图
            pv_production.Model.InvalidatePlot(true);
        }

        /// <summary>
        /// 油气类型占比环形图
        /// </summary>
        private void DoughnutChartPlot()
        {
            // step1 获得文件路径
            string basePath = System.AppDomain.CurrentDomain.BaseDirectory;
            string csvfile = basePath + @"Data\\不同油气类型占比.csv";

            // step2 读取数据
            string[] strArray = File.ReadAllLines(csvfile,Encoding.Default);
            dp.TypeNames = new string[strArray.Length - 1];
            dp.TypePersents = new double[strArray.Length - 1];
            for (int i = 0; i < strArray.Length - 1; i++)
            {
                string strTemp = strArray[i+1];
                string[] strs = strTemp.Split(',');
                dp.TypeNames[i] = strs[0];
                dp.TypePersents[i] = double.Parse(strs[1]);
            }

            // step3 初始化一个新Model
            pv_HChart.Model = new PlotModel();
            pv_HChart.Model.PlotAreaBorderThickness = new OxyThickness(0);
            // pv_HChart.Model.Background = OxyColor.FromArgb(0,0, 0, 0); // 第一个参数是透明度,255是100%,后面三个参数是rgb
            pv_HChart.Model.PlotMargins = new OxyThickness(5); //设置绘图时的边距,向里收缩5个单位
            pv_HChart.Background.Opacity = 0; // 设置背景颜色的透明度
            
            // stpe4 添加饼图
            PieSeries ps = new PieSeries();
            for (int i = 0; i < dp.TypeNames.Length; i++)
            {
                PieSlice bt = new PieSlice(dp.TypeNames[i], dp.TypePersents[i]); // 饼图的组成是一个一个扇形
                ps.Slices.Add(bt);
            }

            // 修改饼图为环形图
            ps.InnerDiameter = 0.45; //默认是1,可以自己修改一下看一下效果
            ps.InsideLabelFormat = "{0}"; // 设置内标签  0是数值,1是名称
            ps.OutsideLabelFormat = "{1}";  // 设置外标签
            ps.TextColor = OxyColors.GreenYellow; // 设置标签颜色
            

            // step5 添加饼图到Model的数据序列里
            pv_HChart.Model.Series.Add(ps);

            // step6 刷新
            pv_HChart.Model.InvalidatePlot(true);
        }
        
        /// <summary>
        /// 原油价格走势k线图 (蜡烛图)
        /// </summary>
        private void KLineChartPlot()
        {
            // step1 获取文件所在路径
            string basePath = System.AppDomain.CurrentDomain.BaseDirectory;
            string csvPath = basePath + @"Data\\石油价格变化.csv";

            // step2 读取数据
            string[] strArray = File.ReadAllLines(csvPath,Encoding.Default);
            int dataLines = strArray.Length - 1;
            dp.ShareData = new DateTime[dataLines];
            dp.OpenPrice = new double[dataLines];
            dp.ClosePrice = new double[dataLines];
            dp.HighPrice = new double[dataLines];
            dp.LowPrice = new double[dataLines];
            for (int i = 0; i < dataLines; i++)
            {
                string strTemp = strArray[i + 1];
                string[] strs = strTemp.Split(new char[] { ',' });  // 这种拆分方式会严重影响性能
                dp.ShareData[i] = DateTime.Parse(strs[0]);
                dp.OpenPrice[i] = double.Parse(strs[1]);
                dp.ClosePrice[i] = double.Parse(strs[2]);
                dp.HighPrice[i] = double.Parse(strs[3]);
                dp.LowPrice[i] = double.Parse(strs[4]);
            }

            // step3 添加新Model
            pv_KLine.Model = new PlotModel();
            pv_KLine.Model.PlotAreaBorderThickness = new OxyThickness(0);
            // pv_KLine.Model.PlotAreaBackground = OxyColor.FromArgb(0, 0, 0, 0);
            // pv_KLine.Model.PlotMargins = new OxyThickness(1);
            pv_KLine.Background.Opacity = 0; // 设置背景颜色的透明度

            // step4 设置x轴
            DateTimeAxis ca = new DateTimeAxis();
            ca.Position = AxisPosition.Bottom;
            ca.TextColor = OxyColors.GreenYellow;
            ca.TickStyle = TickStyle.None;
            pv_KLine.Model.Axes.Add(ca); //添加到Model

            // step5 设置y轴
            LinearAxis la = new LinearAxis();
            la.Position = AxisPosition.Left;
            la.IsAxisVisible = false;
            pv_KLine.Model.Axes.Add(la);


            // stpe6 绘制蜡烛图
            CandleStickSeries css = new CandleStickSeries();
            for (int i = 0; i < dataLines; i++)
            {
                // 看css.Items是什么类型就往里塞什么类型(要把日期转换一下,ToOADate()后才是double类型)
                HighLowItem hli = new HighLowItem(dp.ShareData[i].ToOADate(), dp.HighPrice[i], dp.LowPrice[i], dp.OpenPrice[i], dp.ClosePrice[i]);
                css.Items.Add(hli);
            }

            // step7 添加到model并刷新
            pv_KLine.Model.Series.Add(css);
            pv_KLine.Model.InvalidatePlot(true);
        }

        /// <summary>
        /// 饱和度等值线图
        /// </summary>
        private void IsoLinePlot()
        {
            // step1 获取文件路径
            string basePath = System.AppDomain.CurrentDomain.BaseDirectory;
            string filePath = basePath + @"Data//重油藏饱和度等值线.csv";

            // step2 读取数据
            string[] strArray = File.ReadAllLines(filePath);

            // step3 初始化存储结构,赋予相应个数的存储单元(这里更麻烦一点,因为数据在csv中的存储结构发生了变化)
            dp.YScale = new double[strArray.Length - 1]; //y轴坐标的个数就是数据的行数减一
            string[] strX = strArray[0].Split(',');
            dp.XScale = new double[strX.Length - 1]; // x轴坐标的个数是第一行拆出来的字符串的个数减一
            dp.Values = new double[dp.YScale.Length, dp.XScale.Length];

            // step4 存值进去(重点检查,可能会出错)
            for (int i = 0; i < dp.XScale.Length; i++)
            {
                dp.XScale[i] = double.Parse(strX[i + 1]); // 给X轴存值
            }
            for (int i = 0; i < dp.YScale.Length; i++)
            {
                string[] strTemp = strArray[i + 1].Split(',');
                dp.YScale[i] = double.Parse(strTemp[0]);  // 给y轴存值
                for (int j = 0; j < dp.XScale.Length; j++)
                {
                    dp.Values[i, j] = double.Parse(strTemp[j + 1]);
                }
            }

            // step5 初始化一个新model
            pv_isoLine.Model = new PlotModel();
            pv_isoLine.Model.PlotAreaBorderThickness = new OxyThickness(0);
            // pv_isoLine.Model.Background = OxyColor.FromArgb(0, 0, 0, 0);
            pv_isoLine.Background.Opacity = 0; // 设置背景颜色的透明度


            // step6 添加笛卡尔坐标轴 x轴
            LinearAxis lax = new LinearAxis();
            lax.Position = AxisPosition.Bottom;
            lax.TextColor = OxyColors.GreenYellow;
            pv_isoLine.Model.Axes.Add(lax);

            // step7 添加笛卡尔坐标轴 y轴
            LinearAxis lay = new LinearAxis();
            lay.Position = AxisPosition.Left;
            lay.TextColor = OxyColors.GreenYellow;
            pv_isoLine.Model.Axes.Add(lay);


            // step9 添加等值线图
            ContourSeries cs = new ContourSeries();
            cs.Data = dp.Values;
            cs.RowCoordinates = dp.XScale; 
            cs.ColumnCoordinates = dp.YScale;

            //
            int count = 30; //设置网格数,这里相当于把数值等分为count份
            cs.ContourLevels = new double[count]; // 也就是画15根等值线
            double step = (dp.Values.Max2D() - dp.Values.Min2D()) / (count - 1); //步长 = (最大值-最小值)/(等值线数-1)
            for (int i = 0; i < count; i++)
            {
                cs.ContourLevels[i] = (i + 0.5) * step;
            }
            cs.ContourColors = OxyPalettes.Jet(count).Colors.ToArray();

            // step8 添加颜色轴
            LinearColorAxis lca = new LinearColorAxis();
            lca.Palette = OxyPalettes.Jet(count);
            lca.Position = AxisPosition.Right;
            pv_isoLine.Model.Axes.Add(lca);

            // step9 加到model里并刷新
            pv_isoLine.Model.Series.Add(cs);
            pv_isoLine.Model.InvalidatePlot(true);
        }


        /// <summary>
        /// 双击 放大图片
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void pv_production_MouseDoubleClick(object sender, MouseButtonEventArgs e)
        {
            // 获得临时的绘图面板
            PlotView pvTemp = sender as PlotView;
            // 找到pvTemp的上一级控件(因为拿到过后放大后要还原,就是还给他的父级控件)
            DockPanel dp = (DockPanel)pvTemp.Parent;
            dp.Children.Clear();
            // 再把他弹出来
            Window newWindow = new Window();
            Grid newGrid = new Grid();
            newGrid.Background = Brushes.Black;
            newGrid.Children.Add(pvTemp);
            newWindow.Content = newGrid;
            newWindow.ShowDialog();

            // 关闭后还给父级控件
            newGrid.Children.Clear();
            dp.Children.Add(pvTemp);
        }

        /// <summary>
        /// 全屏按钮
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void button_fullScreen_Click(object sender, RoutedEventArgs e)
        {
            // 去掉原来的标题头
            this.WindowStyle = WindowStyle.None;
            this.WindowState = WindowState.Maximized;
        }

        /// <summary>
        /// 恢复按钮
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void button_recover_Click(object sender, RoutedEventArgs e)
        {
            this.WindowStyle = WindowStyle.ThreeDBorderWindow;
            this.WindowState = WindowState.Normal;
        }

        /// <summary>
        /// 最小化按钮
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void button_miniSize_Click(object sender, RoutedEventArgs e)
        {
            this.WindowState = WindowState.Minimized;
        }

        /// <summary>
        /// 关闭按钮
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void button_close_Click(object sender, RoutedEventArgs e)
        {
            this.Close();
        }

        /// <summary>
        /// 显示监控设备
        /// </summary>
        private void ShowMedia()
        {
            string basePath = System.AppDomain.CurrentDomain.BaseDirectory;
            string Monitor1 = basePath + @"Data\\监控设备1.mp4";
            string Monitor2 = basePath + @"Data\\监控设备2.mp4";
            string Monitor3 = basePath + @"Data\\监控设备3.mp4";
            string Monitor4 = basePath + @"Data\\监控设备4.mp4";
            VideoPlay(media1, Monitor1);
            VideoPlay(media2, Monitor2);
            VideoPlay(media3, Monitor3);
            VideoPlay(media4, Monitor4);
        }
        /// <summary>
        /// 提取出来的播放监控的方法
        /// </summary>
        /// <param name="me">监控设备名</param>
        /// <param name="path">监控所在路径</param>
        public void VideoPlay(MediaElement me,string path)
        {
            me.Source = new Uri(path);
            me.Stretch = Stretch.Fill; // 设置一下画面填充
            me.LoadedBehavior = MediaState.Manual; // 把播放模式设置为手动的,就可以循环播放了
            me.Volume = 0; //设置播放的声音为0
            me.Play();
        }

        // 重复播放事件
        private void MediaEnd_Replay(object sender, RoutedEventArgs e)
        {
            MediaElement me = sender as MediaElement;
            me.Position = TimeSpan.Zero; //回到最开始的时刻
            me.Play();
        }
    }
}

4 功能介绍

// TODO 2023.0828

5 总结

        本次实训还有很多地方值得改进,如在绘图部分很多地方的代码是相同的,还有几个按钮的功能没有实现,希望在以后闲下来的时候完成这个部分的优化。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
WPF(Windows Presentation Foundation)是一种用于创建并展示用户界面的技术。它可以被用于绘制大屏可视化界面,以满足特定的需求。 在WPF中,我们可以使用XAML(可扩展应用程序标记语言)来定义界面的外观和布局,以及使用C#或其他支持.NET编程语言来处理界面的逻辑和数据。因此,我们可以轻松地创建出功能丰富、交互性强的大屏可视化界面。 在绘制大屏可视化界面时,WPF提供了多种强大的图形和绘制功能。我们可以使用各种图形元素(如矩形、椭圆、文本等)来创建界面中的图形组件,也可以使用画笔、刷子和渐变来设置元素的颜色和填充方式。此外,WPF还支持高级的效果和动画特性,可以为界面添加视觉上的吸引力和交互性。 另外,WPF还提供了数据绑定和模板功能,以简化界面与数据的关联和展示。我们可以绑定数据源到界面上的控件,实现数据的实时显示和更新。同时,通过使用模板,我们可以定义可重复使用的界面部件,大大提高界面的开发效率。 对于大屏可视化界面而言,WPF还具有自适应和自定义布局的能力。我们可以使用栅格、面板和容器等布局控件来实现灵活的界面排列,以适应不同分辨率或屏幕尺寸。此外,WPF还支持自定义控件的创建和样式的定义,可以根据需要完全定制界面的外观和行为。 总而言之,WPF提供了一套强大的工具和功能,可以帮助我们绘制出功能丰富、交互性强的大屏可视化界面。其灵活性和可定制性使得开发人员可以根据具体需求去实现各种复杂的视觉效果和交互功能。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

亲爱的老吉先森

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值