Drag and Drop in Silverlight Using F # and Asynchronous Workflows

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

I'm just trying to drag a rectangle onto the canvas using two loops for two states (waiting and dragging), the idea I got from Thomas Petrichek, the book "Real Programming on Functional", but I ran into the problem:

Unlike WPF or WinForms, Silverlight MouseEventArgs does not carry information about the state of the button, so I cannot return from the drag and drop cycle, checking if the left mouse button is clicked. I managed to solve this by introducing a mutable flag.

Does anyone have a solution for this that is not related to volatile state?

Here's the relevant piece of code (please excuse the careless drag and drop code that anchors 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())
        ()

Thanks so much for your time!

+3
source share
1 answer

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

- AwaitObservable (. ), , Observable.map ( , ),

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 # (), t -, :

// 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) 
      () ))
+5

Source: https://habr.com/ru/post/1748766/


All Articles