我正试图在winforms中创建一个图表,该图表数据绑定到内存中的列表,并随着列表的变化而动态更新。这是我的代码:
open System
open System.Linq
open System.Collections
open System.Collections.Generic
open System.Drawing
open System.Windows.Forms
open System.Windows.Forms.DataVisualization
open System.Windows.Forms.DataVisualization.Charting
let link = new LinkedList<double>()
let rnd = new System.Random()
for i in 1 .. 10 do link.AddFirst(rnd.NextDouble()) |> ignore
let series = new Series()
let chart = new System.Windows.Forms.DataVisualization.Charting.Chart(Dock = DockStyle.Fill, Palette = ChartColorPalette.Pastel)
series.Points.DataBindY(link)
let form = new Form(Visible = true, Width = 700, Height = 500)
form.Controls.Add(chart)
let formloop = async {
while not chart.IsDisposed do
link.AddFirst((new System.Random()).NextDouble()) |> ignore
link.RemoveLast()
}
do
Async.StartImmediate(formloop)
Application.Run(form)
Console.WriteLine("Done")
Console.ReadLine() |> ignore
异步似乎可以工作,但图表从未显示任何内容。它只是显示一个空白窗口。我做错了什么?
LinkedList<T>
无法发出已更新的信号,因此Chart
无法知道何时重新绘制自己。
为了使数据绑定更新视图,源列表必须实现IBindingList
,并在内容更改时引发适当的事件。
另外,我必须指出,从非UI线程(如代码中的chart.IsDisposed
)直接访问UI属性/方法是危险的。在WinForms中,这种限制很少被实际执行,所以有时这似乎很好,但后来在客户的机器上崩溃,无法连接调试器。
-
您需要将该系列添加到图表的
SeriesCollection
中。chart.Series.Add series
-
您需要构造一个图表区域,并将其添加到图表的
ChartAreaCollection
中。let area = new ChartArea() chart.ChartAreas.Add area
-
您需要确保在设置图表和表单后调用数据绑定方法。
... form.Controls.Add chart series.Points.DataBindY link
-
正如Fyodor Soikin的回答中所提到的,现在没有办法将绑定集合的更改传达给该系列的
DataPointCollection
。我不太确定IBindingList
是否是一个合适的回应;虽然可以挂接到ListChanged
事件,但我们也可以直接操作系列的DataPointCollection
。let formloop = async{ while not chart.IsDisposed do series.Points.RemoveAt 0 series.Points.AddY(rnd.NextDouble()) |> ignore do! Async.Sleep 100 }
- 最后,我想指出约翰·阿特伍德的这一贡献,他解决了费奥多尔提出的两个问题;数据绑定问题(通过不使用它)和UI线程安全问题