NuGet Package: https://www.nuget.org/packages/CustomCalendarControl/1.0.5
Github URL: https://github.com/mistrypragnesh40/CustomCalendarControlPlugin Required.
Refractored.MvvmHelpers
Rg.Plugins.Popup :
Install Rg.Plugin.Popup Plugin in android, ios & Xamarin Form Project.
Rg.Plugins.Popup :
Install Rg.Plugin.Popup Plugin in android, ios & Xamarin Form Project.
Initialize it in MainActivity.cs & AppDelegate.cs
Rg.Plugins.Popup.Popup.Init(this); (Android)
Rg.Plugins.Popup.Popup.Init(); (ios)
Views
CalendarPage.xamlViewModels
CalendarPageViewModel.cs
CalendarModel.cs
Views
CalendarPage.xaml
<?xml version="1.0" encoding="utf-8" ?> <pages:PopupPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:system="clr-namespace:System;assembly=netstandard" xmlns:pages="clr-namespace:Rg.Plugins.Popup.Pages;assembly=Rg.Plugins.Popup" xmlns:animations="clr-namespace:Rg.Plugins.Popup.Animations;assembly=Rg.Plugins.Popup" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Name="this" x:Class="CustomCalendarControl.Views.CalendarPage"> <pages:PopupPage.Animation> <animations:ScaleAnimation PositionIn="Bottom" PositionOut="Bottom" ScaleIn="1.2" ScaleOut="0.8" DurationIn="200" DurationOut="200" EasingIn="SinOut" EasingOut="SinIn" HasBackgroundAnimation="True"/> </pages:PopupPage.Animation> <ContentPage.Content> <AbsoluteLayout VerticalOptions="CenterAndExpand" HorizontalOptions="CenterAndExpand" AbsoluteLayout.LayoutBounds="1,1,1,1" AbsoluteLayout.LayoutFlags="All" BackgroundColor="Transparent"> <!--<DatePicker />--> <Grid Padding="0,0,0,30" Margin="5,0,5,0" IsClippedToBounds="True" BackgroundColor="White" VerticalOptions="FillAndExpand" HorizontalOptions="FillAndExpand" > <Grid.RowDefinitions> <RowDefinition Height="auto" /> <RowDefinition Height="auto" /> <RowDefinition Height="auto" /> <RowDefinition Height="Auto" /> </Grid.RowDefinitions> <Label LineHeight="1.2" Grid.Row="0" BackgroundColor="Orange" Padding="15,15,5,15" > <Label.FormattedText> <FormattedString> <Span Text="{Binding CurrentYear}" TextColor="White" FontSize="18" > <Span.GestureRecognizers> <TapGestureRecognizer Command="{Binding ShowYearListCommand}"/> </Span.GestureRecognizers> </Span> <Span Text="{x:Static system:Environment.NewLine}" /> <Span Text="{Binding SelectedDateInString}" TextColor="White" /> </FormattedString> </Label.FormattedText> </Label> <ListView BackgroundColor="White" x:Name="lstYear" HeightRequest="340" WidthRequest="310" Grid.Row="1" ItemsSource="{Binding YearList}" VerticalOptions="FillAndExpand"> <ListView.ItemTemplate> <DataTemplate> <ViewCell> <StackLayout Margin="0,10,0,10"> <Label HorizontalTextAlignment="Center" Text="{Binding .}" /> <StackLayout.GestureRecognizers> <TapGestureRecognizer CommandParameter="{Binding .}" Command="{Binding Source={x:Reference this},Path=BindingContext.YearSelectionCommand}" /> </StackLayout.GestureRecognizers> </StackLayout> </ViewCell> </DataTemplate> </ListView.ItemTemplate> <ListView.Triggers> <DataTrigger TargetType="ListView" Binding="{Binding IsCalendarDetailsVisible}" Value="True"> <Setter Property="IsVisible" Value="False" /> </DataTrigger> <DataTrigger TargetType="ListView" Binding="{Binding IsCalendarDetailsVisible}" Value="False"> <Setter Property="IsVisible" Value="True" /> </DataTrigger> </ListView.Triggers> </ListView> <StackLayout Grid.Row="2" Padding="0" IsVisible="{Binding IsCalendarDetailsVisible}"> <Grid ColumnDefinitions="34,34,34,34,34,34,34" IsVisible="{Binding IsCalendarDetailsVisible}" Margin="30,0,0,0" HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand" > <Label FontSize="12" Text="S" Grid.Row="1" TextColor="Red" Grid.Column="0" /> <Label FontSize="12" Text="M" Grid.Row="1" TextColor="Gray" Grid.Column="1"/> <Label FontSize="12" Text="T" Grid.Row="1" TextColor="Gray" Grid.Column="2"/> <Label FontSize="12" Text="W" Grid.Row="1" TextColor="Gray" Grid.Column="3"/> <Label FontSize="12" Text="T" Grid.Row="1" TextColor="Gray" Grid.Column="4"/> <Label FontSize="12" Text="F" Grid.Row="1" TextColor="Gray" Grid.Column="5"/> <Label FontSize="12" Text="S" Grid.Row="1" TextColor="Gray" Grid.Column="6"/> <Label VerticalTextAlignment="Center" Grid.Row="0" Grid.ColumnSpan="2" TextColor="Black" Text="<" FontSize="25" HorizontalTextAlignment="Start" > <Label.GestureRecognizers> <TapGestureRecognizer Command="{Binding PreviousMonthCommand}" /> </Label.GestureRecognizers> </Label> <Label VerticalTextAlignment="Center" Grid.Row="0" Grid.Column="2" Grid.ColumnSpan="2" FontSize="13" FontAttributes="Bold" HorizontalTextAlignment="Start" HorizontalOptions="CenterAndExpand" Text="{Binding CurrentMonthYear}" TextColor="Black" /> <Label VerticalTextAlignment="Center" HorizontalTextAlignment="Center" Grid.Row="0" Grid.Column="5" Grid.ColumnSpan="2" TextColor="Black" Text=">" FontSize="25" > <Label.GestureRecognizers> <TapGestureRecognizer Command="{Binding NextMonthCommand}" /> </Label.GestureRecognizers> </Label> </Grid> <CollectionView IsVisible="{Binding IsCalendarDetailsVisible}" Margin="15,0,0,0" ItemsSource="{Binding CalendarList}" HeightRequest="260" WidthRequest="295" IsGrouped="True" HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand" > <CollectionView.GestureRecognizers> <SwipeGestureRecognizer Direction="Left" Command="{Binding NextMonthCommand}" /> <SwipeGestureRecognizer Direction="Right" Command="{Binding PreviousMonthCommand}" /> </CollectionView.GestureRecognizers> <CollectionView.ItemsLayout> <GridItemsLayout SnapPointsType="MandatorySingle" SnapPointsAlignment="Center" Span="7" Orientation="Horizontal" /> </CollectionView.ItemsLayout> <CollectionView.GroupHeaderTemplate> <DataTemplate> <Label Padding="0" /> </DataTemplate> </CollectionView.GroupHeaderTemplate> <CollectionView.ItemTemplate> <DataTemplate> <Frame BackgroundColor="Red" HasShadow="False" Padding="0" Margin="0" HeightRequest="40" WidthRequest="40"> <Label FontSize="12" TextColor="Black" Text="{Binding DateInNumberStr}" VerticalTextAlignment="Center" HorizontalTextAlignment="Center" > <Label.Triggers> <DataTrigger TargetType="Label" Binding="{Binding CurrentDate}" Value="True"> <Setter Property="TextColor" Value="White" /> </DataTrigger> </Label.Triggers> </Label> <Frame.Triggers> <DataTrigger TargetType="Frame" Binding="{Binding CurrentDate}" Value="True"> <Setter Property="BackgroundColor" Value="Orange" /> <Setter Property="CornerRadius" Value="19.5" /> </DataTrigger> <DataTrigger TargetType="Frame" Binding="{Binding CurrentDate}" Value="False"> <Setter Property="BackgroundColor" Value="Transparent" /> </DataTrigger> </Frame.Triggers> <Frame.GestureRecognizers> <TapGestureRecognizer Command="{Binding Source={x:Reference this}, Path=BindingContext.CurrentDateCommand}" CommandParameter="{Binding .}" /> </Frame.GestureRecognizers> </Frame> </DataTemplate> </CollectionView.ItemTemplate> </CollectionView> </StackLayout> <Label Margin="0,0,90,0" Grid.Row="3" FontAttributes="Bold" x:Name="lblCancelDate" Text="CANCEL" FontSize="17" TextColor="Orange" HorizontalOptions="End" > <Label.GestureRecognizers> <TapGestureRecognizer Tapped="TapGestureRecognizer_Tapped_ForCancelDate" CommandParameter="{Binding .}" /> </Label.GestureRecognizers> </Label> <Label Margin="0,0,30,0" Grid.Row="3" FontAttributes="Bold" TextColor="Orange" Text="OK" x:Name="lblSelectDate" FontSize="17" HorizontalOptions="End" > <Label.GestureRecognizers> <TapGestureRecognizer Tapped="TapGestureRecognizer_Tapped_ForSelectDate" CommandParameter="{Binding .}" /> </Label.GestureRecognizers> </Label> </Grid> </AbsoluteLayout> </ContentPage.Content> </pages:PopupPage>
CalendarPage.xaml.cs
using CustomCalendarControl.ViewModels; using Rg.Plugins.Popup.Services; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Input; using Xamarin.Forms; using Xamarin.Forms.Xaml; namespace CustomCalendarControl.Views { [XamlCompilation(XamlCompilationOptions.Compile)] public partial class CalendarPage : Rg.Plugins.Popup.Pages.PopupPage { public CalendarPage(DateTime date) { InitializeComponent(); this.BindingContext = new CalendarPageViewModel(date); }
private void TapGestureRecognizer_Tapped_ForCancelDate(object sender, EventArgs e) { Device.BeginInvokeOnMainThread(async () => { await PopupNavigation.Instance.PopAsync(); }); }
public ICommand SelectedDateCommand { get; set; } private void TapGestureRecognizer_Tapped_ForSelectDate(object sender, EventArgs e) { var btnObject = (Label)sender; var context = (CalendarPageViewModel)btnObject.BindingContext; SelectedDateCommand.Execute(context.SelectedDate); TapGestureRecognizer_Tapped_ForCancelDate(sender, null); } } }
CalendarPageViewModel.cs
Modelsusing CustomCalendarControl.Models; using MvvmHelpers; using System; using System.Collections.Generic; using System.ComponentModel; using System.Linq; using System.Runtime.CompilerServices; using System.Text; using System.Windows.Input; using Xamarin.Forms; namespace CustomCalendarControl.ViewModels { public class CalendarPageViewModel : INotifyPropertyChanged { #region Properties & Event public event PropertyChangedEventHandler PropertyChanged; int MaxYear; int MinYear; public static List<CalendarModel> _staticCalendarList; public static List<int> dummyYearList; public ObservableRangeCollection<int> YearList { get; } = new ObservableRangeCollection<int>(); public ObservableRangeCollection<Grouping<CalendarHeader, CalendarModel>> CalendarList { get; } = new ObservableRangeCollection<Grouping<CalendarHeader, CalendarModel>>(); private bool _isCalendarDetailsVisible = true; public bool IsCalendarDetailsVisible { get => _isCalendarDetailsVisible; set { _isCalendarDetailsVisible = value; OnPropertyChanged(); } } private string _currentMonthYear = string.Empty; public string CurrentMonthYear { get => _currentMonthYear; set { _currentMonthYear = value; OnPropertyChanged(); } } private int _currentYear; public int CurrentYear { get => _currentYear; set { _currentYear = value; OnPropertyChanged(); } } private DateTime _currentDate; public DateTime CurrentDate { get => _currentDate; set { _currentDate = value; OnPropertyChanged(); } } private DateTime? _selectedDate { get; set; } public DateTime? SelectedDate { get => _selectedDate; set { _selectedDate = value; OnPropertyChanged(); } } private string _selectedDateInString; public string SelectedDateInString { get => _selectedDateInString; set { _selectedDateInString = value; OnPropertyChanged(); } } #endregion #region Constructor public CalendarPageViewModel(DateTime _selectedDate) { AddDates(); BindDates(_selectedDate.Year, _selectedDate.Month, _selectedDate.ToString("MMM"), _selectedDate); } public CalendarPageViewModel() { } #endregion #region Methods private void AddDates() { MaxYear = DateTime.Now.Year + 30; MinYear = DateTime.Now.Year - 100; if (dummyYearList == null) dummyYearList = new List<int>(); if (_staticCalendarList == null) _staticCalendarList = new List<CalendarModel>(); if (_staticCalendarList.Count == 0) { for (int year = MinYear; year <= MaxYear; year++) { for (int month = 1; month <= 12; month++) { int daysCount = DateTime.DaysInMonth(year, month); var dates = new List<CalendarModel>(); for (int days = 1; days <= daysCount; days++) { DateTime date = new DateTime(year, month, days); CalendarModel obj = new CalendarModel(); obj.Date = date; obj.Year = year; obj.Month = month; obj.DateInNumber = days; obj.DateInNumberStr = days + ""; obj.DayName = date.ToString("ddd"); dates.Add(obj); } _staticCalendarList.AddRange(dates); } dummyYearList.Add(year); } } else { if (dummyYearList.Count == 0) { for (int year = MinYear; year <= MaxYear; year++) { dummyYearList.Add(year); } } var selectedDates = _staticCalendarList.Where(f => f.CurrentDate == true).ToList(); selectedDates.ForEach(f => { f.CurrentDate = false; }); } YearList.AddRange(dummyYearList.OrderByDescending(f => f).ToList()); } public void BindDates(int year, int month, string MonthName, DateTime? newDate) { CalendarList.Clear(); bool isFoundStartDate = false; int days = DateTime.DaysInMonth(year, month); CurrentMonthYear = MonthName + " " + year.ToString(); var dates = _staticCalendarList.Where(f => f.Year == year && f.Month == month).ToList(); if (dates.Count > 0) { if (newDate.HasValue) { var currentDate = dates.Where(f => f.DateInNumber == newDate?.Day).FirstOrDefault(); currentDate.CurrentDate = true; SelectedDate = newDate.Value; CurrentDate = newDate.Value; CurrentYear = year; SelectedDateInString = CurrentDate.ToString("ddd, MMMM dd"); } string startDayName = dates.Where(f => f.DateInNumber == 1).FirstOrDefault().DayName.ToLower(); var list = new List<CalendarModel>(); var header = new CalendarHeader(); header.DayName = "Sun"; if (startDayName != header.DayName.ToLower() && !isFoundStartDate) { list.Add(new CalendarModel()); } else { isFoundStartDate = true; } list.AddRange(dates.Where(f => f.DayName.ToLower() == header.DayName.ToLower()).ToList()); CalendarList.Add(new Grouping<CalendarHeader, CalendarModel>(header, list)); list.Clear(); header = new CalendarHeader(); header.DayName = "Mon"; if (startDayName != header.DayName.ToLower() && !isFoundStartDate) { list.Add(new CalendarModel()); } else { isFoundStartDate = true; } list.AddRange(dates.Where(f => f.DayName.ToLower() == header.DayName.ToLower()).ToList()); CalendarList.Add(new Grouping<CalendarHeader, CalendarModel>(header, list)); list.Clear(); header = new CalendarHeader(); header.DayName = "Tue"; if (startDayName != header.DayName.ToLower() && !isFoundStartDate) { list.Add(new CalendarModel()); } else { isFoundStartDate = true; } list.AddRange(dates.Where(f => f.DayName.ToLower() == header.DayName.ToLower()).ToList()); CalendarList.Add(new Grouping<CalendarHeader, CalendarModel>(header, list)); list.Clear(); header = new CalendarHeader(); header.DayName = "Wed"; if (startDayName != header.DayName.ToLower() && !isFoundStartDate) { list.Add(new CalendarModel()); } else { isFoundStartDate = true; } list.AddRange(dates.Where(f => f.DayName.ToLower() == header.DayName.ToLower()).ToList()); CalendarList.Add(new Grouping<CalendarHeader, CalendarModel>(header, list)); list.Clear(); header = new CalendarHeader(); header.DayName = "Thu"; if (startDayName != header.DayName.ToLower() && !isFoundStartDate) { list.Add(new CalendarModel()); } else { isFoundStartDate = true; } list.AddRange(dates.Where(f => f.DayName.ToLower() == header.DayName.ToLower()).ToList()); CalendarList.Add(new Grouping<CalendarHeader, CalendarModel>(header, list)); list.Clear(); header = new CalendarHeader(); header.DayName = "Fri"; if (startDayName != header.DayName.ToLower() && !isFoundStartDate) { list.Add(new CalendarModel()); } else { isFoundStartDate = true; } list.AddRange(dates.Where(f => f.DayName.ToLower() == header.DayName.ToLower()).ToList()); CalendarList.Add(new Grouping<CalendarHeader, CalendarModel>(header, list)); list.Clear(); header = new CalendarHeader(); header.DayName = "Sat"; if (startDayName != header.DayName.ToLower() && !isFoundStartDate) { list.Add(new CalendarModel()); } else { isFoundStartDate = true; } list.AddRange(dates.Where(f => f.DayName.ToLower() == header.DayName.ToLower()).ToList()); CalendarList.Add(new Grouping<CalendarHeader, CalendarModel>(header, list)); } } protected void OnPropertyChanged([CallerMemberName] string propertyValue = "") { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyValue)); } #endregion #region Commands // used for Traverse through date public ICommand CurrentDateCommand { get { return new Command<CalendarModel>((item) => { if (item?.DayName != null) { var selectedDates = _staticCalendarList.Where(f => f.CurrentDate == true).ToList(); selectedDates.ForEach(f => { f.CurrentDate = false; }); CurrentDate = item.Date; CurrentYear = item.Date.Year; item.CurrentDate = true; SelectedDateInString = item.Date.ToString("ddd, dd MMMM"); SelectedDate = CurrentDate; } }); } } //Executed on the Tap of Cancel Button. public ICommand CancelCommand { get { return new Command(() => { SelectedDate = null; }); } } //Executed on the Tap of > Sign. public ICommand NextMonthCommand { get { return new Command(() => { if (CurrentDate != null) { bool isMaxDateReach = false; if (MaxYear == CurrentDate.Year && CurrentDate.Month == 12) { isMaxDateReach = true; } if (!isMaxDateReach) { CurrentDate = CurrentDate.Date.AddMonths(1); BindDates(CurrentDate.Year, CurrentDate.Month, CurrentDate.ToString("MMM"), null); } } }); } } //Executed on the Tap of < Sign. public ICommand PreviousMonthCommand { get { return new Command(() => { if (CurrentDate != null) { bool isMinDateReach = false; if (MinYear == CurrentDate.Year && CurrentDate.Month == 1) { isMinDateReach = true; } if (!isMinDateReach) { CurrentDate = CurrentDate.Date.AddMonths(-1); BindDates(CurrentDate.Year, CurrentDate.Month, CurrentDate.ToString("MMM"), null); } } }); } } public ICommand YearSelectionCommand { get { return new Command<int>((year) => { if (year > 0 && SelectedDate.HasValue) { DateTime newDate; try { newDate = new DateTime(year, (int)SelectedDate?.Month, (int)SelectedDate?.Day); } catch (ArgumentOutOfRangeException ex) { newDate = new DateTime(year, (int)SelectedDate?.Month, (int)SelectedDate?.Day - 1); } BindDates(year, newDate.Month, newDate.ToString("MMM"), newDate); IsCalendarDetailsVisible = true; } }); } } public ICommand ShowYearListCommand { get { return new Command(() => { if (CurrentDate != null) { IsCalendarDetailsVisible = !IsCalendarDetailsVisible; } }); } } #endregion } }
CalendarModel.cs
CalendarHeader.csusing System; using System.Collections.Generic; using System.ComponentModel; using System.Runtime.CompilerServices; using System.Text; namespace CustomCalendarControl.Models { public class CalendarModel : INotifyPropertyChanged { public int Year { get; set; } public int Month { get; set; } public string DayName { get; set; } public int DateInNumber { get; set; } public string DateInNumberStr { get; set; } private bool _currentDate; public bool CurrentDate { get => _currentDate; set { _currentDate = value; OnPropertyChanged(); } } public DateTime Date { get; set; } public event PropertyChangedEventHandler PropertyChanged; protected void OnPropertyChanged([CallerMemberName] string propertyValue = "") { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyValue)); } } }
Implementationsusing System; using System.Collections.Generic; using System.Text; namespace CustomCalendarControl.Models { public class CalendarHeader { public string DayName { get; set; } } }
MainPage.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"
x:Class="CustomCalendarControl.MainPage">
<StackLayout>
<Frame BackgroundColor="#2196F3" Padding="24" CornerRadius="0">
<Label Text="Welcome to Xamarin.Forms!" HorizontalTextAlignment="Center" TextColor="White" FontSize="36"/>
</Frame>
<Label x:Name="lblDate" />
<Button Text="Open Calendar" x:Name="btnCalendar" Clicked="btnCalendar_Clicked" />
</StackLayout>
</ContentPage>
MainPage.xaml.cs
using CalendarControl.Views;
using Rg.Plugins.Popup.Services;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Xamarin.Forms;
namespace CustomCalendarControl
{
public partial class MainPage : ContentPage
{
public MainPage()
{
InitializeComponent();
}
private async void btnCalendar_Clicked(object sender, System.EventArgs e)
{
var calendarPage = new CalendarPage(DateTime.Now);
calendarPage.SelectedDateCommand = new Command<DateTime>((item) =>
{
lblDate.Text = item.ToString("dd MMM yyyy");
});
await PopupNavigation.Instance.PushAsync(calendarPage);
}
}
}
No comments:
Post a Comment