dotnet / aspnetcore

ASP.NET Core is a cross-platform .NET framework for building modern cloud-based web applications on Windows, Mac, or Linux.
https://asp.net
MIT License
35.37k stars 9.99k forks source link

UIMouseEventArgs doesn't provide reference to the element clicked #12259

Open VR-Architect opened 5 years ago

VR-Architect commented 5 years ago

I have not been able to find any sample code to do this. Without being able to know which element was clicked, it seems impossible to use UIMouseEventArgs for moving of elements on the page. I ran into this same issue with the Drag-n-Drap events args too.

If this feature is already in the product, please provide an sample. There is something like this for keydown event in the documents, but not mouse events.

If not already in the product, this is a must have for go live please :)

mkArtakMSFT commented 5 years ago

Thanks for contacting us, @VR-Architect. Given there are too many ways this can work, we tend to leave this up to the developers to do the way they want - through JS Interop. If you intended to stick to a Blazor-specific approach, you can use lambdas per element, to handle the way you want it to. We'll look into providing a sample post 3.0 release showing how this can be done.

VR-Architect commented 5 years ago

Here is an example of the way I did it without JSInterop; however, if the mouse is moved too quickly the moved element gets left behind. If the mouse moves slowly, the drag works fine. But to my original question, I was able to get a reference by using lamba with an int.

@page "/"

<div class="container">
    <div id="items-container" class="base-container" @ref="itemsContainer">
        <div class="header">Items</div>

        @foreach (var item in dragObjects)
        {
            <div class="@item.css" @onmousedown="@((e) => { mouseDown(e, item.id); })" @onmousemove="@mouseMove" @onmouseup="@mouseUp" style="@item.style;">
                TODO Item @item.id
            </div>
        }

    </div>
    <div id="todos-container" class="base-container" @ref="todosContainer">
        <div class="header">TODO</div>
    </div>
</div>

<style>
    html, body {
        width: 100%;
        height: 100%;
        overflow: hidden;
    }

    .container {
        width: 100%;
        height: 100%;
        display: flex;
        flex-direction: row;
        justify-content: space-around;
        align-items: center;
    }

    .base-container {
        min-width: 300px;
        min-height: 400px;
        //border: 1px solid rgba(15, 15, 15, 0.2);
        box-shadow: 0px 0px 20px 2px rgba(15, 15, 15, 0.2);
        padding: 25px;
    }

    .header {
        width: 100%;
        font-family: Oxygen, sans-serif;
        font-size: 22px;
        text-align: center;
    }

    .no-todo-container {
        border: 1px solid rgba(15, 15, 15, 0.2);
        box-shadow: 0px 0px 5px 10px rgba(15, 15, 15, 0.2);
    }

    .todo-container {
        border: 1px solid rgba(15, 15, 15, 0.2);
        box-shadow: 0px 0px 5px 10px rgba(15, 15, 15, 0.2);
    }

    .todo-item {
        //border-bottom: 1px solid;
        width: 250px;
        height: 40px;
        position: absolute;
        background-color: #F44336;
        color: #FFFFFF;
        font-family: Oxygen, sans-serif;
        font-size: 20px;
        padding: 7px;
        margin: 10px 0 10px 0;
        box-shadow: inset 1px 1px 10px 0 rgba(15, 15, 15, 0.2);
        border-radius: 3px;
        cursor: pointer;
    }

    .moving {
        background-color: #E57373;
    }

    .snap {
        width: 301px;
        height: 30px;
        margin-top: 30px;
        margin-bottom: 5px;
    }

    .over {
        background-color: rgba(15, 15, 15, 0.2);
        border: 2px solid #F44336;
        border-radius: 5px;
    }
</style>

@code{

    ElementRef itemsContainer;
    ElementRef todosContainer;
    Vector2 mouseOffset = new Vector2 { top = 0, left = 0 };
    bool isMouseDown = false;
    List<DraggableObject> dragObjects { get; set; }
    int selectedItemId { get; set; }

    protected override void OnInit()
    {
        dragObjects = new List<DraggableObject>();
        dragObjects.Add(new DraggableObject { id = 0, css = "todo-item", top = 150, left = 400, style = "top: 150px; left: 400px;" });
        dragObjects.Add(new DraggableObject { id = 1, css = "todo-item", top = 200, left = 400, style = "top: 200px; left: 400px;" });
    }

    void mouseDown(UIMouseEventArgs args, int id)
    {
        isMouseDown = true;
        selectedItemId = id;
        mouseOffset.left = args.ClientX - dragObjects[selectedItemId].left;
        mouseOffset.top = args.ClientY - dragObjects[selectedItemId].top;
        dragObjects[selectedItemId].css += " moving";
    }

    void mouseMove(UIMouseEventArgs args)
    {
        //e.preventDefault(); ///< Stops the Default Element Bahiavor
        if (isMouseDown)
        {
            dragObjects[selectedItemId].left = args.ClientX - mouseOffset.left;
            dragObjects[selectedItemId].top = args.ClientY - mouseOffset.top;
            dragObjects[selectedItemId].style = "top: " + dragObjects[selectedItemId].top + "px;left: " + dragObjects[selectedItemId].left + "px;";
        }
    }

    void mouseUp(UIMouseEventArgs args)
    {
        isMouseDown = false;
        if (selectedItemId > -1)
        {
            dragObjects[selectedItemId].css = "todo-item";
            selectedItemId = -1;
        }
    }

    public class Vector2
    {
        public long left { get; set; }
        public long top { get; set; }
    }

    public class DraggableObject
    {
        public int id { get; set; }
        public string css { get; set; }
        public ElementRef el { get; set; }
        public long top { get; set; }
        public long left { get; set; }
        public string style { get; set; }
        public List<DraggableObject> children { get; set; }
    }
}
mkArtakMSFT commented 5 years ago

Moving this to Backlog after seeing no community involvement here. We'll reconsider in the future, if we see clear need for this from community.

LukasWillin commented 4 years ago

Hi everyone

I want to add to this issue. I am currently trying to have a list of elements where each can be clicked.

However it feels quiet overkill to add a specific event handler for each element in that list. Another concern would be what if I update the list? Then I'd need to reapply the event handlers everytime I update it. (I am aware of display: none)

In javascript I would apply the handler to a parent element and then get the data from e.target. Like this I only need to use one event handler.

It would be nice if there was some sort of identification of the event target. For example a number or string by which I can lookup the value from a List within c#.

Cheers Lukas