C# .NET WinForm班组生产统计Chart控件X轴与水平滚动功能实现问题
问题描述
我首次在此提问,英文水平有限请见谅。我需要基于SQL查询返回的DataTable绘制堆叠柱形图,目前已经初步实现功能,但存在两个待解决问题:
- X轴绑定datetime类型字段时,会自动生成全天线性时间刻度,我仅需要展示查询结果中实际存在的时间点对应的刻度
- 当展示的数据量过大时,需要支持通过鼠标滚轮水平滚动图表查看所有数据
相关实现代码
设计器代码
namespace GRAPHICOBJECTS { partial class GRAFTREND { /// <summary> /// 必需的设计器变量 /// </summary> private System.ComponentModel.IContainer components = null; /// <summary> /// 清理正在使用的资源 /// </summary> /// <param name="disposing">如果应释放托管资源则为true,否则为false</param> protected override void Dispose(bool disposing) { if (disposing && (components != null)) { components.Dispose(); } base.Dispose(disposing); } #region 组件设计器生成的代码 /// <summary> /// 设计器支持所需的方法,请勿使用代码编辑器修改此方法的内容 /// </summary> private void InitializeComponent() { System.Windows.Forms.DataVisualization.Charting.ChartArea chartArea1 = new System.Windows.Forms.DataVisualization.Charting.ChartArea(); System.Windows.Forms.DataVisualization.Charting.Series series1 = new System.Windows.Forms.DataVisualization.Charting.Series(); System.Windows.Forms.DataVisualization.Charting.Series series2 = new System.Windows.Forms.DataVisualization.Charting.Series(); this.CHART = new System.Windows.Forms.DataVisualization.Charting.Chart(); this.button1 = new System.Windows.Forms.Button(); ((System.ComponentModel.ISupportInitialize)(this.CHART)).BeginInit(); this.SuspendLayout(); // // CHART // this.CHART.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(175)))), ((int)(((byte)(175)))), ((int)(((byte)(175))))); chartArea1.AxisX.Enabled = System.Windows.Forms.DataVisualization.Charting.AxisEnabled.True; chartArea1.AxisX.Interval = 1D; chartArea1.AxisX.IntervalAutoMode = System.Windows.Forms.DataVisualization.Charting.IntervalAutoMode.VariableCount; chartArea1.AxisX.IntervalType = System.Windows.Forms.DataVisualization.Charting.DateTimeIntervalType.Hours; chartArea1.AxisX.IsLabelAutoFit = false; chartArea1.AxisX.LabelStyle.Angle = 90; chartArea1.AxisX.LabelStyle.Format = "dd/MM HH:mm"; chartArea1.AxisX.LabelStyle.Interval = 0D; chartArea1.AxisX.LabelStyle.IntervalType = System.Windows.Forms.DataVisualization.Charting.DateTimeIntervalType.Hours; chartArea1.AxisX2.Interval = 1D; chartArea1.AxisX2.IntervalAutoMode = System.Windows.Forms.DataVisualization.Charting.IntervalAutoMode.VariableCount; chartArea1.AxisX2.IntervalType = System.Windows.Forms.DataVisualization.Charting.DateTimeIntervalType.Hours; chartArea1.AxisX2.IsLabelAutoFit = false; chartArea1.AxisX2.LabelStyle.Angle = 90; chartArea1.AxisX2.LabelStyle.Format = "dd/MM HH:mm"; chartArea1.AxisX2.LabelStyle.Interval = 0D; chartArea1.AxisX2.LabelStyle.IntervalType = System.Windows.Forms.DataVisualization.Charting.DateTimeIntervalType.Hours; chartArea1.Name = "ChartArea1"; this.CHART.ChartAreas.Add(chartArea1); this.CHART.Location = new System.Drawing.Point(1, 0); this.CHART.Name = "CHART"; this.CHART.RightToLeft = System.Windows.Forms.RightToLeft.No; series1.ChartArea = "ChartArea1"; series1.ChartType = System.Windows.Forms.DataVisualization.Charting.SeriesChartType.StackedColumn; series1.IsVisibleInLegend = false; series1.Name = "S2"; series1.XValueType = System.Windows.Forms.DataVisualization.Charting.ChartValueType.DateTime; series2.ChartArea = "ChartArea1"; series2.ChartType = System.Windows.Forms.DataVisualization.Charting.SeriesChartType.StackedColumn; series2.IsVisibleInLegend = false; series2.Name = "S1"; series2.XValueType = System.Windows.Forms.DataVisualization.Charting.ChartValueType.DateTime; this.CHART.Series.Add(series1); this.CHART.Series.Add(series2); this.CHART.Size = new System.Drawing.Size(1012, 350); this.CHART.TabIndex = 1; this.CHART.Text = "CHART"; // // button1 // this.button1.Location = new System.Drawing.Point(3, 352); this.button1.Name = "button1"; this.button1.Size = new System.Drawing.Size(58, 43); this.button1.TabIndex = 2; this.button1.Text = "button1"; this.button1.UseVisualStyleBackColor = true; this.button1.Click += new System.EventHandler(this.button1_Click); // // GRAFTREND // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.Controls.Add(this.button1); this.Controls.Add(this.CHART); this.Name = "GRAFTREND"; this.Size = new System.Drawing.Size(1015, 398); this.Load += new System.EventHandler(this.UserControl2_Load); ((System.ComponentModel.ISupportInitialize)(this.CHART)).EndInit(); this.ResumeLayout(false); } #endregion private System.Windows.Forms.DataVisualization.Charting.Chart CHART; private System.Windows.Forms.Button button1; } }
SQL查询及DrawChart方法调用代码
private void button1_Click(object sender, EventArgs e) { SqlCommand cmd = con.CreateCommand(); cmd.CommandType = CommandType.StoredProcedure; cmd.CommandText = "GetStatisticalData_ShiftGraph"; cmd.Parameters.AddWithValue("@DataStartSH", "2021-09-01 06:00:00.000"); cmd.Parameters.AddWithValue("@DataEndSH", "2021-09-02 06:00:00.000"); cmd.Parameters.AddWithValue("@RecipeName", "TEST"); cmd.Parameters.AddWithValue("@LineN", 1); cmd.ExecuteNonQuery(); DataTable dt = new DataTable(); SqlDataAdapter da = new SqlDataAdapter(cmd); NRecord = da.Fill(dt); GraphicFunctions.DrawChart(this.CHART, dt, "TimeShift", "Rejected", Color.Yellow, "TimeShift", "Good", Color.LightGreen); this.CHART.Series["S1"].IsValueShownAsLabel = true; this.CHART.Series["S2"].IsValueShownAsLabel = true; this.CHART.DataManipulator.InsertEmptyPoints(1, IntervalType.Days, "S1"); this.CHART.DataManipulator.InsertEmptyPoints(1, IntervalType.Days, "S2"); }
绘图方法代码
public static void DrawChart(Chart ObjChart,DataTable DTable, string xColumnS1, string yColumnS1,Color ColorS1, string xColumnS2, string yColumnS2, Color ColorS2) { double PointsCount; try { ObjChart.DataSource = DTable; ObjChart.Series["S1"].XValueMember = xColumnS1; ObjChart.Series["S1"].YValueMembers = yColumnS1; ObjChart.Series["S2"].Enabled = true; ObjChart.Series["S2"].XValueMember = xColumnS2; ObjChart.Series["S2"].YValueMembers = yColumnS2; ObjChart.DataBind(); PointsCount = ObjChart.Series["S1"].Points.Count; for (int i = 0; i <= PointsCount - 1; i++) ObjChart.Series["S1"].Points[i].Color = ColorS1; PointsCount = ObjChart.Series["S2"].Points.Count; for (int i = 0; i <= PointsCount - 1; i++) ObjChart.Series["S2"].Points[i].Color = ColorS2; } catch (System.Exception ex) { throw ex; } }
解决方案
问题1:X轴仅展示实际存在的时间点
X轴自动生成连续时间刻度是因为X值被设置为DateTime类型,图表默认会按照时间连续性补全刻度,修改步骤如下:
- 移除设计器代码中两个Series的
XValueType = ChartValueType.DateTime配置,让X值默认按字符串处理,避免自动生成连续刻度 - 删除
button1_Click方法中的InsertEmptyPoints两行代码,该方法会主动插入空数据点补全时间区间 - 调整X轴配置,将
IntervalAutoMode改为FixedCount,Interval设为1,确保每个实际存在的点都显示刻度
问题2:支持鼠标滚轮水平滚动
通过监听鼠标滚轮事件,动态调整X轴的显示范围实现滚动效果:
- 在
InitializeComponent方法末尾添加滚轮事件绑定:
this.CHART.MouseWheel += CHART_MouseWheel;
- 实现滚轮事件方法:
private void CHART_MouseWheel(object sender, MouseEventArgs e) { var chart = (Chart)sender; var area = chart.ChartAreas[0]; int totalPoints = chart.Series[0].Points.Count; int showCount = 10; // 单屏显示10个数据点,可自行调整 if (totalPoints <= showCount) return; double offset = 2; // 单次滚动偏移量,可自行调整 if (e.Delta > 0) { // 向上滚动,左移显示范围 if (area.AxisX.Minimum - offset >= 0) { area.AxisX.Minimum -= offset; area.AxisX.Maximum -= offset; } } else { // 向下滚动,右移显示范围 if (area.AxisX.Maximum + offset <= totalPoints) { area.AxisX.Minimum += offset; area.AxisX.Maximum += offset; } } }
- 在
DrawChart方法末尾添加初始化显示范围的代码:
int totalPoints = ObjChart.Series["S1"].Points.Count; int showCount = 10; var area = ObjChart.ChartAreas[0]; if (totalPoints > showCount) { area.AxisX.Minimum = 0; area.AxisX.Maximum = showCount; } else { area.AxisX.Minimum = double.NaN; area.AxisX.Maximum = double.NaN; }
内容的提问来源于stack exchange,提问作者AleAle128
相关产品推荐
相关产品推荐

