WinForm常用控件

一 关闭最小化

private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
    if (e.CloseReason == CloseReason.UserClosing)
    {
        e.Cancel = true;
        this.WindowState = FormWindowState.Minimized;
    }
}

二 退出程序接口

System.Environment.Exit(0);

三 最小化或关闭时隐藏到系统托盘

1 添加托盘图标控件NotifyIcon

  直接从工具箱中拖动添加即可。

2 添加(重写)窗口尺寸变动函数Form1_Resize

private void Form1_Resize(object sender, EventArgs e)
{
    if (this.WindowState == FormWindowState.Minimized)    //最小化到系统托盘
    {
        NotifyIcon1.Visible = true;    //显示托盘图标
        this.Hide();    //隐藏窗口
    }
}

3 添加(重写)关闭窗口事件

private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
    //注意判断关闭事件Reason来源于窗体按钮,否则用菜单退出时无法退出!
    if (e.CloseReason == CloseReason.UserClosing)
    {
        e.Cancel = true; //取消"关闭窗口"事件
        this.WindowState = FormWindowState.Minimized;//使关闭时窗口向右下角缩小的效果
        NotifyIcon1.Visible = true;
        this.Hide();
        return;
    }
}

4 添加双击托盘图标事件(双击显示窗口)

private void NotifyIcon1_MouseDoubleClick(object sender, MouseEventArgs e)
{
    NotifyIcon1.Visible = false;
    this.Show();
    WindowState = FormWindowState.Normal;
    this.Focus();
}

  PS:解决了设置“关闭时隐藏到系统托盘”时,点击“退出”菜单也无法退出的问题。——无论是用 this.Close(); 还是 Application.Exit(); 都无法退出!——解决办法是,在关闭窗口事件中判断关闭原因/来源 (e.CloseReason),若为 CloseReason.UserClosing 则为点击了窗口右上角的关闭按钮,否则可能是点击了"退出"菜单,则不执行隐藏到托盘的程序。(详见STEP3的if判断)

5 添加托盘图标的右键菜单

本节内容

(具体代码略)
"退出"菜单:Application.Exit();
"显示窗口"菜单:参考STEP4

四 子窗口最大最小按钮

  窗体的 FormBorderStyle 设置为 sizable 才会出现最大/小化按钮。

五 DataGridView

1 点击事件

  DataGridView点击事件并不总是触发,使用 CellClick事件而不是 CellContentClick ,因为当单击任何 部分时触发该事件(不只是它里面的内容)。

2 自动宽度

1、AllCells 调整列宽,以适合该列中的所有单元格的内容,包括标题单元格。
2、AllCellsExceptHeader 调整列宽,以适合该列中的所有单元格的内容,不包括标题单元格。
3、ColumnHeader 调整列宽,以适合列标题单元格的内容。
4、DisplayedCells 调整列宽,以适合当前屏幕上显示的行的列中的所有单元格的内容,包括标题单元格。
5、DisplayedCellsExceptHeader 调整列宽,以适合当前屏幕上显示的行的列中的所有单元格的内容,不包括标题单元格。
6、Fill 调整列宽,使所有列的宽度正好填充控件的显示区域,只需要水平滚动保证列宽在DataGridViewColumn.MinimumWidth 属性值以上。相对列宽由相对 DataGridViewColumn.FillWeight 属性值决定。
7、None 列宽不会自动调整。
8、NotSet 列的大小调整行为从 DataGridView.AutoSizeColumnsMode 属性继承。

  整体自动宽度:Autosizecolumnsmode = file;,特定列固定宽度:AutoSizeMode = None

3 不显示第一列

RowHeadersVisible = false

六 C# TreeView 右键菜单

  方法一:
  在winform中,添加一个contextMenuStrip1,设置TreeView的属性ContextMenuStrip为contextMenuStrip1,并为这个contextMenuStrip1添加几个菜单项,点击每个菜单项,实现其click事件,然后添加treeivew的nodemouseclick事件,类似如下代码:

//右键菜单
private void treeView1_NodeMouseClick(object sender, TreeNodeMouseClickEventArgs e)
{
    if (e.Button != MouseButtons.Right) return;
    if (e.Node.Parent == null || e.Node == null) return;
    treeView1.SelectedNode = e.Node;
    contextMenuStrip1.Show(tv_Roots, e.X, e.Y);
}
void contextMenuStripItem_Click(object sender, EventArgs e)//编辑飞行段
{
}

  方法二:不同节点对应不同的右键菜单

private void tvOneRoot_NodeMouseClick(object sender, TreeNodeMouseClickEventArgs e)
{
    if (e.Button != MouseButtons.Right) return;
    if (e.Node == null) return; //无节点
    if (e.Node.Level == 0) return;
    int nodeType = GetNodeTypeByGuid(e.Node.Name);
    tvOneRoot.SelectedNode = e.Node;
    InitcmsOneRout(nodeType);
    cmsOneRout.Show(tvOneRoot, e.X, e.Y);
}

/// <summary>
/// 初始化cmsOneRoot 从nodetype
/// </summary>
/// <param name="nodetype"></param>
private void InitcmsOneRout(int nodetype)
{
    cmsOneRout = new ContextMenuStrip();
    if (nodetype == -1)
    {
        ToolStripMenuItem tmiEditRoutStation = new ToolStripMenuItem("编辑飞行站点");
        tmiEditRoutStation.Click += new EventHandler(tmiEditRoutStation_Click);
        cmsOneRout.Items.Add(tmiEditRoutStation);
        ToolStripMenuItem tmiMoveRouteStation = new ToolStripMenuItem("更改位置");
        tmiMoveRouteStation.Click += new EventHandler(tmiMoveRouteStation_Click);
        cmsOneRout.Items.Add(tmiMoveRouteStation);
        ToolStripMenuItem tmiDeleRouteStation = new ToolStripMenuItem("删除飞行站点");
        tmiDeleRouteStation.Click += new EventHandler(tmiDeleRouteStation_Click);
        cmsOneRout.Items.Add(tmiDeleRouteStation);
    }
    else if (nodetype == 0)
    {
        ToolStripMenuItem tmiEditRout = new ToolStripMenuItem("编辑飞行段属性");
        tmiEditRout.Click += new EventHandler(tmiEditRout_Click);
        cmsOneRout.Items.Add(tmiEditRout);
        ToolStripMenuItem tmiDeleteRout = new ToolStripMenuItem("删除飞行段");
        tmiDeleteRout.Click += new EventHandler(tmiDeleteRout_Click);
        cmsOneRout.Items.Add(tmiDeleteRout);
    }
    else if (nodetype == 1)
    {
    }
    else if (nodetype == 2)
    {
    }
    else
    {
    }
}
void tmiEditRout_Click(object sender, EventArgs e)//编辑飞行段
{
    string routId = tvOneRoot.SelectedNode.Name;
    RouteParameter rp = GetRouteParaByGuid(routId);
    FrmPointProperty frm = new FrmPointProperty(rp, 0, 1);
    frm.OnRoutInfoUpdated += new FrmPointProperty.DelegateRouteInfoUpdated(frm_OnRoutInfoUpdated);
    frm.ShowDialog();
}

七 c# winform窗体如何设置才可以不能随意拖动大小

https://www.cnblogs.com/janghe/p/7843677.html

八 滚动条查看PictureBox的大图片

  PictureBox自身没有滚动条,看不到大图片,而Panel自带滚动条,可以用Panel给PictureBox加上滚动条,设置Panel属性AutoScroll为true,设置PictureBox属性SizeMode为AutoSize,这样就可以用滚动条来看PictureBox中的大图片。

九 DockPanel

1 不显示关闭按钮

示例:

imageShow = ImageShow.GetInstance();
imageShow.Show(this.dockPanel1);
imageShow.CloseButton = false;
imageShow.CloseButtonVisible = false;

2 不可浮动

  拖动:如果你想使你的子窗体可以任意拖动,那么你在设置子窗体的DockAreas属性时,保持默认值,不要修改;
  固定:只需设置你窗体的DockAreas为Document就行了。

3 判断Dockpanel中存在多少个子窗体或Contents

foreach (DockContent frm in this.dockPanel1.Contents)
{
     //这样设置后,所有的继承与DockContent的窗体都会被计算在内的
}

4 子窗体布局

  在用DockPanel做MDI程序时,子窗体的初始化布局的实现,比如停靠左边,“日”字型的,有两种方法:

Me.DockPanel1.DockLeftPortion = 0.15'停靠区域占窗口比例

方法1:

frm1.show(DockPanel1, WeifenLuo.WinFormsUI.Docking.DockState.DockTop)
frm1.Show(DockPanel1, WeifenLuo.WinFormsUI.Docking.DockState.DockLeft)
frm1.DockAreas = WeifenLuo.WinFormsUI.Docking.DockAreas.DockLeft

frm2.Show(DockPanel1, WeifenLuo.WinFormsUI.Docking.DockState.DockBottom)
frm2.Show(DockPanel1, WeifenLuo.WinFormsUI.Docking.DockState.DockLeft)
frm2.DockAreas = WeifenLuo.WinFormsUI.Docking.DockAreas.DockLeft 

方法2:

frm1.Show(DockPanel1, WeifenLuo.WinFormsUI.Docking.DockState.DockLeft) 
frm1.DockAreas = WeifenLuo.WinFormsUI.Docking.DockAreas.DockLeft

frm2.Show(frm1.Pane, WeifenLuo.WinFormsUI.Docking.DockAlignment.Bottom, 0.618)
frm2.DockAreas = WeifenLuo.WinFormsUI.Docking.DockAreas.DockLeft

  相对于方法1,方法2可以设置两个子窗口的高度比例,更好用些。

5 示例

  C# 开源控件DockPanel 使用心得:https://blog.csdn.net/gd6321374/article/details/78059501
  C#实现浮动和多标签窗体解决方案—使用Dockpanel:https://blog.csdn.net/zhangyuehua123/article/details/6861401

十 父子窗口传值

  Winform中如何实现父窗体传递数据到子窗体并刷新子窗体:https://www.cnblogs.com/qufly/p/3433195.html

十一 子窗口间传值

https://www.cnblogs.com/wuhuacong/archive/2012/07/06/2578198.html

十二 Log4net

  AssemblyInfo.cs:

using System.Resources;    # line 1

[assembly: NeutralResourcesLanguage("zh-Hans")]    # line 38
[assembly: log4net.Config.XmlConfigurator(Watch = true)]

  App.config:

<configSections>
	<section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net" />
	<!-- For more information on Entity Framework configuration, visit http://go.microsoft.com/fwlink/?LinkID=237468 -->
	<section name="entityFramework" type="System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection, EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" />
</configSections>

<!--log4net配置start-->
<log4net>
  <logger name="Log">
    <level value="INFO" />
    <param name="Encoding" value="utf-8" />
    <appender-ref ref="RollingLog" />
  </logger>
  <logger name="Err">
    <level value="ERROR" />
    <appender-ref ref="RollingErr" />
  </logger>
  <appender name="RollingLog" type="log4net.Appender.RollingFileAppender">
    <file value="Log\" />
    <appendToFile value="true" />
    <rollingStyle value="Date" />
    <datePattern value="yyyyMMdd'.txt'" />
    <!--不加utf-8编码格式,中文字符将显示成乱码-->
    <param name="Encoding" value="utf-8" />
    <staticLogFileName value="false" />
    <layout type="log4net.Layout.PatternLayout">
      <conversionPattern value="记录时间:%d 线程ID:[%thread]- 操作信息:%m%n" />
    </layout>
    <filter type="log4net.Filter.LevelRangeFilter">
      <param name="LevelMin" value="INFO" />
      <param name="LevelMax" value="INFO" />
    </filter>
  </appender>
  <appender name="RollingErr" type="log4net.Appender.RollingFileAppender">
    <file value="Err\" />
    <param name="Encoding" value="utf-8" />
    <appendToFile value="true" />
    <rollingStyle value="Date" />
    <datePattern value="yyyyMMdd'.txt'" />
    <!--不加utf-8编码格式,中文字符将显示成乱码-->
    <param name="Encoding" value="utf-8" />
    <staticLogFileName value="false" />
    <layout type="log4net.Layout.PatternLayout">
      <conversionPattern value="记录时间:%d 线程ID:[%thread]- 错误描述:%m%n" />
    </layout>
    <filter type="log4net.Filter.LevelRangeFilter">
      <param name="LevelMin" value="ERROR" />
      <param name="LevelMax" value="ERROR" />
    </filter>
  </appender>
</log4net>
<!--log4net配置end-->

  LogHelp.cs:

using log4net;
using System.Diagnostics;
using System.Reflection;

namespace Log_Helper
{
    public class LogHelp
    {
        private static readonly ILog logInfo = LogManager.GetLogger("Log");
        private static readonly ILog logErr = LogManager.GetLogger("Err");
        /// <summary>
        /// 记录正常的消息
        /// </summary>
        /// <param name="msg">消息内容</param>
        public static void info(string msg)
        {
            logInfo.Info(msg);
        }
        /// <summary>
        /// 记录异常信息
        /// </summary>
        /// <param name="msg">异常信息内容</param>
        public static void error(string msg)
        {
            StackTrace stackTrace = new StackTrace();
            StackFrame stackFrame = stackTrace.GetFrame(1);
            MethodBase methodBase = stackFrame.GetMethod();
            logErr.Error("类名:" + methodBase.ReflectedType.Name + " 方法名:" + methodBase.Name + " 信息:" + msg);
        }
    }
}

log4net.config

<?xml version="1.0" encoding="utf-8" ?>
<log4net>
	<!--根配置-->
	<root>
		<!--日志级别:可选值: ALL|DEBUG|INFO|WARN|ERROR|FATAL|OFF-->
		<!--OFF: 0-->
		<!--FATAL: FATAL-->
		<!--ERROR: ERROR、FATAL-->
		<!--WARN: WARN、ERROR、FATAL-->
		<!--INFO: INFO、WARN、ERROR、FATAL-->
		<!--DEBUG: DEBUG、INFO、WARN、ERROR、FATAL-->
		<!--ALL: DEBUG、INFO、WARN、ERROR、FATAL-->
		<level value="DEBUG"/>
		<appender-ref ref="ErrorLog" />
		<appender-ref ref="WarnLog" />
		<appender-ref ref="InfoLog" />
		<appender-ref ref="DebugLog" />
	</root>
  <!-- 错误 Error.log-->
  <appender name="ErrorLog" type="log4net.Appender.RollingFileAppender">
    <!--目录路径,可以是相对路径或绝对路径-->
    <param name="File" value="./logs/error/"/>
	<param name="Encoding" value="utf-8" />
    <!--文件名,按日期生成文件夹-->
    <param name="DatePattern" value="yyyyMMdd'.txt'"/>
    <!--追加到文件-->
    <appendToFile value="true"/>
    <!--创建日志文件的方式,可选值:Date[日期],文件大小[Size],混合[Composite]-->
    <rollingStyle value="Composite"/>
    <!--写到一个文件-->
    <staticLogFileName value="false"/>
    <!--单个文件大小。单位:KB|MB|GB-->
    <maximumFileSize value="900MB"/>
    <!--最多保留的文件数,设为"-1"则不限-->
    <maxSizeRollBackups value="15"/>
    <!--日志格式-->
    <layout type="log4net.Layout.PatternLayout">
      <conversionPattern value="[%d{HH:mm:ss}][%thread][%logger] - %m%n"/>
    </layout>
    <filter type="log4net.Filter.LevelRangeFilter">
      <param name="LevelMin" value="ERROR" />
      <param name="LevelMax" value="ERROR" />
    </filter>
  </appender>

  <!-- 警告 Warn.log-->
  <appender name="WarnLog" type="log4net.Appender.RollingFileAppender">
    <!--目录路径,可以是相对路径或绝对路径-->
    <param name="File" value="./logs/warn/"/>
    <!--文件名,按日期生成文件夹-->
    <param name="DatePattern" value="yyyyMMdd'.txt'"/>
	<param name="Encoding" value="utf-8" />
    <!--追加到文件-->
    <appendToFile value="true"/>
    <!--创建日志文件的方式,可选值:Date[日期],文件大小[Size],混合[Composite]-->
    <rollingStyle value="Composite"/>
    <!--写到一个文件-->
    <staticLogFileName value="false"/>
    <!--单个文件大小。单位:KB|MB|GB-->
    <maximumFileSize value="900MB"/>
    <!--最多保留的文件数,设为"-1"则不限-->
    <maxSizeRollBackups value="15"/>
    <!--日志格式-->
    <layout type="log4net.Layout.PatternLayout">
      <conversionPattern value="[%d{HH:mm:ss}][%thread][%logger] - %m%n"/>
    </layout>
    <filter type="log4net.Filter.LevelRangeFilter">
      <param name="LevelMin" value="WARN" />
      <param name="LevelMax" value="WARN" />
    </filter>
  </appender>

  <!-- 信息 Info.log-->
  <appender name="InfoLog" type="log4net.Appender.RollingFileAppender">
    <!--目录路径,可以是相对路径或绝对路径-->
    <param name="File" value="./logs/info/"/>
    <!--文件名,按日期生成文件夹-->
    <param name="DatePattern" value="yyyyMMdd'.txt'"/>
	<param name="Encoding" value="utf-8" />
    <!--追加到文件-->
    <appendToFile value="true"/>
    <!--创建日志文件的方式,可选值:Date[日期],文件大小[Size],混合[Composite]-->
    <rollingStyle value="Composite"/>
    <!--写到一个文件-->
    <staticLogFileName value="false"/>
    <!--单个文件大小。单位:KB|MB|GB-->
    <maximumFileSize value="900MB"/>
    <!--最多保留的文件数,设为"-1"则不限-->
    <maxSizeRollBackups value="15"/>
    <!--日志格式-->
    <layout type="log4net.Layout.PatternLayout">
      <conversionPattern value="[%d{HH:mm:ss}][%thread][%logger] - %m%n"/>
    </layout>
    <filter type="log4net.Filter.LevelRangeFilter">
      <param name="LevelMin" value="INFO" />
      <param name="LevelMax" value="INFO" />
    </filter>
  </appender>

  <!-- 调试 Debug.log-->
  <appender name="DebugLog" type="log4net.Appender.RollingFileAppender">
    <!--目录路径,可以是相对路径或绝对路径-->
    <param name="File" value="./logs/debug/"/>
    <!--文件名,按日期生成文件夹-->
    <param name="DatePattern" value="yyyyMMdd'.txt'"/>
	<param name="Encoding" value="utf-8" />
    <!--追加到文件-->
    <appendToFile value="true"/>
    <!--创建日志文件的方式,可选值:Date[日期],文件大小[Size],混合[Composite]-->
    <rollingStyle value="Composite"/>
    <!--写到一个文件-->
    <staticLogFileName value="false"/>
    <!--单个文件大小。单位:KB|MB|GB-->
    <maximumFileSize value="900MB"/>
    <!--最多保留的文件数,设为"-1"则不限-->
    <maxSizeRollBackups value="15"/>
    <!--日志格式-->
    <layout type="log4net.Layout.PatternLayout">
      <conversionPattern value="[%d{HH:mm:ss}][%thread][%logger] - %m%n"/>
    </layout>
    <filter type="log4net.Filter.LevelRangeFilter">
      <param name="LevelMin" value="DEBUG" />
      <param name="LevelMax" value="DEBUG" />
    </filter>
  </appender>
</log4net>

十三 页面继承

1 winform 窗体继承后无法编辑

  并非所有控件都支持从基本窗体进行可视化继承。以下控件不支持本演练中描述的场景: WebBrowser、ToolStrip、ToolStripPanel、TableLayoutPanel、FlowLayoutPanel、DataGridView 。继承窗体中的这些控件是无论您使用何种修饰符(私有(private)、 protected 或公共(public)),始终为只读。

2 继承窗体 无法拖动修改控件大小

  问题描述:一个窗体集成父窗体,发现无法直接拖动修改的控件,比如修改大小等。
  特征:
  不禁使父窗体控件,就算新加一个控件也会这样:鼠标放到控件移动手方块上会出现一个“继承的控件”的tooptip,
  异常在这里插入图片描述
  正常情况
在这里插入图片描述
  原因:
  父窗体设置了WindowState属性为:System.Windows.Forms.FormWindowState.Maximized
  在父窗体的designer.cs中有这么一句话:this.WindowState = System.Windows.Forms.FormWindowState.Maximized;
  解决办法:
  一、去掉这句话;
  二、放在窗体构造函数中,在这句话前面加上判断,如果VS处于设计模式,就返回;

public frmBaseChild()
{
    InitializeComponent();
    if (CheckDesingModel.IsDesingMode) return;//如果处于设计模式,返回
    this.WindowState = System.Windows.Forms.FormWindowState.Maximized;
}

CheckDesingModel类:

internal class CheckDesingModel
{
	public static bool IsDesingMode
	{
		get
		{
			bool ReturnFlag = false;
			if (LicenseManager.UsageMode == LicenseUsageMode.Designtime)
				ReturnFlag = true;
			else if (System.Diagnostics.Process.GetCurrentProcess().ProcessName == "devenv")
				ReturnFlag = true;
				//if (ReturnFlag)
				// Msg.Warning("设计模式");
				//else Msg.Warning("非设计模式!");
			return ReturnFlag;
		}
	}
}

十四 类调用主窗体控件

  在Form1类下定义静态变量,构造方法中引用这个Form1

class Form1: Form
{
    public static Form1 form1;
    public Form1()
    {
        InitializeComponent();
        form1 = this;
    }
    public void Method()
    {
    }
}

  调用该窗体的控件或方法

Form1.form1.Button;
Form1.form1.Method();

十五 用户控件属性集合定义

  上面已经说过使用属性集合,是因为自定义的控件中有多相同的对象需要在调用时进行设置。(当然你要说是为了把多个属性放在一起好找也不是不行)
  下面用两个例子来说明
  例1:控件中用到多个图形对象(前景、背景、标志…)
  1、创建一个类用来封装调用控件后添加图片对象时需要设置的属性

public class BsItem : Component
{
	private Image _imageItem;
	[Description("选中图片"), Category("外观")]
	public Image SelectImage { get { return _imageItem; } }
	
	private string _ImageName;
	[Description("图片路径"), Category("外观")]
	public string ImageName
	{
		get { return _ImageName; }
		set
		{
			_ImageName = value;
			this._imageItem = Image.FromFile(_ImageName);
			_ImageName = System.IO.Path.GetFileName(_ImageName);
			Name = _ImageName;
		}
	}
}

  2、定义一个图片集合

private List<BsItem> items = new List<BsItem>();

[TypeConverter(typeof(System.ComponentModel.CollectionConverter))]//指定编辑器特性
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]//设定序列化特性
[Category("外观"), Description("图像文件集")]
public List<BsItem> ImageList
{
	get { return items; }
}

[Browsable(false)]//不显示Name 属性
public string Name { get; set; }

  调用时的效果图
在这里插入图片描述
  例2:控件中用到多个自定义变量,需要在调用时设置:数据来源、不同状态下显示的内容等属性
  属性中包含了BOOL,string,图形,枚举,自定义等多种数据类型
  方法与例1相同,这些属性的具体使用代码不赘述,只要在{ get; set; }中编写就OK了

private List<DataAttribute> mDataAttribute = new List<DataAttribute>();

[TypeConverter(typeof(System.ComponentModel.CollectionConverter))]//指定类型装换器
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
[Category("外观"), Description("图像文件集")]
public List<DataAttribute> MDataAttribut
{
	get { return mDataAttribute; }
	set { mDataAttribute = value; }
}
public class DataAttribute
{
	[Description("变量名称"), Browsable(true)]
	public string VariableName { get; set; }
	
	[Description("允许读取变量值"), Browsable(true)]
	public bool ReadEnabled { get; set; }
	
	[Description("变量存储区"), Browsable(true)]
	public catalogOne CatalogOneName { get; set; }
	
	[Description("全局数据块号,DB1对应用户程序的最小全局DB块"), Browsable(true)]
	public catalogTwo CatalogTwoName { get; set; }
	
	[Description("变量为1时显示的图片"), Browsable(true)]
	public Image TrueImage { get; set; }
	
	[Description("变量为0时显示的图片"), Browsable(true)]
	public Image FalseImaRge { get; set; }
	
	[Description("变量未加载时显示的图片"), Browsable(true)]
	public Image NotImage { get; set; }
}

  效果图
在这里插入图片描述

十六 ScottPlot

https://scottplot.net/
https://scottplot.net/cookbook/5.0/
  上位机开发过程中,信号数据是最常遇到的,在采集到信号数据后,如果能更好的展示成了难题。刚好最近接手了一个脑电信号数据的采集的项目,需要实时采集脑电信号并以波形展示出来。经过一番调研,网上有不少开源的图形控件用于波形的展示,比如oxyplot,scottplot,livecharts等,在尝试后发现oxyplot采用MVVM设计,非常符合WPF的开发,Scottplot比较符合Winform的开发,而且接口也比较奇怪,livecharts绘图特别漂亮,但有性能问题,比如绘制大数据点时会特别卡。经过详细调研后决定使用Scottplot来实现,理由下面会说。
  我们知道波形图由坐标轴来绘制,需要横坐标和纵坐标,像脑电数据,跟采集频率有关,即横坐标是时间递增的,而Scottplot专门为这种横坐标递增的设计了一种波形:Signal Plot(信号图),信号图具有均匀分布的 Y 点。信号图非常快,可以交互显示数百万个数据点。有许多不同类型的可绘制对象,每种都有不同的用途。在实际使用中,只需要将数据以double的数组传进去就行,再设置频率,即可自动生成相应的波形,这对于需要高频采集数据并展示在坐标轴上是非常方便的。
  Scottplot的使用可能与WPF的MVVM模式不太适应,官方也不推荐使用MVVM的方式来实现,就直接在后端加载就行。最终效果如图(8KHz的实时采集波形):
Scatter Plot:散点图
Signal Plot:信号图

1 图表Title中文显示问题

https://scottplot.net/cookbook/5.0/Internationalization/AutomaticFontDetection/
  使用ScottPlot 5.0.23 WinForms控件进行绘图,当图表的标题为非英文时,会显示空白方框。

FormPlot_TestItemRecord.Plot.Title("测试",30);

  通过查阅ScottPlot官网中 CookBook-Internationalization-Automatic Font Detection 模块下的文章,发现只需要使用 Font.Automatic() 方法即可完成多种语言的自动适配。

FormPlot_TestItemRecord.Plot.Title("测试",30);
FormPlot_TestItemRecord.Plot.Font.Automatic();
  • 3
    点赞
  • 36
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值