Binding not working with MVVM in xamarin form. No property, bindable property, or event found for 'ItemSelected',

huangapple go评论111阅读模式
英文:

Binding not working with MVVM in xamarin form. No property, bindable property, or event found for 'ItemSelected',

问题

I am trying to bind my ListView ItemSelected with my ViewModel using MVVM in xamarin. For some I am getting a compile time error " No property, bindable property, or event found for 'ItemSelected', or mismatching type between value and property. Scheduler" I have binded my ViewModel in the UI code Behind. Below is my code

UI Code Behind

[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class ClientPage : ContentPage
{
    private ClientViewModel viewModel { get; }
    public ClientPage()
    {
        InitializeComponent();
        BindingContext = viewModel = new ClientViewModel();
    }
}

ViewModel

public class ClientViewModel : BaseViewModel
{
    public ObservableCollection<Client> Clients { get; set; }
    private Client _SelectedItem { get; set; }
    public Client SelectedClient
    {
        get
        {
            return _SelectedItem;
        }
        set
        {
            _SelectedItem = value;
            Task.Run(async () => await ShowClientDetailModal(_SelectedItem));
        }
    }
}

UI

<StackLayout>
    <ListView x:Name="ClientListView"
              ItemsSource="{Binding Clients}"
              VerticalOptions="FillAndExpand"
              HasUnevenRows="true"
              RefreshCommand="{Binding LoadClientsCommand}"
              IsPullToRefreshEnabled="True"
              IsRefreshing="{Binding IsBusy, Mode=OneWay}"
              CachingStrategy="RecycleElement"
              ItemSelected="{Binding SelectedClient}">
        <ListView.ItemTemplate>
            <DataTemplate>
                <ViewCell>
                    <ViewCell.ContextActions>
                        <MenuItem Clicked="OnDelete_Clicked" Text="Delete" CommandParameter="{Binding .}"/>
                    </ViewCell.ContextActions>
                    <StackLayout Padding="10">
                        <Label Text="{Binding FullName}"
                               d:Text="{Binding .}"
                               LineBreakMode="NoWrap"
                               Style="{DynamicResource ListItemTextStyle}" />
                    </StackLayout>
                </ViewCell>
            </DataTemplate>
        </ListView.ItemTemplate>
    </ListView>
</StackLayout>
英文:

I am trying to bind my ListView ItemSelected with with my ViewModel using MVVM in xamarin. For some I am getting a compile time error " No property, bindable property, or event found for 'ItemSelected', or mismatching type between value and property. Scheduler" I have binded my ViewModel in the UI code Behind. Below is my code

UI Code Behind

  [XamlCompilation(XamlCompilationOptions.Compile)]
    public partial class ClientPage : ContentPage
    {
        private ClientViewModel viewModel {get;}
        public ClientPage()
        {
            InitializeComponent();
            BindingContext = viewModel = new ClientViewModel();
        }
    }

ViewModel

 public class ClientViewModel : BaseViewModel
    {
 public ObservableCollection&lt;Client&gt; Clients { get; set; }
        private Client _SelectedItem { get; set; }
        public Client SelectedClient
        {
            get
            {
                return _SelectedItem;
            }
            set
            {
                _SelectedItem = value;
                Task.Run(async () =&gt; await ShowClientDetailModal(_SelectedItem));
            }
        }
    }

UI

 &lt;StackLayout&gt;
        &lt;ListView x:Name=&quot;ClientListView&quot;
                  ItemsSource=&quot;{Binding Clients}&quot;
                  VerticalOptions=&quot;FillAndExpand&quot;
                  HasUnevenRows=&quot;true&quot;
                  RefreshCommand=&quot;{Binding LoadClientsCommand}&quot;
                  IsPullToRefreshEnabled=&quot;True&quot;
                  IsRefreshing=&quot;{Binding IsBusy, Mode=OneWay}&quot;
                  CachingStrategy=&quot;RecycleElement&quot;
                  ItemSelected=&quot;{Binding SelectedClient}&quot;&gt;
            &lt;ListView.ItemTemplate&gt;
                &lt;DataTemplate&gt;
                    &lt;ViewCell&gt;
                        &lt;ViewCell.ContextActions&gt;
                            &lt;MenuItem Clicked=&quot;OnDelete_Clicked&quot; Text=&quot;Delete&quot; CommandParameter=&quot;{Binding .}&quot;/&gt;
                        &lt;/ViewCell.ContextActions&gt;
                        &lt;StackLayout Padding=&quot;10&quot;&gt;
                                   &lt;Label Text=&quot;{Binding FullName}&quot;
                                   d:Text=&quot;{Binding .}&quot;
                                   LineBreakMode=&quot;NoWrap&quot;
                                   Style=&quot;{DynamicResource ListItemTextStyle}&quot; /&gt;
                        &lt;/StackLayout&gt;
                    &lt;/ViewCell&gt;
                &lt;/DataTemplate&gt;
            &lt;/ListView.ItemTemplate&gt;
        &lt;/ListView&gt;
    &lt;/StackLayout&gt;

答案1

得分: 3

ItemSelected 是一个事件,而不是可绑定属性,你需要的是将该事件转换为命令的行为。你可以在 Microsoft 文档中找到一个示例,链接在这里:https://learn.microsoft.com/en-us/xamarin/xamarin-forms/app-fundamentals/behaviors/reusable/event-to-command-behavior

以下是示例代码:

public class EventToCommandBehavior : BehaviorBase<View>
{
    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 string EventName
    {
        get { return (string)GetValue(EventNameProperty); }
        set { SetValue(EventNameProperty, value); }
    }

    public ICommand Command
    {
        get { return (ICommand)GetValue(CommandProperty); }
        set { SetValue(CommandProperty, value); }
    }

    public object CommandParameter
    {
        get { return GetValue(CommandParameterProperty); }
        set { SetValue(CommandParameterProperty, value); }
    }

    public IValueConverter Converter
    {
        get { return (IValueConverter)GetValue(InputConverterProperty); }
        set { SetValue(InputConverterProperty, value); }
    }

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

    protected override void OnDetachingFrom(View bindable)
    {
        DeregisterEvent(EventName);
        base.OnDetachingFrom(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))
        {
            return;
        }

        if (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);
        }
    }

    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);
    }
}

此外,为了使上述代码正常工作,你需要使用下面的 BehaviorBase 类:

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;
    }
}

使用示例:

<ListView.Behaviors>
    <local:EventToCommandBehavior EventName="ItemSelected" Command="{Binding SelectedClient}" />
</ListView.Behaviors>

祝你好运,如果有问题,请随时回来咨询。

英文:

ItemSelected is an event, not a bindable property what you are looking for is a behaviour that converts your Event to a Command

If you check the Microsoft documents you can find one here: https://learn.microsoft.com/en-us/xamarin/xamarin-forms/app-fundamentals/behaviors/reusable/event-to-command-behavior

Behavior:

  public class EventToCommandBehavior : BehaviorBase&lt;View&gt;
{
Delegate eventHandler;
public static readonly BindableProperty EventNameProperty = BindableProperty.Create (&quot;EventName&quot;, typeof(string), typeof(EventToCommandBehavior), null, propertyChanged: OnEventNameChanged);
public static readonly BindableProperty CommandProperty = BindableProperty.Create (&quot;Command&quot;, typeof(ICommand), typeof(EventToCommandBehavior), null);
public static readonly BindableProperty CommandParameterProperty = BindableProperty.Create (&quot;CommandParameter&quot;, typeof(object), typeof(EventToCommandBehavior), null);
public static readonly BindableProperty InputConverterProperty = BindableProperty.Create (&quot;Converter&quot;, typeof(IValueConverter), typeof(EventToCommandBehavior), null);
public string EventName {
get { return (string)GetValue (EventNameProperty); }
set { SetValue (EventNameProperty, value); }
}
public ICommand Command {
get { return (ICommand)GetValue (CommandProperty); }
set { SetValue (CommandProperty, value); }
}
public object CommandParameter {
get { return GetValue (CommandParameterProperty); }
set { SetValue (CommandParameterProperty, value); }
}
public IValueConverter Converter {
get { return (IValueConverter)GetValue (InputConverterProperty); }
set { SetValue (InputConverterProperty, value); }
}
protected override void OnAttachedTo (View bindable)
{
base.OnAttachedTo (bindable);
RegisterEvent (EventName);
}
protected override void OnDetachingFrom (View bindable)
{
DeregisterEvent (EventName);
base.OnDetachingFrom (bindable);
}
void RegisterEvent (string name)
{
if (string.IsNullOrWhiteSpace (name)) {
return;
}
EventInfo eventInfo = AssociatedObject.GetType ().GetRuntimeEvent (name);
if (eventInfo == null) {
throw new ArgumentException (string.Format (&quot;EventToCommandBehavior: Can&#39;t register the &#39;{0}&#39; event.&quot;, EventName));
}
MethodInfo methodInfo = typeof(EventToCommandBehavior).GetTypeInfo ().GetDeclaredMethod (&quot;OnEvent&quot;);
eventHandler = methodInfo.CreateDelegate (eventInfo.EventHandlerType, this);
eventInfo.AddEventHandler (AssociatedObject, eventHandler);
}
void DeregisterEvent (string name)
{
if (string.IsNullOrWhiteSpace (name)) {
return;
}
if (eventHandler == null) {
return;
}
EventInfo eventInfo = AssociatedObject.GetType ().GetRuntimeEvent (name);
if (eventInfo == null) {
throw new ArgumentException (string.Format (&quot;EventToCommandBehavior: Can&#39;t de-register the &#39;{0}&#39; event.&quot;, 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);
}
}
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);
}
}

Also for this to work you will need the BehaviorBase class which can be found below:

public class BehaviorBase&lt;T&gt; : Behavior&lt;T&gt; 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;
}
}

Usage:

   &lt;ListView.Behaviors&gt;
&lt;local:EventToCommandBehavior EventName=&quot;ItemSelected&quot; Command=&quot;{Binding SelectedClient}&quot; /&gt;
&lt;/ListView.Behaviors&gt;

Goodluck feel free to get back if you have questions

答案2

得分: 2

如果您正在尝试在ViewModel中保留ListView的选定项,请像下面这样使用ListView的SelectedItem属性:

<ListView x:Name="ClientListView"
SelectedItem="{Binding SelectedClient}">
英文:

If you are trying to keep the selected item of ListView in your ViewModel, then use the SelectedItem property in the ListView like below,

 &lt;ListView x:Name=&quot;ClientListView&quot;
SelectedItem=&quot;{Binding SelectedClient}&quot;&gt;

huangapple
  • 本文由 发表于 2020年1月6日 17:47:04
  • 转载请务必保留本文链接:https://go.coder-hub.com/59609838.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定