Задать вопрос

В ListView не отображаются данные из ObservableCollection и XAML отказывается делать Binding. Как исправить?

Добрый день! Учусь и пишу курсовой проект постепенно.
Начал постигать Xamarin.Forms с использованием паттерна MVVM и SQLite. И вот столкнулся с проблемой, на которую нигде ответ не могу найти. На главную страницу в ListView должны выводиться данные, полученные из БД, загруженные в ObservableCollection. И вроде бы данные в коллекцию приходят, и есть Binding с ItemsSource, но на экране либо пустые строки, либо ошибки. В итоге застопорился с этим уже на пару дней.

ViewModel для Model
namespace Pillbox.ViewModels
{
    public class MedicineViewModel:BaseViewModel
    {  
        public MedicineViewModel() { }
        public MedicineViewModel(Medicine medicine)
        {
            Id = medicine.Id;
            Title = medicine.Title;
            Format = medicine.Format;
            Method = medicine.Method;
            StartMedicationTime = medicine.StartMedicationTime;
            FinishMedicationTime = medicine.FinishMedicationTime;
            Dosage = medicine.Dosage;
            Number = medicine.Number;
            Start = medicine.Start;
            DurationDays = medicine.DurationDays;
            Finish = medicine.Finish;
            EveryDay = medicine.EveryDay;
            InDays = medicine.InDays;
            NonStop = medicine.NonStop;
        }
        public int Id { get; set; }
        private string _title; 
        public string Title
        {
            get => _title;
            set => Set(ref _title, value);
        }
        private string _format;
        public string Format
        {
            get => _format;
            set => Set(ref _format, value);
        }
        private string _method;
        public string Method
        {
            get => _method;
            set => Set(ref _method, value);
        }
        private TimeSpan _startMedicationTime;
        public TimeSpan StartMedicationTime
        {
            get => _startMedicationTime;
            set => Set(ref _startMedicationTime, value);
        }
        private TimeSpan _finishMedicationTime;
        public TimeSpan FinishMedicationTime
        {
            get => _finishMedicationTime;
            set => Set(ref _finishMedicationTime, value);
        }
        private float _dosage;
        public float Dosage
        {
            get => _dosage;
            set => Set(ref _dosage, value);
        }
        private int _number;
        public int Number 
        { 
            get=>_number; 
            set=>Set(ref _number, value); 
        }
        private DateTime _start;
        public DateTime Start { get=>_start; set=>Set(ref _start, value); }
        private int _durationDays;
        public int DurationDays { get=> _durationDays; set=>Set(ref _durationDays, value); }
        private DateTime _finish;
        public DateTime Finish { get=> _finish; set=>Set(ref _finish, value); }
        private bool _everyDay;
        public bool EveryDay { get=> _everyDay; set=>Set(ref _everyDay, value); }
        private int _inDays;
        public int InDays { get=> _inDays; set=>Set(ref _inDays, value); }
        private bool _nonStop;
        public bool NonStop { get=> _nonStop; set=>Set(ref _nonStop, value); }        
    }
}

ViewModel для главной страницы
namespace Pillbox.ViewModels
{
    public class MedPageViewModel:BaseViewModel
    {
        public ICommand AddMedicineCommand { get; protected set; }
        public ICommand DeleteMedicineCommand { get; protected set; }
        public ICommand SelectMedicineCommand { get; protected set; }
        public ICommand LoadMedicinesCommand { get; protected set; }

        private bool _isDataLoaded;

        private IMedicineDatabase _medicineDB;

        private IPageSevices _pageService;

        private MedicineViewModel _selectedMedicine;
        public MedicineViewModel SelectedMedicine
        {
            get => _selectedMedicine;
            set
            {
                Set(ref _selectedMedicine, value);
            }
        }

        public ObservableCollection<MedicineViewModel> Medicines { get; set; }
            = new ObservableCollection<MedicineViewModel>();
        
        public MedPageViewModel(IPageSevices pageSevices, IMedicineDatabase medicineDatabase)
        {
            _pageService = pageSevices;
            _medicineDB = medicineDatabase;
                                 
            LoadMedicinesCommand = new Command(async () => await Load());
            AddMedicineCommand = new Command(async () => await AddMedicine());
            DeleteMedicineCommand = new Command<MedicineViewModel>(async c => await DeleteMedicine(c));
            SelectMedicineCommand = new Command<MedicineViewModel>(async c => await SelectMedicine(c));

            MessagingCenter.Subscribe<AdditionViewModel, Medicine>
                (this, Events.MedicineAdded, OnMedicineAdded);
            MessagingCenter.Subscribe<AdditionViewModel, Medicine>
                (this, Events.MedicineUpdate, OnMedicineUpdated);            
        }

        private async Task SelectMedicine(MedicineViewModel medicine)
        {
            if (medicine == null) 
                return;
            SelectedMedicine = null;
            await _pageService.PushAsync(new AdditionView(medicine));
        }

        private async Task Load()
        {
            Medicines.Clear();
            try
            {
                if (_isDataLoaded)
                    return;
                _isDataLoaded = true;
                var medicines = await _medicineDB.UpdateMedicineList();
                foreach (var medicine in medicines)
                    Medicines.Add(new MedicineViewModel(medicine));
            }
            catch (Exception)
            { throw; }

        }

        async Task AddMedicine()
        {
            await _pageService.PushAsync(new AdditionView(new MedicineViewModel()));
        }

        async Task DeleteMedicine(MedicineViewModel deleteMedicine)
        {
            if (await _pageService.DisplayAlert("Внимание", $"Вы действительно хотите удалить {deleteMedicine.Title}?", "Да", "Нет"))
            {
                Medicines.Remove(deleteMedicine);
                var medicine = await _medicineDB.GetMedicine(deleteMedicine.Id); 
                await _medicineDB.DeleteMedicine(medicine);
            }
        }           
    }
}


View главной страницы
namespace Pillbox.Views.MainViews
{
    [XamlCompilation(XamlCompilationOptions.Compile)]
    public partial class MedPage : ContentPage
    {     
        public MedPageViewModel ViewModelMP
        {
            get => BindingContext as MedPageViewModel;
            set => BindingContext = value;
        }
        public MedPage()
        {        
            var medicineDB = new MedicineDatabase(DependencyService.Get<ISQLiteDb>());
            var pageService = new PageService();
            ViewModelMP = new MedPageViewModel(pageService, medicineDB);
            InitializeComponent();                      
        }
        protected override void OnAppearing()
        {
           
            ViewModelMP.LoadMedicinesCommand.Execute(null);
            base.OnAppearing();            
        }

        void OnMedicineSelected(object sender, SelectedItemChangedEventArgs e)
        {
            ViewModelMP.SelectMedicineCommand.Execute(e.SelectedItem);
        }          
    }
}

И XAML
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:vm="clr-namespace:Pillbox.ViewModels"
             x:DataType="vm:MedPageViewModel"
             x:Class="Pillbox.Views.MainViews.MedPage"
             Title="Мои лекарства" x:Name="medPage">
        <Grid>
            <Grid.RowDefinitions>
                <RowDefinition Height="*"/>
                <RowDefinition Height="*"/>
            </Grid.RowDefinitions>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="*"/>
                <ColumnDefinition Width="*"/>
            </Grid.ColumnDefinitions>
            <ScrollView Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="2">
                    <ListView x:Name="listView" ItemsSource="{Binding Medicines}" SelectedItem="{Binding SelectedMedicine, Mode=TwoWay}" 
                          HasUnevenRows="True" SeparatorColor="#005400" ItemSelected="OnMedicineSelected" IsVisible="True" IsTabStop="False" IsEnabled="True" IsRefreshing="False">
                        <ListView.ItemTemplate>
                            <DataTemplate>
                        <ViewCell>
                            <Grid IsVisible="True" IsEnabled="True">
                                <Grid.RowDefinitions>
                                    <RowDefinition Height="auto"/>
                                </Grid.RowDefinitions>
                                <Label Text="{Binding Source=Medicines, Path=Title}" FontSize="Large"/>
                                <Label Text="{Binding Source=Medicines, Path=Format}"/>
                                <Label Text="{Binding Source=Medicines, Path=Method}"/>


                            </Grid>                            
                        </ViewCell>
                    </DataTemplate>
                        </ListView.ItemTemplate>

                    </ListView>
            </ScrollView>
            <Button Grid.Row="1" Grid.Column="1" Text="+ Добавить" FontSize="Medium" FontAttributes="None" TextColor="White"
                        BackgroundColor="#13bd13" BorderRadius="75" HorizontalOptions="End" VerticalOptions="End" 
                Command="{Binding AddMedicineCommand}" Margin="0,0,30,30"/>
        </Grid>

</ContentPage>
  • Вопрос задан
  • 608 просмотров
Подписаться 2 Простой 7 комментариев
Решения вопроса 1
FoggyFinder
@FoggyFinder
Вы используете компилируемые привязки, а значит нужно указывать тип в явном виде и для дочерних элементов. По крайней мере до тех пор пока это не будет исправлено в XF

Adding x:DataType to a ContentPage breaks nested o...

Так что чтобы исправить проблему достаточно просто добавить

x:DataType="vm:MedicineViewModel"

чтобы было понятнее привожу контекст

<ListView.ItemTemplate>
    <DataTemplate x:DataType="vm:MedicineViewModel">
        <ViewCell>
            <ViewCell.View>
                <StackLayout>
                    <Label FontSize="Large" Text="{Binding Title}" />
                    <Label FontSize="Small" Text="{Binding Format}" />
                    <Label FontSize="Small" Text="{Binding Method}" />
                </StackLayout>
            </ViewCell.View>
        </ViewCell>
    </DataTemplate>
</ListView.ItemTemplate>


И позвольте дать пару советов которые не относятся непосредственно к ответу

1. В процессе работы над проектов время от времени запрашивайте CodeReview, так код будет чище и вы сможете "расти" быстрее.

2. Не отправляйте в гит секретные ключи. Пока проект учебный это не так и страшно, но лучше проявлять осмотрительность как можно раньше.

3. Когда сталкиваетесь с проблемой (а такое всенепременно будет) не тащите в вопрос код из своего проекта, лучше создайте маленький тестовый проект где не будет ничего лишнего. На первый взгляд это лишняя трата времени, но во многих случаях вы докопаетесь до сути проблемы во время подготовки такого MCVE.
Ответ написан
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы