英文:
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<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>
答案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<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);
}
}
Also for this to work you will need the BehaviorBase class which can be found below:
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;
}
}
Usage:
<ListView.Behaviors>
<local:EventToCommandBehavior EventName="ItemSelected" Command="{Binding SelectedClient}" />
</ListView.Behaviors>
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,
<ListView x:Name="ClientListView"
SelectedItem="{Binding SelectedClient}">
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论