Search code examples
c#drag-and-dropevent-handlingmauigesture-recognition

MAUI: How to bind Event Properties(sender, EventArgs) in code; binding to ViewModel or Code Behind


I have successfully bound a DrogGestureRecognizer's Drop event to the CodeBehind using XAML markup as shown below:

<Label Text="MyTestItem" >
  <Label.GestureRecognizers>
    <DropGestureRecognizer AllowDrop="True" Drop="DropGestureRecognizer_Drop_Item"/>
  </Label.GestureRecognizers>
</Label>

However I need to accomplish the same thing in c# code as I am creating a view/control that must be dynamically built due to recursion. My requirement is that I get both the arguments from the event (sender and DropEventArgs). I'm open to a solution that works with the CodeBehind or the ViewModel.

Below is my attempt, however I'm struggling with the MultiBinding. You can see my comments in the code:

RelativeBindingSource rbs2 = new   RelativeBindingSource(RelativeBindingSourceMode.FindAncestorBindingContext, ancestorType: typeof(TreeViewPageViewModel));

DropGestureRecognizer dropgGr = new DropGestureRecognizer { AllowDrop = true };

dropgGr.Bind(DropGestureRecognizer.DropCommandProperty,
    mode: BindingMode.OneTime,
    source: rbs2,
    path: "DropGestureRecognizerDropCommand"
);

dropgGr.SetBinding(DropGestureRecognizer.DropCommandParameterProperty, new MultiBinding
{
    Bindings = new Collection<BindingBase>
    {
        new Binding("."), // Is this the sender?
        new Binding("")   // How to set the DropEvenArgs here?  x:TypeArguments="DropEventArgs"
    }
});
label.GestureRecognizers.Add(dropgGr);

SOLVED: The goal is to allow my control to drag and drop from one label to another label inside the control, not external desktop drag/drop integration. I have been able to attach a Flyout menu to my label and a TapGestureRecognizer and get it to successfully call a command in my ViewModel with the code below.

            RelativeBindingSource rbs = new RelativeBindingSource(RelativeBindingSourceMode.FindAncestorBindingContext, ancestorType: typeof(ITreeViewPageViewModel));

            // Context Menu --------------------------------------
            var menuItem1 = new MenuFlyoutItem
            {
                Text = "Delete Item",
                CommandParameter = xamlItem.ItemId
            };

            menuItem1.Bind(MenuFlyoutItem.CommandProperty,
                mode: BindingMode.OneTime,
                source: rbs,
                path: "DeleteItemCommand"
            );

            var menuFlyout = new MenuFlyout();

            menuFlyout.Add(menuItem1);
            FlyoutBase.SetContextFlyout(label, menuFlyout);

            // TagGesture ----------------------------------------
            TapGestureRecognizer tgr = new TapGestureRecognizer
            {
                Buttons = ButtonsMask.Primary,
                CommandParameter = xamlItem.ItemId,
                NumberOfTapsRequired = 2
            };
            tgr.Bind(TapGestureRecognizer.CommandProperty,
                mode: BindingMode.OneTime,
                source: rbs,
                path: "ItemSelectedCommand"
                );

            label.GestureRecognizers.Add(tgr);

In the above examples notice the commandParameter is being set, however in this case the parameter is an ID from my data. What I really need for my DragDrop Gestures, is to attach to the existing Drop "event" rather than a "Command". It turns out it was surpisingly easy:

            DropGestureRecognizer dropGr = new DropGestureRecognizer { AllowDrop = true };
            dropGr.Drop += DropGr_Drop_Item; 
            label.GestureRecognizers.Add(dropGr);
        
        // This method is in the same class as the above code, and since I need to call a method in my
        // ViewModel you can see the last line that is used to accomplish this.
        private void DropGr_Drop_Item(object? sender, DropEventArgs e)
        {
            var data = e.Data.Properties["Text"].ToString();
            var sourceItemId = e.Data.Properties["ItemId"].ToString();


            var label = (sender as Element)?.Parent as Label;
            var targetItemId = label.StyleId.Substring(5);

            
            (this.BindingContext as ITreeViewPageViewModel).ReOrderItems(sourceItemId, targetItemId); 
   
        }

Solution

  • What I really needed for my DragDrop Gestures was to attach to the existing Drop "event" rather than a "Command". It turns out it was surpisingly easy:

        DropGestureRecognizer dropGr = new DropGestureRecognizer { AllowDrop = true };
                dropGr.Drop += DropGr_Drop_Item; 
                label.GestureRecognizers.Add(dropGr);
            
            // This method is in the same class as the above code, and since I need to call a method in my
            // ViewModel you can see the last line that is used to accomplish this.
            private void DropGr_Drop_Item(object? sender, DropEventArgs e)
            {
                var data = e.Data.Properties["Text"].ToString();
                var sourceItemId = e.Data.Properties["ItemId"].ToString();
    
    
                var label = (sender as Element)?.Parent as Label;
                var targetItemId = label.StyleId.Substring(5);
    
                
                (this.BindingContext as ITreeViewPageViewModel).ReOrderItems(sourceItemId, targetItemId); 
       
            }