Schedule control

SamVanCutsem.2880SamVanCutsem.2880 BEMember
edited October 2016 in Xamarin.Forms

Hello
I have some trouble creating a Xamarin.Forms week scheduler. Our client wants a scrollable scheduler that looks like this:

So I’ve created a Grid with 7 columns with a RelativeLayout in it. With some binding properties I bind a collection of WeekOverviewViewModels with a key of the current date and a list of PlanningEntries. I’ve placed this control in a CarouselView but when I scroll it hurts the performance. I’ve found that RelativeLayouts are best not used in scrollable views.

AbsoluteLayout has the potential to perform layouts without a single measure call. This makes it very powerful for performance. If AbsoluteLayout cannot be used, consider RelativeLayout. If using RelativeLayout, passing Constraints directly will be considerably faster than using the expression API. That is because the expression API uses JIT, and on iOS the tree has to be interpreted, which is slower. The expression API is suitable for page layouts where it only required on initial layout and rotation, but in ListView, where it's run constantly during scrolling, it hurts performance.

So it’s not optimal to use RelativeLayouts in a CarouselView. Is there another way to draw those blocks based on the start and end time of a PlanningEntry?
Here is the code I used:

<?xml version="1.0" encoding="UTF-8"?> <ContentView xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="Core.Mobile.Controls.WeekOverviewControl" xmlns:i18n="clr-namespace:Core.Mobile.Extensions;assembly=Core.Mobile"> <ContentView.Content> <StackLayout> <Grid x:Name="WeekOverviewHeader"> <Grid.ColumnDefinitions> <ColumnDefinition Width="*" /> <ColumnDefinition Width="*" /> <ColumnDefinition Width="*" /> <ColumnDefinition Width="*" /> <ColumnDefinition Width="*" /> <ColumnDefinition Width="*" /> <ColumnDefinition Width="*" /> </Grid.ColumnDefinitions> <Grid.RowDefinitions> <RowDefinition Height="Auto" /> <RowDefinition Height="Auto" /> </Grid.RowDefinitions> <Label Grid.Row="0" Grid.Column="0" HorizontalTextAlignment="Center" HorizontalOptions="CenterAndExpand" FontAttributes="Bold" TextColor="#d8d8d8" Text="{i18n:Translate PlanningOverviewView_Monday}" /> <Label Grid.Row="1" Grid.Column="0" x:Name="MondayNumericLabel" HorizontalTextAlignment="Center" HorizontalOptions="CenterAndExpand" FontSize="Micro" TextColor="#d8d8d8" /> <Label Grid.Row="0" Grid.Column="1" HorizontalTextAlignment="Center" HorizontalOptions="CenterAndExpand" FontAttributes="Bold" TextColor="#d8d8d8" Text="{i18n:Translate PlanningOverviewView_Tuesday}" /> <Label Grid.Row="1" Grid.Column="1" x:Name="TuesdayNumericLabel" HorizontalTextAlignment="Center" HorizontalOptions="CenterAndExpand" FontSize="Micro" TextColor="#d8d8d8" /> <Label Grid.Row="0" Grid.Column="2" HorizontalTextAlignment="Center" HorizontalOptions="CenterAndExpand" FontAttributes="Bold" TextColor="#d8d8d8" Text="{i18n:Translate PlanningOverviewView_Wednesday}" /> <Label Grid.Row="1" Grid.Column="2" x:Name="WednesdayNumericLabel" HorizontalTextAlignment="Center" HorizontalOptions="CenterAndExpand" FontSize="Micro" TextColor="#d8d8d8" /> <Label Grid.Row="0" Grid.Column="3" HorizontalTextAlignment="Center" HorizontalOptions="CenterAndExpand" FontAttributes="Bold" TextColor="#d8d8d8" Text="{i18n:Translate PlanningOverviewView_Thursday}" /> <Label Grid.Row="1" Grid.Column="3" x:Name="ThursdayNumericLabel" HorizontalTextAlignment="Center" HorizontalOptions="CenterAndExpand" FontSize="Micro" TextColor="#d8d8d8" /> <Label Grid.Row="0" Grid.Column="4" HorizontalTextAlignment="Center" HorizontalOptions="CenterAndExpand" FontAttributes="Bold" TextColor="#d8d8d8" Text="{i18n:Translate PlanningOverviewView_Friday}" /> <Label Grid.Row="1" Grid.Column="4" x:Name="FridayNumericLabel" HorizontalTextAlignment="Center" HorizontalOptions="CenterAndExpand" FontSize="Micro" TextColor="#d8d8d8" /> <Label Grid.Row="0" Grid.Column="5" HorizontalTextAlignment="Center" HorizontalOptions="CenterAndExpand" FontAttributes="Bold" TextColor="#d8d8d8" Text="{i18n:Translate PlanningOverviewView_Saterday}" /> <Label Grid.Row="1" Grid.Column="5" x:Name="SaterdayNumericLabel" HorizontalTextAlignment="Center" HorizontalOptions="CenterAndExpand" FontSize="Micro" TextColor="#d8d8d8" /> <Label Grid.Row="0" Grid.Column="6" HorizontalTextAlignment="Center" HorizontalOptions="CenterAndExpand" FontAttributes="Bold" TextColor="#d8d8d8" Text="{i18n:Translate PlanningOverviewView_Sunday}" /> <Label Grid.Row="1" Grid.Column="6" x:Name="SundayNumericLabel" HorizontalTextAlignment="Center" HorizontalOptions="CenterAndExpand" FontSize="Micro" TextColor="#d8d8d8" /> </Grid> <ScrollView> <Grid> <Grid.ColumnDefinitions> <ColumnDefinition Width="*" /> <ColumnDefinition Width="*" /> <ColumnDefinition Width="*" /> <ColumnDefinition Width="*" /> <ColumnDefinition Width="*" /> <ColumnDefinition Width="*" /> <ColumnDefinition Width="*" /> </Grid.ColumnDefinitions> <Grid.RowDefinitions> <RowDefinition Height="360" /> </Grid.RowDefinitions> <RelativeLayout x:Name="MondayRelativeLayout" Grid.Row="0" Grid.Column="0" VerticalOptions="FillAndExpand" HorizontalOptions="FillAndExpand" BackgroundColor="#d8d8d8" /> <RelativeLayout x:Name="TuesdayRelativeLayout" Grid.Row="0" Grid.Column="1" VerticalOptions="FillAndExpand" HorizontalOptions="FillAndExpand" BackgroundColor="#d8d8d8" /> <RelativeLayout x:Name="WednesDayRelativeLayout" Grid.Row="0" Grid.Column="2" VerticalOptions="FillAndExpand" HorizontalOptions="FillAndExpand" BackgroundColor="#d8d8d8" /> <RelativeLayout x:Name="ThursdayRelativeLayout" Grid.Row="0" Grid.Column="3" VerticalOptions="FillAndExpand" HorizontalOptions="FillAndExpand" BackgroundColor="#d8d8d8" /> <RelativeLayout x:Name="FridayRelativeLayout" Grid.Row="0" Grid.Column="4" VerticalOptions="FillAndExpand" HorizontalOptions="FillAndExpand" BackgroundColor="#d8d8d8" /> <RelativeLayout x:Name="SaterdayRelativeLayout" Grid.Row="0" Grid.Column="5" VerticalOptions="FillAndExpand" HorizontalOptions="FillAndExpand" BackgroundColor="#d8d8d8" /> <RelativeLayout x:Name="SundayRelativeLayout" Grid.Row="0" Grid.Column="6" VerticalOptions="FillAndExpand" HorizontalOptions="FillAndExpand" BackgroundColor="#d8d8d8" /> </Grid> </ScrollView> </StackLayout> </ContentView.Content> </ContentView>

`public partial class WeekOverviewControl : ContentView
{
public static readonly BindableProperty PlanningEntriesProperty =
BindableProperty.Create(nameof(PlanningEntries), typeof(IList<Grouping<DateTime, PlanningEntry>>), typeof(WeekOverviewControl));

    public static readonly BindableProperty CurrentWeekProperty =
            BindableProperty.Create(nameof(CurrentWeek), typeof(DateTime), typeof(WeekOverviewControl), defaultValue: DateTime.Now);

    public static readonly BindableProperty PlanningEntryCommandProperty =
            BindableProperty.Create(nameof(PlanningEntryCommand), typeof(ICommand), typeof(WeekOverviewControl));

    public WeekOverviewControl()
    {
        InitializeComponent();
    }

    public IList<Grouping<DateTime, PlanningEntry>> PlanningEntries
    {
        get { return (IList<Grouping<DateTime, PlanningEntry>>)GetValue(PlanningEntriesProperty); }
        set { SetValue(PlanningEntriesProperty, value); }
    }

    public DateTime CurrentWeek
    {
        get { return (DateTime)GetValue(CurrentWeekProperty); }
        set { SetValue(CurrentWeekProperty, value); }
    }

    public ICommand PlanningEntryCommand
    {
        get { return (ICommand)GetValue(PlanningEntryCommandProperty); }
        set { SetValue(PlanningEntryCommandProperty, value); }
    }

    protected override void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        base.OnPropertyChanged(propertyName);
        if (propertyName.Equals(nameof(PlanningEntries)))
        {
            MondayNumericLabel.Text = CurrentWeek.Day.ToString();
            TuesdayNumericLabel.Text = CurrentWeek.AddDays(1).Day.ToString();
            WednesdayNumericLabel.Text = CurrentWeek.AddDays(2).Day.ToString();
            ThursdayNumericLabel.Text = CurrentWeek.AddDays(3).Day.ToString();
            FridayNumericLabel.Text = CurrentWeek.AddDays(4).Day.ToString();
            SaterdayNumericLabel.Text = CurrentWeek.AddDays(5).Day.ToString();
            SundayNumericLabel.Text = CurrentWeek.AddDays(6).Day.ToString();


            foreach (var planningEntry in PlanningEntries)
            {
                switch (planningEntry.Key.DayOfWeek)
                {
                    case DayOfWeek.Friday:
                        AddToRelativeLayout(planningEntry, FridayRelativeLayout);
                        break;
                    case DayOfWeek.Monday:
                        AddToRelativeLayout(planningEntry, MondayRelativeLayout);
                        break;
                    case DayOfWeek.Saturday:
                        AddToRelativeLayout(planningEntry, SaterdayRelativeLayout);
                        break;
                    case DayOfWeek.Sunday:
                        AddToRelativeLayout(planningEntry, SundayRelativeLayout);
                        break;
                    case DayOfWeek.Thursday:
                        AddToRelativeLayout(planningEntry, ThursdayRelativeLayout);
                        break;
                    case DayOfWeek.Tuesday:
                        AddToRelativeLayout(planningEntry, TuesdayRelativeLayout);
                        break;
                    case DayOfWeek.Wednesday:
                        AddToRelativeLayout(planningEntry, WednesDayRelativeLayout);
                        break;
                    default:
                        break;
                }
            }
        }
    }

    private void AddToRelativeLayout(IEnumerable<PlanningEntry> planningEntries, RelativeLayout dayRelativeLayout)
    {
        foreach (var planningEntry in planningEntries)
        {
            var planningLayout = CreatePlanningStackLayout();
            var startTimeLabel = CreatePlanningTimeLabel();
            var endTimeLabel = CreatePlanningTimeLabel();

            var tapPlanningEntry = new TapGestureRecognizer();
            tapPlanningEntry.Command = PlanningEntryCommand;
            tapPlanningEntry.CommandParameter = planningEntry;
            planningLayout.GestureRecognizers.Add(tapPlanningEntry);

            var converter = new Converters.PlanningEntryToColorConverter();
            var color = (Color)converter.Convert(planningEntry, null, null, null);
            //planningLayout.BackgroundColor = color;
            planningLayout.BackgroundColor = Color.Green;

            startTimeLabel.Text = planningEntry.Start.ToString("HH:mm");
            endTimeLabel.Text = planningEntry.End.ToString("HH:mm");
            endTimeLabel.VerticalOptions = LayoutOptions.EndAndExpand;

            Device.OnPlatform(iOS: () =>
            {
                startTimeLabel.FontSize = Device.GetNamedSize(NamedSize.Micro, typeof(Label));
                endTimeLabel.FontSize = Device.GetNamedSize(NamedSize.Micro, typeof(Label));
            });

            if (planningEntry.Start.TimeOfDay != new TimeSpan(0, 0, 0, 0, 0))
                planningLayout.Children.Add(startTimeLabel);
            if (planningEntry.End.TimeOfDay != new TimeSpan(0, 0, 0, 0, 0))
                planningLayout.Children.Add(endTimeLabel);

            dayRelativeLayout.Children.Add(planningLayout,
                        Constraint.RelativeToParent((parent) => { return 0; }),
                        Constraint.RelativeToParent((parent) => { return parent.Height / 24 * (planningEntry.Start.Hour + (planningEntry.Start.Minute / 60)); }),
                        Constraint.RelativeToParent((parent) => { return parent.Width; }),
                        Constraint.RelativeToParent((parent) => { return parent.Height / 24 * Convert.ToDouble((planningEntry.End - planningEntry.Start).TotalHours); }));
        }
    }

    private static StackLayout CreatePlanningStackLayout()
    {
        return new StackLayout()
        {
            Padding = new Thickness(3)
        };
    }

    private static Label CreatePlanningTimeLabel()
    {
        return new Label()
        {
            //TODO: Change Color to resource
            TextColor = Color.White,
            HorizontalTextAlignment = TextAlignment.Center
        };
    }
}`

<?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:controls="clr-namespace:Core.Mobile.Controls;assembly=Core.Mobile" xmlns:cv="clr-namespace:Xamarin.Forms;assembly=Xamarin.Forms.CarouselView" x:Class="Core.Mobile.Views.WeekOverviewView"> <cv:CarouselView Position="{Binding Position, Mode=TwoWay}" Item="{Binding SelectedWeekOverviewViewModel, Mode=TwoWay}" ItemsSource="{Binding WeekOverviewViewModels}" x:Name="WeekOverviewCarousel"> <cv:CarouselView.ItemTemplate> <DataTemplate> <controls:WeekOverviewControl CurrentWeek="{Binding CurrentWeek}" PlanningEntries="{Binding WeekOverview}" /> </DataTemplate> </cv:CarouselView.ItemTemplate> </cv:CarouselView> </ContentPage>

A PlanningEntry contains a StartDate and and EndDate.

Thanks in advance!

Sam

Sign In or Register to comment.