开发工具:virsual studio 2022 + .net8
C#和java相比各有千秋:
1.C#支持ushort无符号,java都是有符号
2.int等基本数据类型都是一个对象,内部有很多工具方法带来很多转换类型上的遍历,但底层本身还是int基本数据类型(都是语法糖),推测struct修饰的对象都是语法糖?最终都是值类型数据
3.泛型原理上,c#更加高性能,使用了占位符;而java底层还是object强转,只保证了安全性
4.C#委托等技巧上,更加高级(可能用到指针之类)click.event += eventListener;
5.支持ref和out, C#和java本事属于值传递,直接赋值临时对象的引用会变;但C#支持ref和out,就变得也支持引用传递
6.C#还有命名空间的概念,感觉有点不适用,虽然写代码时可以无视它。。但类文件里面多个方法块花括号还是不适应;同时访问修饰符也多了internal(也可用反射调用该修饰的变量或方法,和java类似)
7.struct类型定义的变量是值类型,class定义的变量是引用类型。因此struct类型定义的对象是分配在栈上面的,而class定义的对象是分配在堆上的;struct可以实现接口
8.C#个人觉得缺点,支持的方法太多,写法上比较随意,和java相比显得有点乱。。。
9.using int32 = System.Int32; 然后在文件中使用int32
时,它就会被当作System.Int32
处理。这种做法不会创建一个新的数据类型,它只是简化了你的代码编写。在使用using
指令时,你不能为内置数据类型创建别名,只能为自定义类型创建别名
1.整体文件夹布局风格统一
还是围绕java项目结构风格,对各个业务代码所在位置分工明确;变量名用开头小写驼峰,类名大写;好处:写多了不会忘记java写法。。。写法上也更舒服
2.组件需要自定义开发
(新增用户控件->画笔Paint自己画->工具箱可自动加载该组件->大组件套小组件随便用)
3.组件-电池组件实现
namespace BatteryMoniterCenter.components
{
public partial class BatteryFrame : Panel
{
Rectangle rect;//绘制区域
public BatteryFrame()
{
InitializeComponent();
//设置控件样式标志----绘制
SetStyle(ControlStyles.Selectable, true);
SetStyle(ControlStyles.AllPaintingInWmPaint, true);//忽略窗口消息,减少闪烁
SetStyle(ControlStyles.OptimizedDoubleBuffer, true);//绘制到缓冲区,减少闪烁
SetStyle(ControlStyles.UserPaint, true);//控件由其自身而不是操作系统绘制
SetStyle(ControlStyles.ResizeRedraw, true);//控件调整其大小时重绘
SetStyle(ControlStyles.SupportsTransparentBackColor, true);//支持透明背景
rect = this.ClientRectangle;
}
protected override void OnSizeChanged(EventArgs e)
{
base.OnSizeChanged(e);
rect = this.ClientRectangle;
this.Region = new Region(rect);
rect.Width -= 1;
rect.Height -= 1;
}
//属性扩展:背景色1、背景色2、边框粗细、边框颜色、圆角半径、渐变模式
private Color bgColor = Color.Gray;
/// <summary>
/// 背景色(渐变色中,颜色1)
/// </summary>
[DefaultValue(typeof(Color), "Gray"), Description("控件背景色")]
public Color BgColor
{
get { return bgColor; }
set
{
bgColor = value;
Invalidate();
}
}
[DefaultValue(typeof(Color), "Transparent"), Description("控件背景颜色2")]
private Color bgColor2 = Color.Transparent;
public Color BgColor2
{
get { return bgColor2; }
set
{
bgColor2 = value;
Invalidate();
}
}
private Color borderColor = Color.Gray;
[DefaultValue(typeof(Color), "Gray"), Description("控件边框颜色")]
public Color BorderColor
{
get { return borderColor; }
set
{
borderColor = value;
if (borderWidth > 0)
Invalidate();
}
}
private int borderWidth = 0;
[DefaultValue(typeof(int), "0"), Description("控件边框粗细")]
public int BorderWidth
{
get { return borderWidth; }
set
{
borderWidth = value;
Invalidate();
}
}
[DefaultValue(typeof(int), "5"), Description("圆角弧度大小")]
private int radius = 5;
public int Radius
{
get { return radius; }
set
{
radius = value;
Invalidate();
}
}
[DefaultValue(typeof(LinearGradientMode), "Vertical"), Description("渐变方式")]
private LinearGradientMode gradientMode = LinearGradientMode.Vertical;
public LinearGradientMode GradientMode
{
get { return gradientMode; }
set
{
gradientMode = value;
Invalidate();
}
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
//重绘外观:如果有边框,画边框、背景、内容
Graphics g = e.Graphics;//绘图对象
g.SmoothingMode = SmoothingMode.HighQuality; //呈现质量--高质量呈现
Rectangle rect1;//定义背景矩形
GraphicsPath path = new GraphicsPath();//边框圆角路径
GraphicsPath path2 = new GraphicsPath();//背景圆角路径
if (rect.Width <= 0 && rect.Height <= 0)
{
return;//被最小化后,不刷新
}
path = PaintClass.GetRoundBattery(rect, radius, BatteryModel.defaultBuild());//圆角矩形路径
if (borderWidth > 0)//有边框时
{
g.FillPath(new SolidBrush(borderColor), path);//填充边框的圆角矩形
//内部背景区域矩形
rect1 = new Rectangle(rect.X + borderWidth, rect.Y + borderWidth, rect.Width - 2 * borderWidth, rect.Height - 2 * borderWidth);
//生成内部矩形的圆角矩形路径
path2 = PaintClass.GetRoundBattery(rect1, radius, BatteryModel.defaultBuild());
}
else //无边框
{
path2 = path;
rect1 = rect;
}
//背景色填充
if (bgColor2 != Color.Transparent) //渐变填充
{
//渐变画刷
LinearGradientBrush bgBrush = new LinearGradientBrush(rect1, bgColor, bgColor2, gradientMode);
g.FillPath(bgBrush, path2);//填充背景
}
else
{
//纯色填充
Brush b = new SolidBrush(bgColor);
g.FillPath(b, path2);
}
}
}
}
4.主界面展示
调整色彩搭配上比较耗时,其他的暂时没难度
读写xml和json等,调用第三方库复制个使用案例即可
5.主界面数据联动
plc对接modbusRUT工具类,配合Slave假数据来联动显示(通过虚拟串口转发,COM1转发COM2)
主要学习了
1.新的创建线程的方式 Task.Run(async() => await)..
2.非主线程中刷新控件if (this.IsHandleCreated)
{
this.Invoke(new Action(() =>
{
refreshPlcData();
}));
}
3.创建主站 master = ModbusSerialMaster.CreateRtu(serialPort.Open()=>IsOpen=>serialPort);
4 读取functionCode:4的方法=>IModbusMaster master.ReadInputRegistersAsync(slave.SlaveId, slave.StartAddress, (Context.deviceList.Count * 10).ToString().GetUshort());需要和slave中设置的functionCode和slaveId一致