Home > database >  How can I pass Command parameter from EventToCommandBehavior
How can I pass Command parameter from EventToCommandBehavior

Time:12-28

I am using EventToCommandBehavior with Entry control. Entry is inside DataTemplate of Bindable stacklayout.

I want to pass current item as a command parameter. I am not able to do that. Below is my code.

This is my behavior class

public class EventToCommandBehavior : BehaviorBase<VisualElement>
{
    Delegate eventHandler;
    public static readonly BindableProperty EventNameProperty = BindableProperty.Create("EventName", typeof(string), typeof(EventToCommandBehavior), null, propertyChanged: OnEventNameChanged);
    public static readonly BindableProperty CommandProperty = BindableProperty.Create("Command", typeof(ICommand), typeof(EventToCommandBehavior), null);

    public static readonly BindableProperty CommandParameterProperty = BindableProperty.Create("CommandParameter", typeof(object), typeof(EventToCommandBehavior), null);
    public static readonly BindableProperty InputConverterProperty = BindableProperty.Create("Converter", typeof(IValueConverter), typeof(EventToCommandBehavior), null);

    public object CommandParameter
    {
        get { return GetValue(CommandParameterProperty); }
        set { SetValue(CommandParameterProperty, value); }
    }
    public IValueConverter Converter
    {
        get { return (IValueConverter)GetValue(InputConverterProperty); }
        set { SetValue(InputConverterProperty, value); }
    }
    public string EventName
    {
        get { return (string)GetValue(EventNameProperty); }
        set { SetValue(EventNameProperty, value); }
    }
    public ICommand Command
    {
        get { return (ICommand)GetValue(CommandProperty); }
        set { SetValue(CommandProperty, value); }
    }

    protected override void OnAttachedTo(VisualElement bindable)
    {
        base.OnAttachedTo(bindable);
        RegisterEvent(EventName);
    }

    protected override void OnDetachingFrom(VisualElement bindable)
    {
        DeregisterEvent(EventName);
        base.OnDetachingFrom(bindable);
    }
    static void OnEventNameChanged(BindableObject bindable, object oldValue, object newValue)
    {
        var behavior = (EventToCommandBehavior)bindable;

        if (behavior.AssociatedObject == null) return;

        string oldEventName = (string)oldValue;
        string newEventName = (string)newValue;

        behavior.DeregisterEvent(oldEventName);
        behavior.RegisterEvent(newEventName);
        behavior.CommandParameter = bindable;
    }

    void RegisterEvent(string name)
    {
        if (string.IsNullOrWhiteSpace(name)) return;

        EventInfo eventInfo = AssociatedObject.GetType().GetRuntimeEvent(name);

        if (eventInfo == null)
            throw new ArgumentException(string.Format("EventToCommandBehavior: Can't register the '{0}' event.", EventName));

        MethodInfo methodInfo = typeof(EventToCommandBehavior).GetTypeInfo().GetDeclaredMethod("OnEvent");
        eventHandler = methodInfo.CreateDelegate(eventInfo.EventHandlerType, this);
        eventInfo.AddEventHandler(AssociatedObject, eventHandler);
    }

    void DeregisterEvent(string name)
    {
        if (string.IsNullOrWhiteSpace(name) || eventHandler == null)
            return;

        EventInfo eventInfo = AssociatedObject.GetType().GetRuntimeEvent(name);

        if (eventInfo == null)
            throw new ArgumentException(string.Format("EventToCommandBehavior: Can't de-register the '{0}' event.", EventName));

        eventInfo.RemoveEventHandler(AssociatedObject, eventHandler);
        eventHandler = null;
    }

    void OnEvent(object sender, object eventArgs)
    {
        if (Command == null) return;

        object resolvedParameter;
        if (CommandParameter != null)
        {
            resolvedParameter = CommandParameter;
        }
        else if (Converter != null)
        {
            resolvedParameter = Converter.Convert(eventArgs, typeof(object), null, null);
        }
        else
        {
            resolvedParameter = eventArgs;
        }


        if (Command.CanExecute(resolvedParameter))
            Command.Execute(resolvedParameter);
    }

}
public class BehaviorBase<T> : Behavior<T> where T : BindableObject
{
    public T AssociatedObject { get; private set; }
    protected override void OnAttachedTo(T bindable)
    {
        base.OnAttachedTo(bindable);
        AssociatedObject = bindable;

        if (bindable.BindingContext != null)
            BindingContext = bindable.BindingContext;

        bindable.BindingContextChanged  = OnBindingContextChanged;
    }
    protected override void OnDetachingFrom(T bindable)
    {

        base.OnDetachingFrom(bindable);
        bindable.BindingContextChanged -= OnBindingContextChanged;
        AssociatedObject = null;
    }

    void OnBindingContextChanged(object sender, EventArgs e)
    {
        OnBindingContextChanged();
    }

    protected override void OnBindingContextChanged()
    {
        base.OnBindingContextChanged();
        BindingContext = AssociatedObject.BindingContext;
    }
}

This is my xaml code

<Entry     
MaxLength="5"
HorizontalOptions="Center"
HeightRequest="42">
<Entry.Behaviors>
<helpers:EventToCommandBehavior
    Command="{Binding BindingContext.RefreshCommand, Source={x:Reference multitenderPage}}"
             CommandParameter="{Binding .}" EventName="TextChanged"></helpers:EventToCommandBehavior>
</Entry.Behaviors>
</Entry>

This is viewmodel code

public ICommand RefreshCommand { get; set; }
RefreshCommand = new Command<TendersList>(Refresh1);

When I do Command<TendersList>, command is not hitting. When I do Command(async (args) =>, I am getting old and new value of Entry in args, instead of that I want to get item or itemId from param.

How can I get current record/item of list. when behavior hitting?

CodePudding user response:

The reason this happens is the last line of this method, every time you set a new event it will override whatever you have in your Command parameter and it will replace it with that specific object:

static void OnEventNameChanged(BindableObject bindable, object oldValue, object newValue)
{
    var behavior = (EventToCommandBehavior)bindable;

    if (behavior.AssociatedObject == null) return;

    string oldEventName = (string)oldValue;
    string newEventName = (string)newValue;

    behavior.DeregisterEvent(oldEventName);
    behavior.RegisterEvent(newEventName);
    behavior.CommandParameter = bindable;
}

CodePudding user response:

Do you want to get the Entry's text after trigerring event RefreshCommand?

If yes, you can pass the Entry itself as the parameter of the CommandParameter.

Please refer the following code:

        <StackLayout BindableLayout.ItemsSource="{Binding User.Items}"
                     Orientation="Vertical"
                     Margin="0,10,0,0"
                     Spacing="20">
            <BindableLayout.ItemTemplate>
                <DataTemplate>
                    <Entry  x:Name="myEntry"   MaxLength="5"
                            HorizontalOptions="FillAndExpand" HeightRequest="42">
                        <Entry.Behaviors>
                            <bindablelayoutdemo:EventToCommandBehavior
         Command="{Binding BindingContext.RefreshCommand, Source={x:Reference myPage}}"
         CommandParameter="{x:Reference myEntry}"  EventName="TextChanged"></bindablelayoutdemo:EventToCommandBehavior>
                        </Entry.Behaviors>

                    </Entry>
                </DataTemplate>
            </BindableLayout.ItemTemplate>
        </StackLayout>

Note:

myPage is the x:Name="myPage" of current page.

  • Related