ME-MarvinE / XCalendar

A plugin for .NET providing an API for representing a calendar along with fully customisable calendar controls for Xamarin Forms and .NET MAUI
MIT License
300 stars 36 forks source link

Passing DateTime of a day in viewmodel to make more control for background changing #111

Open masterofdaemon opened 1 year ago

masterofdaemon commented 1 year ago

Hello, i have a problem with some logic, how can i pass here DateTime of a day instead static one 9-2-2023 ?

<xc:CalendarView.DayTemplate>
                <DataTemplate x:DataType="{x:Type xcModels:CalendarDay}">
                    <Border
                        Margin="2.5"
                        BackgroundColor="{Binding BindingContext.ObservableParameters[9-2-2023], Source={x:Reference This},

or how i can make it in c#?

new CalendarView{ VerticalOptions = LayoutOptions.Start}
                .Bind(CalendarView.DaysProperty, static(WorkDayViewModel vm) => vm.Calendar.Days)
                .Bind(CalendarView.DaysOfWeekProperty, static(WorkDayViewModel vm) => vm.Calendar.DayNamesOrder)

i can't make children of it and this solution is faster i think

masterofdaemon commented 1 year ago
new CalendarView{ VerticalOptions = LayoutOptions.Start, DayTemplate = new DataTemplate{}
    }

that's all i can get but i don't know how to implement DataTemplate, help me please, with any solution

ME-MarvinE commented 1 year ago

The DataTemplate you provide for CalendarView.DayTemplate gets passed a BindingContext of the type of day you used in the CalendarView's Days property. The default Calendar uses CalendarDay.

So just like how you would normally bind to your ViewModel's properties, you can bind to all the properties of the CalendarDay instead. The CalendarDay's DateTime property is what you are looking for, so in your example, you would have BackgroundColor="{Binding DateTime}".

If you want to change this value from your ViewModel, just change it in the actual collection you binded the CalendarView's Days property to.

public void Set8thDayToToday()
{
    //Assuming you have binded the 'Days' property to 'MyCalendar.Days' in your CalendarView:
    MyCalendar.Days[7].DateTime = DateTime.Today;
}

However, since the days' properties get changed by the Calendar such as CalendarDay.DateTime when navigating or CalendarDay.IsSelected when the Calendar.SelectedDates collection is modified (the logic for this is overridable in child classes), you'll probably want to attach an event handler to the Calendar's DaysUpdated event and change the values in there. The DaysUpdated event gets called after the Calendar updates the properties of the days in its Days collection.

For example:

ViewModel:

public class MyViewModel
{
    public Calendar<CalendarDay> MyCalendar { get; set; } =  new Calendar();

    public MyViewModel()
    {
        MyCalendar.DaysUpdated += MyCalendar_DaysUpdated();
    }

    public void MyCalendar_DaysUpdated(object sender, EventArgs e)
    {
        Set8thDayToToday();
    }

    public void Set8thDayToToday()
    {
        MyCalendar.Days[7].DateTime = DateTime.Today;
    }
}

View:

<xc:CalendarView Days="{Binding MyCalendar.Days"} DaysOfWeek="{Binding MyCalendar.DayNamesOrder}" NavigatedDate="{Binding MyCalendar.NavigatedDate}">

    <xc:CalendarView.DayTemplate>
        <DataTemplate>
            <!--This will bind to the 'DateTime' property of the CalendarDay-->
            <Label Text="{Binding DateTime}"/>
        </DataTemplate>
    </xc:CalendarView.DayTemplate>

</xc:CalendarView>

I don't know how to create views in C#. You can find many resources on the internet that will explain bindings in C# better than me.

masterofdaemon commented 1 year ago

i need to make work day another color and selected another too : ` WorkDays.Add(new DateTime(2023, 2, 9));

        WorkDays.Add(new DateTime(2023, 2, 10));

        WorkDays.Add(new DateTime(2023, 2, 11));

        WorkDays.Add(new DateTime(2023, 2, 12));

        foreach (var day in Calendar.Days)
        {
            if (WorkDays.Contains(day.DateTime))
            {
                Parameters.Add($"{day.DateTime.Day}-{day.DateTime.Month}-{day.DateTime.Year}", Color.FromArgb("#00f"));
            }
            else
            {
                Parameters.Add($"{day.DateTime.Day}-{day.DateTime.Month}-{day.DateTime.Year}", Color.FromArgb("#f00"));
            }
        }`
ME-MarvinE commented 1 year ago

You can replicate the default functionality of a day by using a DayView in the CalendarView's DayTemplate property. You will need to bind the IsSelected, IsToday, IsCurrentMonth, and IsInvalid properties of the DayView to the ones in the CalendarDay. You will also need to set the SelectedCommand, TodayCommand, and CurrentMonthCommand to the command you use to select a day.

To change what colours the DayView changes to, there are properties for the TextColor and BackgroundColor for each state the CalendarDay can be in. For example SelectedBackgroundColor will be the colour used when the day is selected, OtherMonthCommand will occurr when the day is not in the current month. All the custom properties for DayView are explained on this page in the wiki.

Currently there is no easy way to override the changes that the DayView makes based on the DayState. That's one of the things I need to work on. For setting the background colour on a work day, here are some work arounds:

ME-MarvinE commented 1 year ago

DayViews can now be customised using styles for each DayState instead of individual properties, for example CurrentMonthStyle.

Default functionality can now be replicated by using a markup reference to set the BasedOn property of your style to a style defined in Xamarin.[Forms/Maui].DefaultStyles. More info in the DayView wiki page.

There is now an easy way to override the changes that the DayView makes based on the DayState.DayViews: set the [DayState]Style property to null, for example `DayViewOtherMonthStyle="{x:Null}".

Your "IsWorkDay" property would go in the class that inherits from CalendarDay or implements ICalendarDay and then you would use the above methods to override the default colours while still getting the default look for everything else.

Did this solve your issue?