Home > Blockchain >  RefreshView won't stop refreshing - Xamarin Forms
RefreshView won't stop refreshing - Xamarin Forms

Time:04-28

For my Xamarin Forms application I am working on developing a calendar page. I have a No-SQL database set up and working and I have my main calendar view page (EventManagerPage) and the backend of this page (EventManagerViewPage).

The issue I am facing is how to let the system know that the RefreshView is refreshing?

The normal Xamarin.Forms.IsBusy indicator has not been working and my page will continue to refresh without stopping. I can tell the calendar events are there as they appear below the calendar after I pull down the page to refresh and click on a date, but the calendar itself does not show the events present (blue square per event on date).

My ViewModel is based off of a custom calendar ViewModel that extends both the INotifyPropertyChanged interface and the BaseViewModel.

Using IsBusy does not result in any error being thrown nor any messages in the debug output. I've tried some other ways around this to try and get the reload to stop once done but those have all resulted in errors that prevent the app from compiling.

So far I have tried creating a custom Boolean to act as the IsBusy Xamarin.Forms indicator but this resulted in the error of the member not being found in the data context.

I additionally attempted to follow the example in this Microsoft Doc on RefreshViews. This also resulted in the error of the member not being found in the data context and I was unable to set refreshView.Command = refreshCommand; (I'm not sure if this is important for the error).

I have put my code below. As a note, the calendar I am using is the Plugin.XCalendar by author MarvinE.
I appreciate any help/suggestions anyone has to offer!

CalendarBaseViewModel.cs

using MvvmHelpers;
using PropertyChanged;
using System.ComponentModel;
using System.Runtime.CompilerServices;

namespace PPA.ViewModels
{
    [AddINotifyPropertyChangedInterface]
    public abstract class CalendarBaseViewModel : BaseViewModel, INotifyPropertyChanged
    {
        #region Events
        public event PropertyChangedEventHandler PropertyChanged;
        #endregion

        #region Methods
        protected virtual void OnPropertyChanged([CallerMemberName] string PropertyName = null)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(PropertyName));
        }
        #endregion
    }
}

EventManagerViewModel.cs

using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using System.Text;
using System.Windows.Input;
using Xamarin.CommunityToolkit.ObjectModel;
using Xamarin.Forms;
using PPA.Models;
using System.Threading.Tasks;
using PPA.Views;
using PPA.Services;
using System.Diagnostics;

namespace PPA.ViewModels
{
    public class EventManagerViewModel : CalendarBaseViewModel
    {
        #region Properties
        public ObservableRangeCollection<Event> Events { get; }
        public ObservableRangeCollection<DateTime> SelectedDates { get; }
        public ObservableRangeCollection<Event> SelectedEvents { get; }

        #endregion

        public AsyncCommand AddEventCommand { get; }
        public AsyncCommand LoadEventsCommand { get; }

        IEventDataStore EventService;

        #region Constructors
        public EventManagerViewModel()
        {
            AddEventCommand = new AsyncCommand(OnAddEvent);
            LoadEventsCommand = new AsyncCommand(LoadEvents);
            EventService = DependencyService.Get<IEventDataStore>();

            Events = new ObservableRangeCollection<Event>();
            SelectedDates = new ObservableRangeCollection<DateTime>(); 
            SelectedEvents = new ObservableRangeCollection<Event>();


            SelectedDates.CollectionChanged  = SelectedDates_CollectionChanged;

        }
        #endregion

        #region Methods
        private void SelectedDates_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            SelectedEvents.ReplaceRange(Events.Where(x => SelectedDates.Any(y => x.DateTime.Date == y.Date)).OrderByDescending(x => x.DateTime));
        }

        private async Task OnAddEvent()
        {
            await Shell.Current.GoToAsync(nameof(NewEventPage));
        }

        async Task LoadEvents()
        {
            IsBusy = true;
            // refreshview.IsRefreshing = true;
            try
            {
                Events.Clear();
                var events = await EventService.GetEventsAsync();
                foreach (var ev in events)
                {
                    Events.Add(ev);
                }
            }
            catch (Exception ex)
            {
                Debug.WriteLine(ex);
            }
           IsBusy = false;
        } 
        


        public void OnAppearing()
        {
         IsBusy = true;
        }
        #endregion
    }
}

EventManagerPage.xaml

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage x:Class="PPA.Views.EventManagerPage"
    xmlns="http://xamarin.com/schemas/2014/forms"
    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
    xmlns:Converters="clr-namespace:PPA.Converters"
    xmlns:Models="clr-namespace:PPA.Models"
    xmlns:ViewModels="clr-namespace:PPA.ViewModels"
    xmlns:xc="clr-namespace:XCalendar;assembly=XCalendar"
    xmlns:xcModels="clr-namespace:XCalendar.Models;assembly=XCalendar"
    xmlns:xct="http://xamarin.com/schemas/2020/toolkit" xmlns:xcConverters="clr-namespace:XCalendar.Converters;assembly=XCalendar" 
             x:DataType="ViewModels:EventManagerViewModel"
             x:Name="This"
    Title="Event Calendar"
    xct:SafeAreaEffect.SafeArea="True"
             >
    <ContentPage.Resources>
        <!--  Limits a string to a certain amount of characters  -->
        <xcConverters:StringCharLimitConverter x:Key="StringCharLimitConverter"/>
        <!--  Returns true if all bindings evaluate to true  -->
        <xct:VariableMultiValueConverter x:Key="AllTrueConverter" ConditionType="All"/>
        <!--  Inverts a binded boolean value  -->
        <xct:InvertedBoolConverter x:Key="InvertedBoolConverter"/>
    </ContentPage.Resources>
    <ContentPage.ToolbarItems>
        <ToolbarItem Text="Add" Command="{Binding AddEventCommand}" />
    </ContentPage.ToolbarItems>

    <RefreshView x:DataType="ViewModels:EventManagerViewModel" Command="{Binding LoadEventsCommand}" IsRefreshing="{Binding IsBusy, Mode=TwoWay}">
    <Grid
        ColumnSpacing="0"
        RowDefinitions="Auto,*"
        RowSpacing="0">

        <Frame
            Margin="10"
            Padding="0"
            BackgroundColor="White"
            CornerRadius="15">

            <xc:CalendarView
                x:Name="MainCalendarView"
                Grid.Row="0"
                DayNameTextColor="{StaticResource ContentTextColor}"
                NavigationArrowColor="{StaticResource ContentTextColor}"
                NavigationBackgroundColor="Transparent"
                NavigationTextColor="{StaticResource ContentTextColor}"
                SelectedDates="{Binding SelectedDates}"
                SelectionAction="Modify"
                SelectionType="Single">

                <xc:CalendarView.DayTemplate>
                    <DataTemplate x:DataType="{x:Type xcModels:CalendarDay}">
                        <!--  ContentView so that the margin is respected by the MonthView  -->
                        <ContentView>
                            <xc:CalendarDayView
                                Margin="2.5"
                                HeightRequest="43"
                                CalendarView="{Binding ., Source={x:Reference MainCalendarView}}"
                                CurrentMonthTextColor="{StaticResource CalendarBackgroundTextColor}"
                                DateTime="{Binding DateTime}"
                                OutOfRangeTextColor="{StaticResource CalendarTertiaryColor}"
                                SelectedTextColor="{StaticResource CalendarPrimaryTextColor}"
                                TodayBorderColor="{StaticResource CalendarPrimaryColor}"
                                TodayTextColor="{StaticResource CalendarBackgroundTextColor}">

                                <xc:CalendarDayView.ControlTemplate>
                                    <ControlTemplate>
                                        <!--  Using a Grid to stack views on the z axis  -->
                                        <Grid RowSpacing="2">

                                            <Grid.RowDefinitions>
                                                <RowDefinition Height="1.5*"/>
                                                <RowDefinition/>
                                            </Grid.RowDefinitions>

                                            <!--  ContentPresenter displays the default content for the control  -->
                                            <ContentPresenter
                                                Grid.Row="0"
                                                Grid.RowSpan="2"
                                                VerticalOptions="Center"/>

                                            <StackLayout
                                                Grid.Row="1"
                                                HorizontalOptions="Center"
                                                Orientation="Horizontal"
                                                Spacing="2.5">

                                                <!--  I want the event indicators to only be visible when the DateTime is in the currently navigated month  -->
                                                <StackLayout.IsVisible>
                                                    <MultiBinding Converter="{StaticResource AllTrueConverter}">
                                                        <!--  TemplatedParent refers to the view that the ControlTemplate resides in  -->
                                                        <Binding Path="IsCurrentMonth" Source="{RelativeSource TemplatedParent}"/>
                                                        <Binding
                                                            Converter="{StaticResource InvertedBoolConverter}"
                                                            Path="IsOutOfRange"
                                                            Source="{RelativeSource TemplatedParent}"/>
                                                    </MultiBinding>
                                                </StackLayout.IsVisible>

                                                <BindableLayout.ItemsSource>
                                                    <Binding Path="DateTime.Date" Source="{RelativeSource TemplatedParent}">
                                                        <Binding.Converter>
                                                            <Converters:EventWhereConverter
                                                                Items="{Binding BindingContext.Events, Source={x:Reference This}}"
                                                                UseTimeComponent="False"
                                                                WhiteList="True"/>
                                                        </Binding.Converter>
                                                    </Binding>
                                                </BindableLayout.ItemsSource>

                                                <BindableLayout.ItemTemplate>
                                                    <DataTemplate x:DataType="{x:Type Models:Event}">
                                                        <BoxView
                                                            CornerRadius="100"
                                                            HeightRequest="7"
                                                            HorizontalOptions="CenterAndExpand"
                                                            VerticalOptions="Center"
                                                            WidthRequest="7"
                                                            Color="Blue"/>
                                                    </DataTemplate>
                                                </BindableLayout.ItemTemplate>

                                            </StackLayout>

                                        </Grid>
                                    </ControlTemplate>
                                </xc:CalendarDayView.ControlTemplate>

                            </xc:CalendarDayView>
                        </ContentView>
                    </DataTemplate>
                </xc:CalendarView.DayTemplate>

            </xc:CalendarView>

        </Frame>

        <CollectionView Grid.Row="1" ItemsSource="{Binding SelectedEvents}">

            <CollectionView.EmptyView>
                <Label
                    FontAttributes="Bold"
                    FontSize="20"
                    HorizontalTextAlignment="Center"
                    Text="No Events on Selected Date(s)"
                    TextColor="{StaticResource ContentTextColor}"
                    VerticalTextAlignment="Center"/>
            </CollectionView.EmptyView>
            <CollectionView.ItemsLayout>
                <LinearItemsLayout ItemSpacing="0" Orientation="Vertical"/>
            </CollectionView.ItemsLayout>

            <CollectionView.ItemTemplate>
                <DataTemplate x:DataType="{x:Type Models:Event}">
                    <ContentView Padding="5">
                        <Frame
                            Padding="0"
                            BackgroundColor="{StaticResource ContentBackgroundColor}"
                            CornerRadius="10">
                            <StackLayout Orientation="Horizontal" Spacing="0">
                                <BoxView BackgroundColor="CornflowerBlue" WidthRequest="20"/>

                                <StackLayout Padding="10" Spacing="0">
                                    <Label
                                        FontAttributes="Bold"
                                        FontSize="20"
                                        Text="{Binding DateTime, StringFormat='{0: dd MMMM HH:mm}'}"
                                        TextColor="{StaticResource ContentTextColor}"
                                        VerticalTextAlignment="Center"/>
                                    <Label
                                        FontSize="16"
                                        Text="{Binding Title}"
                                        TextColor="{StaticResource ContentTextColor}"
                                        Margin="5,0,0,0"/>
                                    <Label
                                        Margin="5,10,0,0"
                                        FontSize="14"
                                        Text="{Binding Description}"
                                        TextColor="{StaticResource ContentTextColor}"/>
                                </StackLayout>
                            </StackLayout>
                        </Frame>
                    </ContentView>
                </DataTemplate>
            </CollectionView.ItemTemplate>
        </CollectionView>

    </Grid>
     </RefreshView>
</ContentPage>

EventManagerPage.xaml.cs

using PPA.ViewModels;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;

namespace PPA.Views
{
    [XamlCompilation(XamlCompilationOptions.Compile)]

    public partial class EventManagerPage : ContentPage
    {
        EventManagerViewModel _viewModel;
        public EventManagerPage()
        {

            InitializeComponent();
            BindingContext = _viewModel = new EventManagerViewModel();
            
        }

        protected override void OnAppearing()
        {
            base.OnAppearing();
            _viewModel.OnAppearing();
        }
    }
}

CodePudding user response:

Have you created binding property for "IsBusy" in the ViewModel as below:

    private bool _isBusy;
    public bool IsBusy
    {
        get
        {
            return _isBusy;
        }
        set
        {
            _isBusy = value;
            OnPropertyChanged("IsBusy");
        }
    }
  • Related