接到一个需求,绘制一个水管线的图片,这种管线可以有12种分段方法,最后将这12种分段方法合并后在一条水管线上展示,要求:
⒈支持分段的属性展示;
⒉要求每个分段都能清晰展示,分段数在0(没有分段)~100之间,水管线长度不定;
3、每个分段的属性都有值,可以更改,使用XML存储;
4、不同材质管线使用不同的背景;
5、根据不同的值,分段显示不同的颜色;
6、支持鼠标滚动。
因为需要快速完成,时间紧,第一时间想到使用C#的GDI+,可是在支持热点和放大与缩小时卡壳了,赶紧换了一种方法,制定一个自定义控件,在Panel上绘图,支持拖动,放大与缩小,可是总是会出现这样那样的问题,也想使用SVG,可是担心还是完不成,换成DataGridView,也算完成了,效果如下:
经常在网上看到说在中国35岁以后就不能写代码了,感觉一个是热爱不够了,第二个可能是身体状况也不允许了。在一般企业中,也没有动力去费神像年轻时候完成一个程序,那个时候有冲劲和干劲,特别有耐心,希望得到别人的肯定与赞扬,现在50多岁了,没有动力了,想一想以前也是觉得可笑了。
这个完成后,自定义一个控件就可以使用了。
下面是完成的代码:
定义:
private static ToolTip toolTip = new ToolTip();
// 定义一个List,用于存储点的信息
List<float> Points = new List<float>() {0 };
int MinColWidth=48;//定义最小分段对应的宽度
float pipelineLength;//定义管线长度
float RateF;//实际进行计算的比例
初始化:
//初始化DataGridView
dataGridView1.ColumnHeadersVisible = false;//隐藏标题栏
dataGridView1.CellBorderStyle = DataGridViewCellBorderStyle.None;//取消分割线
//dataGridView1.ScrollBars = ScrollBars.Horizontal;
dataGridView1.ScrollBars = ScrollBars.None;//去掉滚动条
dataGridView1.AllowUserToAddRows = false;//不允许用户增加行
dataGridView1.AllowUserToDeleteRows = false;//不允许用户删除行
dataGridView1.AllowUserToResizeRows = false;//不允许用户改变行高度
//dataGridView1.Columns.Add("Column1", "列1");
dataGridView1.RowTemplate.Height = 32;//设置行高度
//dataGridView1.Rows.Add();
//dataGridView1.Rows[0].DefaultCellStyle.BackColor = Color.LightGray;
dataGridView1.AllowUserToResizeColumns = false;//不允许用户改变栏宽度
dataGridView1.RowHeadersVisible = false;//隐藏最前面的选择列
dataGridView1.Columns.Clear();//删除所有的列
dataGridView1.Columns.Add("Column1", "列1");//添加一列
dataGridView1.Rows.Add();//添加一行
dataGridView1.Rows[0].ReadOnly = true;//设置第一行为只读
dataGridView1.Rows[0].DefaultCellStyle.BackColor = Color.LightGray;//设置行背景色
dataGridView1.Columns[0].Visible = false;//隐藏标题栏
dataGridView1.EditMode = DataGridViewEditMode.EditProgrammatically;// 设置编辑模式为EditProgrammatically
//注册事件
dataGridView1.CellPainting += dataGridView1_CellPainting;//单元格渲染
dataGridView1.CellFormatting += dataGridView1_CellFormatting;
dataGridView1.MouseEnter += dataGridView1_MouseEnter;//鼠标移入
dataGridView1.MouseLeave += dataGridView1_MouseLeave;//鼠标离开
dataGridView1.MouseWheel += dataGridView1_MouseWheel;//鼠标滚动
dataGridView1.RowPrePaint+=dataGridView1_RowPrePaint;//行渲染
//List<float> P = new List<float>() { 12, 27, 41, 73, 89, 105, 119, 126, 138, 166, 192, 208, 255, 377, 410, 439 };
List<float> P = new List<float>() { 31, 25, 45,39, 73, 89, 115, 121 };
UpdatePoints(P, 138);
事件以及函数内容:
void UpdatePoints(List<float> NewPoints,float NowDistance)
{
Points = NewPoints;
pipelineLength = NowDistance;
Points.Add(0);
Points.Add(pipelineLength);
Points.Sort();
//计算实际的换算比例
RateF = ((dataGridView1.Width - (Points.Count - 1) * 5) / pipelineLength);
textBox1.Text += "计算比例:" + RateF.ToString() + Environment.NewLine;
textBox1.Text += "管线长度:" + pipelineLength.ToString() + Environment.NewLine;
textBox1.Text += "显示宽度:" + dataGridView1.Width.ToString() + Environment.NewLine;
//得到最小的分段
float MinSectionLength = CalculateMinDistance();
textBox1.Text += "最小分段:" + MinSectionLength.ToString() + Environment.NewLine;
if (MinSectionLength * RateF < MinColWidth)
{
//重新设定比例
RateF = MinColWidth / MinSectionLength;
}
textBox1.Text += "最后比例:" + RateF.ToString() + Environment.NewLine;
//更新DataGridView
//设置每一列的宽度
//foreach (int point in Points)
for(int i=1; i<Points.Count; i++)
{
float ColWidth = (Points[i] - Points[i - 1]) * RateF;
AddColumn(dataGridView1,"Section", (int)ColWidth, Color.Brown, Color.Cyan, Points[i - 1], Points[i]);//增加分段
if (i!=Points.Count-1)
{
AddColumn(dataGridView1, "Dot", 5, Color.Brown, Color.Blue, Points[i - 1], Points[i]);//增加点
}
}
}
// 动态增加列
void AddColumn(DataGridView dataGridView, string StrType, int width, Color textColor, Color cellBackColor,float StartDot,float EndDot)
{
DataGridViewTextBoxColumn column = new DataGridViewTextBoxColumn();
column.Width = width;
//column.HeaderText = text;
column.DefaultCellStyle.ForeColor = textColor;
column.DefaultCellStyle.BackColor = cellBackColor;
// 为每一列添加属性
column.Tag = new ColumnProperty
{
SectionType = StrType,
StartPoint = StartDot,
EndPoint = EndDot,
Distance = EndDot - StartDot
};
dataGridView.Columns.Add(column);
// 获取第一行
DataGridViewRow firstRow = dataGridView.Rows[0];
// 设置特定列的单元格属性
//firstRow.Cells[column.Index].Value = text;
firstRow.Cells[column.Index].Style.BackColor = cellBackColor;
firstRow.Cells[column.Index].Style.ForeColor = Color.Red;
//dataGridView.Columns.Add(column);
}
class ColumnProperty
{
public int SectionIndex { get; set; } //分段序号
public string SectionType { get; set; } //类型
public float StartPoint { get; set; } //起点
public float EndPoint { get; set; } //终点
public float Distance { get; set; } //距离
public string Rule01 { get; set; } //
public string Rule02 { get; set; } //
public string Rule03 { get; set; } //
public string Rule04 { get; set; } //
public string Rule05 { get; set; } //
public string Rule06 { get; set; } //
public string Rule07 { get; set; } //
public string Rule08 { get; set; } //
public string Rule09 { get; set; } //
public string Rule10 { get; set; } //
public string Rule11 { get; set; } //
public string Rule12 { get; set; } //管理站队
public float Pvalue { get; set; } //失效可能性
public float Cvalue { get; set; } //风险
public float Rvalue { get; set; } //后果
}
float CalculateMinDistance()
{
// 实现计算功能,计算相邻两个点之间的距离,得到最小值
List<float> segmentLengths = new List<float>();
for (int i = 0; i < Points.Count - 1; i++)
{
float segmentLength = Points[i + 1] - Points[i];
segmentLengths.Add(segmentLength);
}
float minSegmentLength = segmentLengths.Count > 0 ? segmentLengths.Min() : pipelineLength;
return FloatFormat(minSegmentLength);
}
private float FloatFormat(float f)
{
return (float)Math.Round(f, 2); ;
}
private void dataGridView1_Scroll(object sender, ScrollEventArgs e)
{
// 判断是否是水平滚动
if (e.ScrollOrientation == ScrollOrientation.HorizontalScroll)
{
// 水平滚动内容
dataGridView1.HorizontalScrollingOffset = e.NewValue;
}
}
private void dataGridView1_CellFormatting(object sender, DataGridViewCellFormattingEventArgs e)
{
//if (e.RowIndex >= 0 && e.ColumnIndex >= 0)
//{
// if (e.ColumnIndex % 2 == 0)
// {
// e.CellStyle.BackColor = Color.DarkGray;
// }
// else
// {
// e.CellStyle.BackColor = Color.LightGray;
// }
//}
}
private void dataGridView1_CellClick(object sender, DataGridViewCellEventArgs e)
{
//if (e.RowIndex >= 0 && e.ColumnIndex >= 0 && e.ColumnIndex % 2 == 0)
//{
// dataGridView1.Rows[e.RowIndex].DefaultCellStyle.SelectionBackColor = Color.Transparent;
// dataGridView1.Rows[e.RowIndex].DefaultCellStyle.SelectionForeColor = dataGridView1.DefaultCellStyle.ForeColor;
//}
}
private void dataGridView1_RowPrePaint(object sender, DataGridViewRowPrePaintEventArgs e)
{
e.PaintParts &= ~DataGridViewPaintParts.Background;
if (e.RowIndex >= 0)
{
Image image = Image.FromFile("D:/CSharp/TestTreeview/WinFormsApp2/WinFormsApp2/image/TRQpipeline.jpg"); // 替换为实际的图片路径
Rectangle rect = new Rectangle(e.RowBounds.Left, e.RowBounds.Top, dataGridView1.Columns.GetColumnsWidth(DataGridViewElementStates.Visible) - 1, e.RowBounds.Height - 1);
e.Graphics.DrawImage(image, rect);
}
}
private void dataGridView1_MouseEnter(object sender, EventArgs e)
{
dataGridView1.BorderStyle = BorderStyle.FixedSingle;
//dataGridView1.BorderColor = Color.Red;
}
private void dataGridView1_MouseLeave(object sender, EventArgs e)
{
dataGridView1.BorderStyle = BorderStyle.None;
}
private void dataGridView1_CellPainting(object sender, DataGridViewCellPaintingEventArgs e)
{
//if (e.RowIndex >= 0 && e.ColumnIndex >= 0)
//{
// // 设置单元格背景颜色
// e.CellStyle.BackColor = Color.Red;
// // 设置单元格背景色的透明度
// e.CellStyle.BackColor = Color.FromArgb(192, e.CellStyle.BackColor);
//}
if (e.RowIndex >= 0 && e.ColumnIndex >= 0)
{
if (e.ColumnIndex % 2 != 0)
{
// 设置偶数列单元格的前景色为半透明背景色
using (Brush brush = new SolidBrush(Color.FromArgb(0, Color.White)))
{
e.Graphics.FillRectangle(brush, e.CellBounds);
}
}
else
{
// 设置奇数列单元格的前景色为深灰色并覆盖行背景色
using (Brush brush = new SolidBrush(Color.Black))
{
e.Graphics.FillRectangle(brush, e.CellBounds);
}
}
// 绘制单元格内容
e.PaintContent(e.CellBounds);
e.Handled = true;
}
}
// 处理鼠标滚轮事件
private void dataGridView1_MouseWheel(object sender, MouseEventArgs e)
{
int delta = e.Delta;
int newOffset = dataGridView1.HorizontalScrollingOffset - delta;
// 检查滚动范围是否超出边界
if (newOffset < 0)
{
newOffset = 0; // 将滚动范围限制在最左边
}
else if (newOffset > GetHorizontalScrollingOffsetMax())
{
newOffset = GetHorizontalScrollingOffsetMax(); // 将滚动范围限制在最右边
}
// 设置新的水平滚动偏移量
dataGridView1.HorizontalScrollingOffset = newOffset;
}
private int GetHorizontalScrollingOffsetMax()
{
int maxOffset = 0;
foreach (DataGridViewColumn column in dataGridView1.Columns)
{
maxOffset += column.Width;
}
maxOffset -= dataGridView1.ClientSize.Width;
return maxOffset;
}
private void dataGridView1_CellMouseEnter(object sender, DataGridViewCellEventArgs e)
{
if (e.RowIndex >= 0 && e.ColumnIndex % 2 != 0)
{
DataGridViewRow row = dataGridView1.Rows[e.RowIndex];
ColumnProperty properties = (ColumnProperty)row.Cells[e.ColumnIndex].OwningColumn.Tag;
string tooltip = $"Type: {properties.SectionType}\nStart Point: {properties.StartPoint}\nEnd Point: {properties.EndPoint}";
toolTip.SetToolTip(dataGridView1, tooltip);
}
else
{
string tooltip = "";
toolTip.SetToolTip(dataGridView1, tooltip);
}
}
private void dataGridView1_CellMouseDoubleClick(object sender, DataGridViewCellMouseEventArgs e)
{
//判断分段属性如果是Section
if (e.RowIndex >= 0)
{
DataGridViewRow row = dataGridView1.Rows[e.RowIndex];
ColumnProperty properties = (ColumnProperty)row.Cells[e.ColumnIndex].OwningColumn.Tag;
if (properties.SectionType == "Section")
{
MessageBox.Show(properties.StartPoint.ToString()+ "|"+properties.EndPoint.ToString());
}
}
}
功能是完成了,后面还需要完善细节,就是属性值的更新,主要是TreeView与XML内容的同步,包括自动计算。