Forum Xamarin.Forms
We are excited to announce that the Xamarin Forums are moving to the new Microsoft Q&A experience. Q&A is the home for technical questions and answers at across all products at Microsoft now including Xamarin!

We encourage you to head over to Microsoft Q&A for .NET for posting new questions and get involved today.

How do I solve event Binding for a Custom Control in XAML

PaulSinnemaPaulSinnema NLMember ✭✭✭

I've got a custom control called MovingFrame. It's a drag-n-drop control. When dropping a dragged item onto another item a DropReceived event is fired. I've got it working with code in code behind but I would like to be able just to bind in XAML. For some reason I always get the following error message:

XFC0009 No property, BindableProperty, or event found for "DropReceived", or mismatching type between value and property.

The solution below works fine but I need code behind code to solve it. I didn't find a solution that solely depends on XAML.

The (partial) code of the custom control looks like this.

    public class MovingFrame : Frame, IDragAndDropMovingView, IDragAndDropHoverableView, IDragAndDropReceivingView
    {
        public delegate void DropReceivedEventHandler(object sender, DropReceivedEventArgs e);

        public event DropReceivedEventHandler DropReceived;

        public void OnDropReceived(IDragAndDropMovingView view)
        {
            DropReceived?.Invoke(this, new DropReceivedEventArgs(view));
        }

        protected override void OnParentSet()
        {
            base.OnParentSet();
            this.InitializeDragAndDrop();
        }
    }

Here's the implementation in XAML:

    <?xml version="1.0" encoding="utf-8" ?>
    <forms:MvxContentPage 
        xmlns="http://xamarin.com/schemas/2014/forms"
        xmlns:forms="clr-namespace:MvvmCross.Forms.Views;assembly=MvvmCross.Forms"
        xmlns:views="clr-namespace:eTabber.Views"
        xmlns:viewmodels="clr-namespace:eTabber.Core.ViewModels;assembly=eTabber.Core"
        xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
        xmlns:d="http://xamarin.com/schemas/2014/forms/design"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:tgb="clr-namespace:TGB.Xamarin.Forms.DragAndDrop;assembly=TGB.Xamarin.Forms"
        mc:Ignorable="d"
        Padding="0"
        x:Class="eTabber.Views.SetView" x:TypeArguments="viewmodels:SetlistViewModel"
        Title="{Binding PageTitle}" >

        <StackLayout
            StyleClass="MainContainer"
            Spacing="0"
            Margin="0"
            Padding="0">
            <Grid>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="*"/>
                </Grid.ColumnDefinitions>

                <Grid.RowDefinitions>
                    <RowDefinition Height="auto" />
                    <RowDefinition Height="auto" />
                    <RowDefinition Height="*" />
                </Grid.RowDefinitions>

                <Label Text="{Binding SetList.Name, Mode=OneWay}" StyleClass="Title" Grid.Row="0"></Label>
                <StackLayout Orientation="Horizontal" Grid.Row="1">
                    <Button Text="Add" Command="{Binding AddCommand}" />
                </StackLayout>
                <!--<ScrollView Margin="0" Padding="0" Orientation="Vertical" Grid.Row="2">-->
                <StackLayout BindableLayout.ItemsSource="{Binding SetItems}" x:Name="SetlistsListView" Grid.Row="2">
                    <BindableLayout.ItemTemplate>
                        <DataTemplate x:Name="RowTemplate">
                            <tgb:MovingFrame x:Name="MovingFrame" 
                                             Padding="0"
                                             Margin="0" 
                                             DropReceived="{Binding DropReceivedHandler}">
                                <StackLayout Orientation="Vertical">
                                    <Label Text="{Binding Set.SetName, Mode=OneWay}"
                                           IsVisible="{Binding TitleVisible, Mode=OneWay}"
                                           StyleClass="Title"
                                           Grid.Row="0">
                                    </Label>
                                    <Grid>
                                        <Grid.ColumnDefinitions>
                                            <ColumnDefinition Width="*"/>
                                            <ColumnDefinition Width="2*"/>
                                        </Grid.ColumnDefinitions>

                                        <Label Text="{Binding Path=Song.Artist.Name, Mode=OneWay}" Grid.Column="0" Grid.Row="0" StyleClass="Label"/>
                                        <Label Text="{Binding Path=Song.Title, Mode=OneWay}" Grid.Column="1" Grid.Row="0" StyleClass="Label"/>
                                    </Grid>
                                </StackLayout>
                            </tgb:MovingFrame>
                        </DataTemplate>
                    </BindableLayout.ItemTemplate>
                </StackLayout>
                <!--</ScrollView>-->
            </Grid>
        </StackLayout>
    </forms:MvxContentPage>

Code behind with the current solution:

using eTabber.Core.ViewModels;
using MvvmCross.Forms.Views;
using System.ComponentModel;
using TGB.Xamarin.Forms.DragAndDrop;
using TGB.Xamarin.Forms.DragAndDrop.DragAndDropEventArgs;
using Xamarin.Forms;

namespace eTabber.Views
{
    [DesignTimeVisible(false)]
    public partial class SetView : MvxContentPage<SetlistViewModel>
    {
        public SetView()
        {
            InitializeComponent();

            SetlistsListView.ChildAdded += SetlistsListView_ChildAdded;
        }

        private void SetlistsListView_ChildAdded(object sender, ElementEventArgs e)
        {
            var movingFrame = e.Element as MovingFrame;

            if (movingFrame != null)
            {
                var viewModel = ViewModel as SetlistViewModel;

                if (viewModel != null)
                {
                    movingFrame.DropReceived -= MovingFrame_DropReceived;
                    movingFrame.DropReceived += MovingFrame_DropReceived;
                }

            }
        }

        private void MovingFrame_DropReceived(object sender, DropReceivedEventArgs e)
        {
            var viewModel = ViewModel as SetlistViewModel;

            if(viewModel != null)
            {
                viewModel.DropReceivedHandler(sender, e);
            }
        }
    }
}

And partial viewmodel class (BaseViewModel is based on MvxViewModel):

using eTabber.Data.Model;
using TGB.Xamarin.Forms.DragAndDrop.DragAndDropEventArgs;
using Xamarin.Forms;

namespace eTabber.Core.ViewModels
{
    public class SetlistViewModel : BaseViewModel<SetList>
    {
        public SetlistViewModel(IMvxNavigationService navigationService) : base(navigationService)
        {
        }

        public void DropReceivedHandler(object sender, DropReceivedEventArgs e)
        {

        }
    }
}

Answers

  • igorkr_10igorkr_10 Member ✭✭✭✭
    edited July 12
    1. Your DropReceived is an event. You don't need to bind it. In XAML Write this:

    DropReceived="DropReceivedHandler"

    and tap F12. Method DropReceivedHandler will be created in your page.

    1. If you want to use binding for actions try ICommand.

    2. For using binding mechanism you need to create BindableProperty

  • PaulSinnemaPaulSinnema NLMember ✭✭✭
    edited July 12

    @igorkr_10 said:
    1. Your DropReceived is an event. You don't need to bind it. In XAML Write this:

    DropReceived="DropReceivedHandler"

    and tap F12. Method DropReceivedHandler will be created in your page.

    1. If you want to use binding for actions try ICommand.

    2. For using binding mechanism you need to create BindableProperty

    That is just the point. I don't want the DropReceivedHandler to be on the Page but I would like to bind to the ViewModel (MvvmCross).

    If you look a f.i. the code of the Button in Xamarin.Forms the 'Clicked' event can be bound to from XAML. How does that work?

  • igorkr_10igorkr_10 Member ✭✭✭✭

    @PaulSinnema said:

    That is just the point. I don't want the DropReceivedHandler to be on the Page but I would like to bind to the ViewModel (MvvmCross).

    If you want to handle user actions in ViewModel use Commands.

    If you look a f.i. the code of the Button in Xamarin.Forms the 'Clicked' event can be bound to from XAML. How does that work?

    Do you mean this?

    <Button Clicked="OnClicked"/>

    This is not the binding. This is one-time setting handler to event. The C# equivalent is

    button.Clicked += OnClicked;

    The button object is a private property of your page like the OnClicked method too.

  • igorkr_10igorkr_10 Member ✭✭✭✭

    Why do you need handler in ViewModel? If you want to do something with UI when dropping item, use handler in page. Because Page is UI layer.
    But if you need to operate with data use Commands in ViewModel because this is data processing layer

  • JarvanJarvan Member, Xamarin Team Xamurai

    How do I solve event Binding for a Custom Control in XAML

    To set binding for an event in xaml, try the following code:

    public class MovingFrame : Frame, IDragAndDropMovingView, IDragAndDropHoverableView, IDragAndDropReceivingView
    {
        public MovingFrame()
        {
        }
    
        public event EventHandler<DropReceivedEventArgs> DropReceived;
    }
    

    Refer to:
    https://docs.microsoft.com/en-us/dotnet/api/xamarin.forms.listview.itemselected?view=xamarin-forms

Sign In or Register to comment.