我最近问了这个问题:
Replay Recorded Data Stream in F#并将该代码与我在这里找到的功能子集结合起来:
http://www.mattssoftwareblog.com/?p=271
结合起来看起来像这样:
#r "System.Reactive"
#r "System.CoreEx"
#r "FSharp.PowerPack"
#r "WindowsBase"
#r "PresentationCore"
#r "PresentationFramework"
#r "System.Xaml"
#r "System.Interactive.dll"
open System
open System.Linq
open System.Collections.Generic
open System.Net
open System.IO
open System.Threading
open System.Windows
open System.Windows.Input
open System.Windows.Controls
open System.Windows.Shapes
open System.Windows.Media
open System.Xaml
我需要使用此处生成的事件(来自我之前的 SO 问题):
let prices = [ (0, 10.0); (1000, 10.5); (500, 9.5); (2500, 8.5); (500, 10.0); (1000, 10.5); (500, 9.5); (2500, 8.5) ]
let evt = new Event<float>()
async { for delay, price in prices do
do! Async.Sleep(delay)
evt.Trigger(price) }
|> Async.StartImmediate
evt.Publish.Add(printfn "Price updated: %A")
用作此处随机创建的行的数据源(以下代码来自我提到的博客文章):
let create f =
Observable.Create<_>(fun x ->
f x
new System.Action((fun () -> ())))
let fromEvent (event:IEvent<_,_>) = create (fun x -> event.Add x.OnNext)
// Random Walker
let rand = Random()
let mutable m = 0.
let randomWalker() =
m <- m + (rand.NextDouble() * 10.) - 5.
m
let timer = new System.Timers.Timer()
timer.Interval <- 100.
let timerObs = (timer.Elapsed |> fromEvent).Select(fun _ -> randomWalker())
let chartWindow = new Window(Height = 600., Width = 600.)
let canvas = new Canvas()
chartWindow.Content <- canvas
chartWindow.Show()
let line xs =
let segs =
seq { for x , y in xs |> List.tail ->
LineSegment(Point(x,y), true) :> PathSegment }
let (sx, sy) = xs |> List.head
PathGeometry([PathFigure(Point(sx,sy), segs, false)])
let plot xs (path:Path) =
let now = DateTime.Now
let timeSpan = TimeSpan(0,1,0)
let width = 600.
let height = 600.
let pts = xs |> List.map (fun (x:Timestamped<float>) ->
(600.-(now - (x.Timestamp.DateTime)).TotalMilliseconds * 600. / timeSpan.TotalMilliseconds),x.Value + 300.)
path.Dispatcher.BeginInvoke(new SendOrPostCallback(fun pts -> path.Data <- line (pts :?> (float*float)list)), pts) |> ignore
let trailing (timespan:TimeSpan) (obs:IObservable<'
a>) =
obs.Timestamp()
.Scan([], fun ys x ->
let now = DateTime.Now
let timespan = timespan
x :: (ys |> List.filter (fun x -> (now - x.Timestamp.DateTime) < timespan)))
.Where(fun xs -> xs |> List.length > 1)
// Main Path
let mainPath = new Path(Stroke=Brushes.Blue, StrokeThickness=1.)
canvas.Children.Add(mainPath)
let trailingRandomsSub = (timerObs |> trailing (TimeSpan.FromSeconds(60.))).Subscribe(fun xs -> plot xs mainPath)
timer.Start()
如果将其粘贴到交互式 session 中,您将看到一条蓝线出现,它是随机生成的,而不是使用我的新
evt
Event
.我想我的困惑是不了解如何制作和使用 Observable
来自我的evt
.基本上,我怎样才能使 evt 成为我的蓝线数据源?提前致谢,
鲍勃
最佳答案
在 F# 中,IEvent<'T>
接口(interface)继承自 IObservable<'T>
.这意味着您可以在任何需要观察到的地方使用 F# 事件。
应用程序的最后一点(获取事件、添加时间戳、使用 Scan
获取包含到目前为止生成的项目的列表并绘制进度)可以这样编写:
let trailingRandomsSub =
evt.Publish.Timestamp()
|> Observable.scan (fun l e -> e::l) []
|> Observable.add (fun xs -> plot xs mainPath)
F# 为某些 Rx 函数提供了包装器,因此您可以使用
Observable.scan
,它有一点 F# 友好的语法。 Observable.add
只是 Subscribe
的另一种语法.F# 事件和可观察对象之间的主要区别在于,可观察对象在您附加处理程序时启动。另一方面,您使用
Async.StartImmediate
创建的 F# 事件当 StartImmediate
时立即开始方法被调用(这意味着 - 要使示例正常工作,您需要立即评估所有内容,或者编写一个启动事件的函数)。
关于wpf - 在 F# 中处理事件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5386633/