Xamarin MVVM, привязка элемента ListView в качестве командного параметра

#c# #xamarin #mvvm #data-binding

#c# #xamarin #mvvm #привязка данных

Вопрос:

У меня есть ListView, заполненный из viewmodel, и теперь я могу легко привязать элементы datatemplate к свойствам элементов источника данных и отправить эти свойства команде в модели представления. Однако я полагаю, что нужно иметь возможность отправлять весь исходный элемент. Однако, когда я отправляю что-либо, кроме строки, либо приложение выходит из строя, либо все кнопки отключены и, похоже, не запускают команду. Этот ООП и xamarin newb были бы очень признательны за любые указания / помощь. Я подозреваю, что это просто синтаксис в кнопке или в команде, который я не понимаю, что у меня есть на данный момент:

 <ContentPage.BindingContext>
    <local:TestViewModel/>
</ContentPage.BindingContext>
<ContentPage.Content>
    <StackLayout  >
        <Label Text="{Binding SelectedItem.Term_id}"
            VerticalOptions="CenterAndExpand" 
            HorizontalOptions="CenterAndExpand" />
        <BoxView HeightRequest="1" BackgroundColor="Black" HorizontalOptions="FillAndExpand" />
        <ListView x:Name="propListView"
            SelectedItem="{Binding SelectedItem}"
            ItemsSource="{Binding SimpleProperties}"  >
            <ListView.ItemTemplate>
                <DataTemplate>
                    <ViewCell x:Name="theItem" >
                        <StackLayout Orientation="Horizontal">
                            <Button Text="{Binding Agt_name}"
                              Command="{Binding BindingContext.doCheckin, Source={x:Reference Page}}"
                              CommandParameter="{Binding BindingContext.doCheckin, Source={x:Reference theItem}, Mode=OneWayToSource}"
                                    />
                            <Label Text="{Binding MilesDistance, StringFormat='~{0:f2} mi.'},
                        Converter={ConvertToDouble}}" HorizontalOptions="CenterAndExpand" />
                        </StackLayout>
                    </ViewCell>
                </DataTemplate>
            </ListView.ItemTemplate>
        </ListView>
    </StackLayout>
</ContentPage.Content>
  

 using AgentApp.Models;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Windows.Input;
using Xamarin.Forms;

namespace AgentApp
{
    class TestViewModel : BaseViewModel
    {
        private ObservableCollection<Property> simpleProperties;
        public ObservableCollection<Property> SimpleProperties { set { SetProperty(ref simpleProperties, value); } get { return simpleProperties; } }

        private Property selectedItem;
        public Property SelectedItem { get { return selectedItem; } set { SetProperty(ref selectedItem, value); } }

        public ICommand setSelection { private set; get; }
        public ICommand doCheckin { private set; get; }
        public TestViewModel()
        {
            var SimplePropertiesList = new List<Property>();
            for (int i = 1; i <= 5; i  )
            {
                Property prop = new Property();
                prop.Term_id = i.ToString();
                prop.Agt_name = "Prop "   i.ToString();
                prop.MilesDistance = (double)i * 2.5;
                SimplePropertiesList.Add(prop);
            }
            SimpleProperties = new ObservableCollection<Property>(SimplePropertiesList);

            /* for buttons:*/
            doCheckin = new Command<Property>(
                execute: (Property item) =>
                {
                    Console.WriteLine("doCheckin() executes: "   item.Term_id);
                    SelectedItem = item;
                },
            canExecute: (Property item) =>
            {
                Console.WriteLine("doCheckin() canExecute return"   item.Term_id);
                return (SelectedItem != item); 
            }
                );
        }
    }
}
  

и, конечно

 class BaseViewModel : INotifyPropertyChanged
    {
        public bool SetProperty<T>(ref T storage, T value, [CallerMemberName] string propertyName = null)
        {
            if (Object.Equals(storage, value)) return false;
            storage = value;
            OnPropertyChanged(propertyName);
            return true;
        }
        public virtual void OnPropertyChanged(string propertyName)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
        public event PropertyChangedEventHandler PropertyChanged;
    }
  

Заранее спасибо!

Комментарии:

1. "{Binding .}" для ссылки на текущий элемент

Ответ №1:

Однако я полагаю, что нужно иметь возможность отправлять весь исходный элемент. Однако, когда я отправляю что-либо, кроме строки, либо приложение выходит из строя, либо все кнопки отключены и, похоже, не запускают команду.

Согласно вашему описанию, вы хотите передать текущий элемент при нажатии кнопки, я прав? если да, как сказал Джейсон, измените параметр CommandParameter кнопки следующим образом:

   <ListView
            x:Name="propListView"
            ItemsSource="{Binding SimpleProperties}"
            SelectedItem="{Binding selectedItem}">
            <ListView.ItemTemplate>
                <DataTemplate>
                    <ViewCell>
                        <StackLayout Orientation="Horizontal">
                            <Button
                                Command="{Binding BindingContext.doCheckin, Source={x:Reference page27}}"
                                CommandParameter="{Binding .}"
                                Text="{Binding Agt_name}" />
                            <Label HorizontalOptions="CenterAndExpand" Text="{Binding MilesDistance}" />
                        </StackLayout>
                    </ViewCell>
                </DataTemplate>
            </ListView.ItemTemplate>
        </ListView>

 doCheckin = new Command((e) =>
        {
            var item = (e as Property);
            Console.WriteLine("the property Term_id is {0} ", item.Term_id);
        });
  

Кстати, в selecteditem необходимо реализовать INotifyPropertyChanged для обновления данных.

   private Property _selectedItem;
    public Property selectedItem 
    { 
        get { 
            return _selectedItem; 
        } 
        set { 
            _selectedItem=value;
            OnPropertyChanged("selectedItem");
        } 
    }      
  

Комментарии:

1. Спасибо @Cherry-bu-msft, я пришел правильно. Я реализую OnPropertyChanged в классе BaseViewModel, который расширяет эта ViewModel. Чего мне также не хватало, так это вызова (doCheckin as Command).ChangeCanExecute(); , при котором изменяются базовые переменные.