拖放Silverlight中使用F#和异步工作流 [英] Drag and Drop in Silverlight with F# and Asynchronous Workflows

查看:163
本文介绍了拖放Silverlight中使用F#和异步工作流的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想实现拖放使用F#和异步工作流在Silverlight下降。

I'm trying to implement drag and drop in Silverlight using F# and asynchronous workflows.

我只是试图拖动画布上的一个矩形,用两个循环的两种状态(等待并拖动),一个想法,我从托马斯Petricek的著作现实世界的函数编程了,但我跑一个问题:

I'm simply trying to drag around a rectangle on the canvas, using two loops for the the two states (waiting and dragging), an idea I got from Tomas Petricek's book "Real-world Functional Programming", but I ran into a problem:

与WPF或WinForms的,Silverlight中的MouseEventArgs不携带有关按钮状态的信息,这样我就可以从拖动循环通过检查鼠标左键不再是pressed不归。我只设法通过引入可变标志,以解决这个问题。

Unlike WPF or WinForms, Silverlight's MouseEventArgs do not carry information about the button state, so I can't return from the drag-loop by checking if the left mouse button is no longer pressed. I only managed to solve this by introducing a mutable flag.

有没有人有一个解决方案,不涉及可变状态?

Would anyone have a solution for this, that does not involve mutable state?

下面是有关的code部分(请原谅马虎拖拉code,它捕捉矩形鼠标指针):

Here's the relevant code part (please excuse the sloppy dragging code, which snaps the rectangle to the mouse pointer):

type MainPage() as this =
    inherit UserControl()
    do
        Application.LoadComponent(this, new System.Uri("/SilverlightApplication1;component/Page.xaml", System.UriKind.Relative))
    let layoutRoot : Canvas = downcast this.FindName("LayoutRoot")
    let rectangle1 : Rectangle = downcast this.FindName("Rectangle1")

    let mutable isDragged = false

    do
        rectangle1.MouseLeftButtonUp.Add(fun _ -> isDragged <- false)

        let rec drag() = async {
            let! args = layoutRoot.MouseMove |> Async.AwaitEvent
            if (isDragged) then
                Canvas.SetLeft(rectangle1, args.GetPosition(layoutRoot).X)
                Canvas.SetTop(rectangle1, args.GetPosition(layoutRoot).Y)
                return! drag()
            else
                return()
            } 
        let wait() = async {
            while true do
                let! args = Async.AwaitEvent rectangle1.MouseLeftButtonDown
                isDragged <- true
                do! drag()
            }

        Async.StartImmediate(wait())
        ()

非常感谢您的时间!

Thank you very much for your time!

推荐答案

解决这个问题的方法是使用一个重载 AwaitEvent ,允许你等待两个事件。而不是仅仅等待的MouseMove ,也可以等待的MouseUp 事件 - 在第一种情况下,你可以继续移动和在第二种情况下,你可以从循环返回并停止拖动和放大器;下降(这实际上是在这本书中的部分后面讨论的 16.4.5 的)

The way to solve this issue is to use an overloaded AwaitEvent that allows you to wait for two events. Instead of just waiting for MouseMove, you can also wait for the MouseUp event - in the first case, you can continue moving and in the second case, you can return from the loop and stop drag&drop (this is actually discussed later in the book in section 16.4.5).

下面是code - 它实际上使用的方法 AwaitObservable 变种(见下文),这是一般一个更好的选择,因为它与<工作code> Observable.map 和类似的组合程序(如果你想利用这些)。

Here is the code - it actually uses AwaitObservable variant of the method (see below), which is a better choice in general, because it works with Observable.map and similar combinators (in case you wanted to use these).

let! args = Async.AwaitObservable(layoutRoot.MouseMove, layoutRoot.MouseUp)
match args with
| Choice1Of2(args) ->
    // Handle the 'MouseMove' event with 'args' here
    Canvas.SetLeft(rectangle1, args.GetPosition(layoutRoot).X)  
    Canvas.SetTop(rectangle1, args.GetPosition(layoutRoot).Y)  
    return! drag()  
| Choice2Of2(_) ->
    // Handle the 'MouseUp' event here
    return()  

据我所知,重载 AwaitObservable 方法不是在F#库(还),但您可以从弄不明白的他的书网站,或者您可以使用下面的code:

As far as I know, the overloaded AwaitObservable method is not available in the F# libraries (yet), but you can get it from the book's web site, or you can use the following code:

// Adds 'AwaitObservable' that takes two observables and returns
// Choice<'a, 'b> containing either Choice1Of2 or Choice2Of2 depending
// on which of the observables occurred first
type Microsoft.FSharp.Control.Async with   
  static member AwaitObservable(ev1:IObservable<'a>, ev2:IObservable<'b>) = 
    Async.FromContinuations((fun (cont,econt,ccont) -> 
      let rec callback1 = (fun value ->
        remover1.Dispose()
        remover2.Dispose()
        cont(Choice1Of2(value)) )
      and callback2 = (fun value ->
        remover1.Dispose()
        remover2.Dispose()
        cont(Choice2Of2(value)) )
      // Attach handlers to both observables
      and remover1 : IDisposable  = ev1.Subscribe(callback1) 
      and remover2 : IDisposable  = ev2.Subscribe(callback2) 
      () ))

这篇关于拖放Silverlight中使用F#和异步工作流的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆