Avalonia.FuncUI icon indicating copy to clipboard operation
Avalonia.FuncUI copied to clipboard

Underlying Control Access

Open keldor314 opened this issue 4 years ago • 2 comments

So I'm doing some things involving drag and drop, and am very interested in functions like GetInputElementsAt(). Note that using the obvious functions like OnPointerEnter() will not work since in a drag and drop operation, the first control has focus as long as the mouse button is down, and other potential target controls will not get mouse events during this time. The problem is that this function returns a list of Avalonia controls, and I need some way of going backwards to figure out how to properly wire this information into an update event.

What's the best way of going about this sort of thing? Would I create a custom user control and wire up an event? Is there a way to do things like AddHandler() for DragDrop events and have this visible to the DSL? What happens when things are regenerated in create()?

Finally, how does using inherited classes in the DSL work? For instance, maybe I have a Button and I need to put handlers on inherited InputElement events. I don't see any of this in the examples.

keldor314 avatar Dec 30 '20 00:12 keldor314

I've never really used Drag&Drop in my applications, so had to google for it. Found this StackOverflow questions to see how this should work

Few extension methods

[<AutoOpen>]
module Extensions =
    type Interactive with
        static member allowDrop<'t when 't :> Interactive>(isAllowed) : IAttr<'t> =
            AttrBuilder<'t>.CreateProperty<bool>(DragDrop.AllowDropProperty, isAllowed, ValueNone)

        static member onDrop<'t when 't :> Interactive>(func: DragEventArgs -> unit, ?subPatchOptions) =
            AttrBuilder<'t>.CreateSubscription<DragEventArgs>(DragDrop.DropEvent, func, ?subPatchOptions = subPatchOptions)

Create view


    let view state dispatch =
        StackPanel.create [
            StackPanel.children [
                TextBlock.create [
                    TextBlock.text "drag me"
                    TextBlock.background "purple"
                    TextBlock.height 80.
                    TextBlock.onPointerPressed
                        (fun e ->
                            let data = DataObject()
                            data.Set(DataFormats.Text, System.DateTime.Now.ToLongTimeString())
                            DragDrop.DoDragDrop(e, data, DragDropEffects.Copy) |> ignore)
                ]
                TextBlock.create [
                    TextBlock.allowDrop true
                    TextBlock.text "drop here"
                    TextBlock.background "darkcyan"
                    TextBlock.height 80.
                    TextBlock.onDrop
                        (fun e -> printfn "drop %A" <| e.Data.Get(DataFormats.Text))
                ]
            ]
        ]

And current time get displayed in console.

There is one problem: Drop event raised twice when dragging control (bug), but raised once when dragging file from explorer (normal behavior)

jl0pd avatar Dec 31 '20 16:12 jl0pd

There is a feature planned that would allow you to simple do something with the control reference on render. Don't know when I will have time for it as I currently don't use FuncUI in any work/side project. I'm happy to take PR's tho.

TextBlock.create [
    TextBlock.useViewRef (fun textBlock -> ...)
]

JaggerJo avatar Jan 02 '21 15:01 JaggerJo

There are now 2 ways of doing this:

  1. Using init functions
Button.create [
    Button.init (fun button -> 
        button.Name <- "whatever"
    )
]
  1. Using outlets
let mutable outlet: Button = null

let setOutlet value = outlet <- value
 
View.createWithOutlet setOutlet Button.create []

JaggerJo avatar Mar 06 '23 13:03 JaggerJo