Hello Bryan,
At the moment a relatively easy way to do this is to use a calendar with multiple selection and perhaps in its multi view mode. Almost a codeless solution, you can easily get the first and last date the user selected.
Here is also another example I made for you that uses date pickers (it applies to time picker and date-time pickers) that mimics validation messages and prevents the user from selecting wrong ranges. Feel free to improve it to match your needs (and, of course, to post such changes for the community to see if you like).
<style>
.msg {
color: rgba(243,23,0,0.5);
}
</style>
<div style="width: 500px;">
<TelerikDatePicker Value="@StartDate" ValueChanged="@( (DateTime d) => StartChanged(d) )" Max="@GetHigherDate()" Width="100%" />
<br />
<TelerikDatePicker Value="@EndDate" ValueChanged="@( (DateTime d) => EndChanged(d) )" Min="@GetLowerDate()" Width="100%" />
@*<TelerikDatePicker Value="@StartDate" OnChange="@StartChangedNotImmediate" Max="@GetHigherDate()" Width="100%" />
<br />
<TelerikDatePicker Value="@EndDate" OnChange="@EndChangedNotImmediate" Min="@GetLowerDate()" Width="100%" />*@
@if (ShowErrorMessage)
{
<span class="msg">@WrongRangeMessage</span>
}
</div>
<TelerikButton Enabled="@IsValid()" OnClick="@SaveAppointment">Save</TelerikButton>
@if (ShowResult)
{
<br />
@StartDate
<br />
@EndDate
}
@code{
DateTime StartDate { get; set; } = DateTime.Now;
DateTime EndDate { get; set; } = DateTime.Now;
string WrongRangeMessage = "Start date must be before end date. We reset the selection, try again.";
bool ShowErrorMessage { get; set; }
bool IsValid()
{
if(StartDate <= EndDate)
{
return true;
}
return false;
}
bool ShowResult { get; set; }
async void SaveAppointment()
{
if (IsValid())
{
//raise an event to pass the selection as all this would usually be encapsulated in its own component
//here we will just flicker the result for a bit for brevity of the example
ShowResult = true;
await Task.Delay(2000);
ShowResult = false;
StateHasChanged();
}
}
void StartChanged(DateTime userChoice)
{
if (userChoice > GetHigherDate())
{
ShowErrorMessage = true;
}
else
{
StartDate = userChoice;
ShowErrorMessage = false;
}
}
void EndChanged(DateTime userChoice)
{
if (userChoice < GetLowerDate())
{
ShowErrorMessage = true;
}
else
{
EndDate = userChoice;
ShowErrorMessage = false;
}
}
DateTime GetLowerDate()
{
return StartDate <= EndDate ? StartDate : EndDate;
}
DateTime GetHigherDate()
{
return StartDate >= EndDate ? StartDate : EndDate;
}
//goes with the commented pickers that use the OnChange event - resets the value and shows
//the message only after the OnChang event fires - blur or enter on the input, not on every change
//so it gives the user time to make a mistake whilte typing and correct without resetting immediately
//this is just an example so you can choose the UX you prefer
//void StartChangedNotImmediate(object userInput)
//{
// DateTime userChoice = (DateTime)userInput;
// if (userChoice > GetHigherDate())
// {
// ShowErrorMessage = true;
// }
// else
// {
// StartDate = userChoice;
// ShowErrorMessage = false;
// }
//}
//void EndChangedNotImmediate(object userInput)
//{
// DateTime userChoice = (DateTime)userInput;
// if (userChoice < GetLowerDate())
// {
// ShowErrorMessage = true;
// }
// else
// {
// EndDate = userChoice;
// ShowErrorMessage = false;
// }
//}
}
Regards,
Marin Bratanov
Progress Telerik