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

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

问题描述

我正在使用F#和异步工作流程来实现Silverlight中的拖放。

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

我只是试图在画布上拖动一个矩形,使用两个州的两个循环(等待和拖动),我从Tomas 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不携带关于按钮状态的信息,因此我不能通过检查是否不再按下鼠标左键从拖动回路返回。我只是通过引入一个可变标志来解决这个问题。

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?

以下是相关的代码部分(请原谅鼠标指针中的矩形拖动代码):

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())
        ()

非常感谢你的时间!

推荐答案

解决这个问题的方法是使用一个重载的 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).

这是代码 - 它实际上使用了一种方法(见下文)的一个更好的选择,因为它可以与 c> C> c> 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()  

As据我所知,F#库中尚未提供重载的 AwaitObservable 方法,但您可以从t 他的书的网站,或者你可以使用以下代码:

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) 
      () ))

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

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