在WinForms中,我正在尝试创建一个交互式图表,但在一个方面遇到了困难。我正在为图表使用System.Windows.Forms.DataVisualization.Charting.Chart,并使用FastLine系列来呈现数据。
在此图表中,您可以缩放/平移/更改频道,并根据需要动态添加/删除新点。原因是这些点的来源是一个大的二进制文件,可以超过一千兆字节,我使用MemoryMappedFile来提取尽可能少的数据来提高性能。
当向右平移时,一切正常 - 新的点被添加到列表的末尾,你甚至不能说它们在一秒钟之前就不存在了。
然而,当我向左平移时,图表以非常奇怪的方式显示。整条地方都有线路。我知道这里发生了什么,我无法弄清楚如何解决它。
原因是当添加一个点时,从添加到新点的最后一个点绘制一条线。因此,如果最后一个点被添加到右侧,并且新点被添加到左侧,则一条线将在图表中射击。
以下是为图表添加点数的代码:
for (var i = scanStart; i + _step <= scanEnd; i += _step)
{
var sample = _expeData.ReadSample(channel, i);
Series[0].Points.AddXY(i, sample);
}
有没有办法防止这种情况发生?这是一个屏幕截图,显示向左平移后的样子:
答案 0 :(得分:0)
我决定尝试使用LINQ处理排序。
// Suspend updates prior to insertion to speed everything up
chart1.Series[0].Points.SuspendUpdates();
// Do a quicky population for testing purposes
for (int i = 0; i < 100; i++)
{
chart1.Series[0].Points.AddXY(i, i);
}
for (int i = -100; i < 0; i++)
{
chart1.Series[0].Points.AddXY(i, -i * 2);
}
// Sort the data points
var newDataPoints =
(from d in chart1.Series[0].Points
orderby d.XValue
select new { XValue = d.XValue, YValue = d.YValues[0] }).ToList();
// Replace the datapoints
chart1.Series[0].Points.DataBind(newDataPoints, "XValue", "YValue", "");
// Turn updates back on
chart1.Series[0].Points.ResumeUpdates();
答案 1 :(得分:0)
这是一个很酷的控制,我以前从未注意过。请不要将我的帖子标记为答案。它只是大声说话。
你能清除图表并在LEFT PAN上重新分配数据吗?
您可以向您的班级声明一些全局变量:
private int x_index;
private DataTable m_dataTable;
在Chart的构造函数中,连接AxisViewChanging和AxisViewChanged事件处理程序:
public Form1() {
InitializeComponent();
x_index = -1;
if (graph1.Series == null) {
graph1.Series = new SeriesCollection();
}
graph1.AxisViewChanging += new EventHandler<ViewEventArgs>(graph1_AxisViewChanging);
graph1.AxisViewChanged += new EventHandler<ViewEventArgs>(graph1_AxisViewChanged);
}
现在,修改Get Data例程以将数据收集到新的DataTable中:
private void GetData(string channel, int scanStart, int step, int scanEnd) {
m_dataTable = new DataTable();
var col1 = m_dataTable.Columns.Add("Index", typeof(int));
var col2 = m_dataTable.Columns.Add("Data", typeof(Series));
for (var i = scanStart; i + step <= scanEnd; i += step) {
var sample = _expeData.ReadSample(channel, i);
var row = m_dataTable.NewRow();
row[col1] = i;
row[col2] = new Series(sample);
m_dataTable.Rows.Add(row);
}
}
下面的更改处理程序只记录您当前的索引:
private void graph1_AxisViewChanging(object sender, ViewEventArgs e) {
// below is probably wrong, but basically, get the x_axis before the data changes
x_index = Convert.ToInt32(graph1.Series[0].XValueMember);
}
下面的Changed处理程序用于显示您在上面收集的x_index
之前的数据:
private void graph1_AxisViewChanged(object sender, ViewEventArgs e) {
var index = Convert.ToInt32(graph1.Series[0].XValueMember);
if (index < x_index) {
graph1.Series.Clear();
for (int i = index; i < index + 50; i++) {
var pt = (Series)m_dataTable.Rows[i]["Data"];
graph1.Series.Add(pt);
}
}
}
在上面,值50
将是您想要计算的任何值。它应该在代码中的某处确定为变量或常量,以使它看起来不像幻数。
我不确定这是否符合您的要求。
如果确实如此,当有人向右移动时,需要调整它来计算您的值 - 因为我的代码可能会破坏该功能。 ;)
答案 2 :(得分:0)
您是否尝试过使用InsetXY方法?
chart.Series[0].Points.InsertXY(0, i, sample)
通过这种方式,您将始终添加到第一个位置。
不知道这是否适合你的问题。
答案 3 :(得分:0)
我发现解决方案是使用Series [0] .Sort()函数。我修改了原始代码如下:
for (var i = scanStart; i + _step <= scanEnd; i += _step)
{
var sample = _expeData.ReadSample(_channel, i);
Series[0].Points.AddXY(i, sample);
}
Series[0].Sort(new PointComparer());
我添加了一个私有类来处理排序:
private class PointComparer : IComparer<DataPoint>
{
public int Compare(DataPoint x, DataPoint y)
{
return (int)(x.XValue - y.XValue);
}
}
根据XValue对点进行排序。当点数开始增长到大约40,000以上时,会有轻微的性能损失。但是,我可以通过仅加载屏幕上可见的点(在现代宽屏显示器上最多大约2,000点)然后在超过一定量(我使用20,000)时修剪来减轻这种情况。这会导致在相对大量的平移后偶尔出现打嗝,但这并不是很明显。