比赛训练笔记 - WinForm

编程 · 2023-07-15

DataGridView

使用错误容器时的巨坑

使用 List 作为 DataSource。点击单元格时,就算没有单元格点击事件,也会在外层(父窗体 ShowDialog 子窗体的位置)报 IndexOutOfRange 下标越界异常。
解决方法:只要是用于 DGV 显示的列表,一律用BindingList

BindingList

ResetBinding()(对于整个列表)或ResetItem(i)(对于特定元素)来手动触发“列表已改变”事件。
RaiseListChangedEvents属性控制 BindingList 是否会发出“列表已改变”的事件。
注意:这个属性不仅控制添加和删除元素时是否会自动触发事件,同时也控制ResetBinding() / ResetItem(i)这类手动触发事件的方法是否会触发事件。

添加向列表大量元素时的标准流程:

list.RaiseListChangedEvents = false;

list.Clear();
foreach(var item in source)
{
    list.Add(item);
}

// 一定要先启用事件,然后再手动触发事件。
list.RaiseListChangedEvents = true;
list.ResetBindings();

外观与杂项行为

  • 内容多行显示:DefaultCellStyle属性集合 → WrapMode属性设置为 True。
    AutoSizeRowsMode 行自动大小模式设置为 AllCells,行大小会自动匹配内容高度。
  • 表头是否可见:

    • 左侧行头:RowHeaderVisible属性。
    • 顶部列头:ColumnHeaderVisible属性。
  • 是否可多选:MultiSelect属性。
  • 整行选择:SelectionMode设置为 FullRowSelect。整行选择不会影响单元格点击。

在 ContentClick 内容点击事件中,如果用户选择了表头,则e.RowIndex为 -1。

在 CellFormat 单元格格式化事件中,e.FormatApplied控制是否还要使用其他格式化器继续格式化内容。如果设置为 true,则不会继续进行格式化。如果只是修改了颜色,就不要设置这个属性。否则如果内容不是文本形式,就会报错。

Timer

Timer 会自动重复。

Start() / Stop()方法和设置Enabled属性的功能相等。

Timer 不受 ShowDialog 阻塞线程的影响。但是 Timer.Tick “时间到了”事件始终在UI线程上运行。

Timer会在窗体对象存在时一直运作。在窗体 Close 之后,如果没有Dispose,Timer 还是会一直在后台运行。

自定义绘制

用控件的CreateGraphics()方法创建画布。

Graphics g = this.CreateGraphics();

[danger]绘制是一次性的,控件区域刷新后就会消失。
因此,一定要在控件的Paint事件中进行绘制。[/danger]

绘制图形需要画笔 Pen 对象。

Pen pen = new Pen(color, width);

像素

直接设置 Image 指定位置的像素颜色。

image.SetPixel(x, y, color);

直线(Line)

g.DrawLine(pen, pointStart, pointEnd);

矩形(Rectangle)

g.DrawRectangle(pen, x, y, width, height);

椭圆(Ellipse)

g.DrawEllipse(pen, x, y, width, height);

圆弧(Arc)

角度从右边开始,顺时针。
在矩形中画椭圆(定义的矩形是椭圆的 bounding box),传入的角度会作 cos 和 sin 定位到圆上的点。
startAngle开始,顺时针推进advanceAngle角度作为终点。
圆弧

g.DrawArc(pen, rect, startAngle, advanceAngle);

扇形(Pie)

使用方法同圆弧,但是圆弧的开头和结尾会与圆心连直线。
扇形

g.DrawPie(pen, rect, startAngle, advanceAngle);

贝塞尔曲线(Bezier)

由起点、控制点 1(位于起点处)、控制点 2(位于终点处)、终点组成。

g.DrawBezier(pen, pointStart, pointControl1, pointControl2, pointEnd);

多边形(Polygon)

传入顶点数组,顶点之间依次连接直线,绘制出多边形。

Point[] vertices = {...};

g.DrawPolygon(pen, vertices)

文本(String)

绘制文本需要字体 Font 和画刷 Brush 对象。
Brush 类型通常有:

  • SolidBrush:纯色
  • LinearGradientBrush:线性渐变色
Font font = new Font(fontName, emSize);
Brush brush = new SolidBrush(color);

g.DrawString(text, font, brush, x, y);

可以提前测量文本的大小:

SizeF size = g.MeasureString(text, font);

图像(Image)

g.DrawImage(image, x, y, width, height);

变换矩阵

可以设置绘制时的变换矩阵。

添加平移(在当前变换矩阵的基础上):

g.TranslateTransform(x, y);

添加旋转(在当前变换矩阵的基础上):

g.RotateTransform(angle);

重置变换矩阵:

g.ResetTransform();

离屏渲染

可以创建使用 Image 作为画布的 Graphics,进行离屏渲染。
最后用控件的 Graphics 来将 Image 画到控件上。

Graphics g = Graphics.FromImage(image);

抗锯齿

可以设置 Graphics 绘制时的渲染质量。

g.SmoothingMode = SmoothingMode.AntiAlias;

默认值为 Default,有以下设置:

  • 无抗锯齿:Default / HighSpeed / None
  • 有抗锯齿:HighQuality / AntiAlias
笔记 C#
Theme Jasmine by Kent Liao