In Power Apps non esiste nativamente un controllo calendario, ma può comunque essere realizzato tramite un componente.
CalendarioCalendario

Component


Per prima cosa creiamo un nuovo componente
New componentNew component
Vanno aggiunte due gallery blank vertical, una per l'header delle colonne e uno per i giorni del mese:
Gallery blank verticalGallery blank vertical
Il nuovo componente avrà questa struttura:
  • CompSgartCalendar
  • lblCopyright
  • glryHeader
  • lblHeader
  • rectHeader
  • glryDays
  • lblDay
  • icnEvent
  • icnToday
  • rectDay
Ogni componente dovrà avere le proprietà impostate in modo appropriato, vedi dopo.
Custom properties

Perchè funzioni, vanno aggiunte delle custom properties al componente:
Custom propertiesCustom properties
 #class=table-striped
Display name;Name;Property Type;Data Type;Rise OnReset;DefaultValue
Current date;CurrentDate;Input;Date andTime;true;Today()
Current day color;CurrentDayColor;Input;Color;false;RGBA(128, 0, 0, 1)
Event dates;EventDates;Input;Table;false;[Today(), Today()+1]
Event color;eventColor;Input;Color;false;RGBA(255, 0, 0, 1)
Header labels;HeaderLabels;Input;Table;false;["Lun", "Mar", "Mer", "Gio", "Ven","Sab", "Dom"]
Header fill;HeaderFill;Input;Color;false;RGBA(255, 119, 40, 1)
Header color;HeaderColor;Input;Color;false;RGBA(250, 250,250, 1)
Day fill;DayFill;Input;Color;false;RGBA(246, 239, 221, 1)
Day color;DayColor;Input;Color;false;RGBA(0, 0, 0, 1)
Selectedcolor;SelectedColor;Input;Color;false;RGBA(255, 119, 40, .5)
Selected date;SelectedDate;Output;Date and time;false;_selectedDate
i valori di default delle proprietà possono essere impostate andando nella scheda Advanced
Advanced default propertyAdvanced default property
Le proprietà permettono di impostare la data del mese da visualizzare, i colori dei vari elementi, oltre a poter ottenere la proprietà di uscita (SelectedDate).
Set componente

Il passo successivo è fare alcune inizializzazioni nell'evento OnReset
OnResetOnReset
/* giorno corrente */
Set(_currentDate, Today());
If(IsBlank(CompSgartCalendar.CurrentDate) = false, Set(_currentDate, CompSgartCalendar.CurrentDate));
/* data scelta dall'utente di output */
Set(_selectedDate, _currentDate);
/* anno, mese, giorno corrente */
Set(_year, Year(_currentDate));
Set(_month, Month(_currentDate));
Set(_day, Day(_currentDate));
/* primo giorno del mese */
Set(_date1, DateAdd(_currentDate, -_day + 1, Days));
/* determino il gioro della sittimana */
Set(_dayOffset, Weekday(_date1, StartOfWeek.Monday)-1);
/* primo giorno visualizzato sul calenario */
Set(_firstDate, DateAdd(_date1, -_dayOffset,Days));
/* calcolo l'ultimo indice da visualizzare */
Set(_lastDay, Day(DateAdd(DateAdd(_date1, 1, Months), -1, Days)));
Set(_lastIndex, If(_dayOffset + _lastDay > 34, 41, 34));
A questo punto si possono impostare le proprietà dei controlli per:
  • rendere il controllo responsive
  • visualizzare il giorno corrente (Today)
  • visualizzare un segno di spunta se la data è una di quelle passate nella proprietà EventDates
  • evidenziare il giorno selezionato tramite click (valore che verrà riportato nella property di output SelectedDate)

Entrando nel dettaglio, le proprietà dei controlli andranno impostate nel modo seguente:
/* glryHeaders */
glryHeaders.BorderThickness = 0
glryHeaders.Fill = RGBA(0, 0, 0, 0)
glryHeaders.Height = Parent.Height / 7
glryHeaders.Items = CompSgartCalendar.HeaderLabels
glryHeaders.ShowScrollbar = false
glryHeaders.TemplatePadding = 2
glryHeaders.TemplateSize = Self.Height - Self.TemplatePadding * 2
glryHeaders.Width = Parent.Width
glryHeaders.WrapCount = 7
glryHeaders.X = 0
glryHeaders.Y = 0

/* glryHeaders lblHeader */
lblHeader.BorderThickness = 0
lblHeader.Color = CompSgartCalendar.HeaderColor
lblHeader.Fill = RGBA(0, 0, 0, 0)
lblHeader.FontWeight = Normal
lblHeader.Height = Parent.TemplateHeight
lblHeader.PaddingBottom = 0
lblHeader.PaddingLeft = 0
lblHeader.PaddingRight = 0
lblHeader.PaddingTop = 0
lblHeader.Text = ThisItem.Value
lblHeader.Width = Parent.TemplateWidth
lblHeader.X = 0
lblHeader.Y = 0

/* glryHeaders rectHeader */
rectHeader.BorderThickness = 0
rectHeader.Fill = CompSgartCalendar.HeaderFill
rectHeader.Height = Parent.TemplateHeight
rectHeader.Width = Parent.TemplateWidth
rectHeader.X = 0
rectHeader.Y = 0

/* glryDays */
glryDays.BorderThickness = 0
glryDays.Fill = RGBA(0, 0, 0, 0)
glryDays.Height =Parent.Height - glryHeaders.Height
glryDays.Items =  [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41]
glryDays.OnSelect = Set(_selectedDate, DateAdd(_firstDate, ThisItem.Value, Days))
glryDays.ShowScrollbar = false
glryDays.TemplatePadding = 2
glryDays.TemplateSize = Parent.Height / 7 - Self.TemplatePadding -1
glryDays.Width = Parent.Width
glryDays.WrapCount = 7
glryDays.X = 0
glryDays.Y = glryHeaders.Height

/* glryDays lblDay */
lblDay.BorderThickness = 0
lblDay.Color = If(IsNumeric(ThisItem.Value), CompSgartCalendar.DayColor, CompSgartCalendar.HeaderColor)
lblDay.Fill = If(DateAdd(_firstDate, ThisItem.Value, Days) = _selectedDate, CompSgartCalendar.SelectedColor, RGBA(0,0,0,0))
lblDay.FontWeight = Normal
lblDay.Height = Parent.TemplateHeight
lblDay.OnSelect  = Select(Parent)
lblDay.PaddingBottom = 0
lblDay.PaddingLeft = 0
lblDay.PaddingRight = 0
lblDay.PaddingTop = 0
lblDay.Size = 21
lblDay.Text = Day(DateAdd(_firstDate, ThisItem.Value, Days))
lblDay.Visible  =ThisItem.Value <= _lastIndex
lblDay.Width = Parent.TemplateWidth
lblDay.X = 0
lblDay.Y = 0

/* glryDays rectDay */
rectDay.BorderThickness = 0
rectDay.Fill = If(_month = Month(DateAdd(_firstDate, ThisItem.Value, Days)) ,CompSgartCalendar.DayFill, RGBA(0,0,0,0))
rectDay.Height = Parent.TemplateHeight
rectDay.Width = Parent.TemplateWidth
rectDay.X = 0
rectDay.Y = 0

/* glryDays icnEvent */
icnEvent.BorderThickness = 0
icnEvent.Color = CompSgartCalendar.EventColor
icnEvent.Fill = RGBA(0, 0, 0, 0)
icnEvent.Height = 20
icnEvent.Icon = Check
icnEvent.OnSelect  = Select(Parent)
icnEvent.PaddingBottom = 0
icnEvent.PaddingLeft = 0
icnEvent.PaddingRight = 0
icnEvent.PaddingTop = 0
icnEvent.Visible = lblDay.Visible && IsBlank(LookUp(CompSgartCalendar.EventDates, ThisRecord.Value = DateAdd(_firstDate, ThisItem.Value, Days)).Value)=false
icnEvent.Width = 20
icnEvent.X = Parent.TemplateWidth - Self.Width - 5
icnEvent.Y = Parent.TemplateHeight - Self.Height - 5

/* glryDays icnToday (Right triangle) */
icnToday.BorderThickness = 0
icnToday.Fill = CompSgartCalendar.CurrentDayColor
icnToday.Height = 25
icnToday.OnSelect  = Select(Parent)
icnToday.PaddingBottom = 0
icnToday.PaddingLeft = 0
icnToday.PaddingRight = 0
icnToday.PaddingTop = 0
icnToday.Visible = lblDay.Visible && DateAdd(_firstDate, ThisItem.Value, Days) = Today()
icnToday.Width = 25
icnToday.X = 0
icnToday.Y = Parent.TemplateHeight - Self.Height

/* lblCopyright */
lblDay.BorderThickness = 0
lblDay.Color = RGBA(127, 0, 0, .6)
lblDay.Fill = RGBA(0, 0, 0, 0)
lblDay.FontWeight = FontWeight.Bold
lblDay.Height = 40
lblDay.Size = 16
lblDay.Text = "sgart.it"
lblDay.Width = 150
lblDay.X = Parent.Width - Self.Width
lblDay.Y = Parent.Height - Self.Height
In particolare la gallery glryDays dovrà avere la proprietà Items impostata con una collection fissa che copra il numero di giorni visualizzabili: [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41].

Sia glryHeader che glryDays dovranno avere la proprietà WrapCount uguale a 7 per fare in modo che le colonne si ripetano orizzontalmente.

La proprietà Text della label lblDay dovrà visualizzare il giorno corretto: Day(DateAdd(_firstDate, ThisItem.Value, Days)).

Il background del rettangolo rectDay, proprietà Fill, evidenzierà se il giorno fa parte o meno del mese selezionato:If(_month = Month(DateAdd(_firstDate, ThisItem.Value, Days)) ,CompSgartCalendar.DayFill, RGBA(0,0,0,0))

Screen


Una volta realizzato il componente può essere aggiunto alla pagina (screen):
componente in paginacomponente in pagina
Nell'esempio i controlli hanno questa struttura:
  • ScrnCalendar
  • icnright
  • icnLeft
  • lblMontYear
  • lblTitleEvents
  • lblSelected
  • lblTitleSelected
  • DatePicker1
  • CompSgartCalendar_1 (il componente appena realizzato)
  • glryDates
  • lblDate
  • lblDay
  • icnEvent
  • icnToday
  • rectDay
dove le proprietà principali vanno impostate in questo modo:
/* App */
App.OnStart = ClearCollect(TableDates, [Today(), Date(2020,11,4), Date(2020,11,24), Date(2020,12,25), Date(2020,12,26)]); Set(_currentDate, Today());

/* DatePicker1 */
DatePicker1 .DefaultDate = _currentDate

/* CompSgartCalendar_1 */
CompSgartCalendar_1.CurrentDate = DatePicker1.SelectedDate
CompSgartCalendar_1.CurrentDayColor = RGBA(128, 0, 0, 1)
CompSgartCalendar_1.DayColor = RGBA(0, 0, 0, 1)
CompSgartCalendar_1.DayFill = RGBA(246, 239, 221, 1)
CompSgartCalendar_1.EventColor = RGBA(255, 0, 0, 1)
CompSgartCalendar_1.EventDates= TableDates
CompSgartCalendar_1.Fill = RGBA(253, 222, 207, .1)
CompSgartCalendar_1.HeaderColor = RGBA(250, 250,250, 1)
CompSgartCalendar_1.HeaderFill = RGBA(255, 119, 40, 1)
CompSgartCalendar_1.HeaderLabels = ["Lun", "Mar", "Mer", "Gio", "Ven","Sab", "Dom"]
CompSgartCalendar_1.SelectedColor = RGBA(255, 119, 40, .5)

/* lblSelected - visualizzo il parametro di output */
lblSelected .Text = Text(CompSgartCalendar_1.SelectedDate, DateTimeFormat.ShortDate, "it-IT")

/* icnRight -  mese successivo */
icnRight.OnSelect = Set(_currentDate, DateAdd(DatePicker1.SelectedDate, 1,Months))

/* icnRight -  mese precedente */
icnRight.OnSelect = Set(_currentDate, DateAdd(DatePicker1.SelectedDate, -1,Months))

/* glryDates */
glryDates.Items = SortByColumns(TableDates, "Value")
lblDate.Text = ThisItem.Value
La collection TableDates contiene le date degli eventi da visualizzare sul calendario. La label lblSelected visualizza il parametro di output del calendario SelectedDate.

Realizzare questo controllo è un buon esercizio didattico che aiuta a prendere familiarità con il mondo Power Apps e i componenti.

Alternativa


Come dicevo all'inizio, non esiste un controllo calendario nativo, ma esiste un template di schermo di tipo calendario.
Template screen calendarTemplate screen calendar
Questo template aggiunge un calendario con tutti i controlli di navigazione e un data source collegato al calendario dell'utente:
Screen con calendarScreen con calendar
può essere un ottima base di partenza per eventuali personalizzazioni, come ad esempio cambiare il data source e farlo puntare a un data source diverso.