DotNetT Dark_870x220

No more using multiple date time pickers to setup your time intervals. We are happy to introduce the brand new TimeSpanPicker control for WinForms applications. Learn all about the new control in the following article.

We've received multiple requests through the WinForms feedback portal to add a time span picker to the Telerik UI for WinForms suite and they have not been disregarded. In case you missed it, as of earlier this year, with the Telerik R1 2019 release, the new TimeSpanPicker control is available in the suite.

The RadTimeSpanPicker is a UI component that provides full control over selecting a specific time span and duration within certain ranges using the built-in components for days, hours, minutes, seconds and milliseconds.

Meet RadTimeSpanPicker

Imagine that we need to implement a flight booking system. First, we need to place a couple of DropDownLists for the from/to destinations, a DateTimePicker for picking the date of the flight and perhaps a filter for how long flight we will need. Here, in the last part, is where the RadTimeSpanPicker comes into play.

time-span-picker-flight

What we did above to configure the control is simply place it in the form and set its Format, and then we get the value the user entered from the Value property. We can also use the ValueChanged event to get notified when the Value property is changed. As simple as this.

this.radTimeSpanPicker1.Format = "'Under 'hh' hours'";
 
private void RadTimeSpanPicker1_ValueChanged(object sender, EventArgs e)
{
    TimeSpan flightDuration = this.radTimeSpanPicker1.Value.Value;     
    // filter the flights
}

Other Useful Functionalities of the Control:

  • Negative values – useful to show time overdue cases
  • Binding – through the simple data binding
  • Localization – make the control speak any language with the convenient localization provider
  • EditMode – various edit modes are provided to capture the different use cases the control can be used in
    • Mask – limit the user to just use the text box to modify the value
    • Popup – limit the user to pick only predefined values from the pop-up
    • Combined – give the user full flexibility
  • Step properties – define the steps for each component, e.g. by 10min, by 2 hours, etc.
  • ReadOnly – this lets you display time span information to the user without allowing editing, or allows you to enable/disable the editing capability per some business logic
  • Null Values support – the control happily accepts null as Value, which is useful in data bound scenarios, in this case the NullText property can be used to show prompt text
  • SpinButtons – a convenient feature that you can enable for users to change the value with the built in buttons in the text box
  • Various events are at your leisure to get informed of value being changed (ValueChanging and ValueChanged), pop-up open/close (PopupOpened/Closed), as well as convenient extensibility point where we can plug in custom logic (the ComponentCreated and ComponentsCreated events of PopupContentElement) - more on this topic later

Structure

The control consists of two main elements: RadMaskedEditBoxElement and RadTimeSpanPickerContentElement.

RadMaskedEditBoxElement

RadMaskedEditBoxElement allows users to modify the value directly in the text box and at the same time validates it. The Format property specifies which portions of a time span value can be edited. The default format is “dd:hh:mm:ss.fff”

time span picker

To add specific content to the format string you will need to enclose it with single quotes. Here is an example that shows this.

this.radTimeSpanPicker1.Format = "dd' days, 'hh' hours, 'mm' minutes, 'ss' seconds, 'fff' milliseconds'";

time span picker custom format

RadTimeSpanPickerContentElement 

RadTimeSpanPickerContentElement is the main element of the pop-up and hosts a collection of components. Each component represents a different part of the TimeSpan object.

time span picker popup

We provide five default time span part components, so you can easily set up the time intervals you wish to pick:

  • DayTimeSpanPickerComponent
  • HourTimeSpanPickerComponent
  • MinuteTimeSpanPickerComponent
  • SecondTimeSpanPickerComponent
  • MillisecondTimeSpanPickerComponent.

Components are created each time the value of TimeSpanPickerElement Format property is changed, based on the desired format (for example: if the format is “dd:hh”, day and hour components will be created when the pop-up is opened).

Custom Components

We’ve provided a way to handle scenarios which require a custom time span as well.

For example, if you want the user to be able to pick one week intervals, you can easily implement it by creating a custom BaseTimeSpanPickerComponent and overriding its GetValueInTicks() and SetValue(TimeSpan value) methods. GetValueInTicks returns a TimeSpan value as ticks corresponding to the selected item from the UI component. SetValue is used to set the value of the component and choose which part of the TimeSpan value we want to use.

public override long GetValueInTicks()
{
    double weeks = this.WeeksListTimeSpanPickerUIComponent.GetSelectedValue();
    return TimeSpan.FromDays(weeks * 7).Ticks;
}
 
public override void SetValue(TimeSpan value)
{
    double weeks = value.Days / 7d;
    this.WeeksListTimeSpanPickerUIComponent.SetSelectedValue(weeks);
}

The BaseTimeSpanPickerComponent has a UI part, which is used to display the values to the end users. By default, we use RadListElement to list all available values. Now let’s add specific text to the values displayed in the list. We will override the CreateItemsSource method, which is used to create our items based on three properties: Minimum, Maximum and Step. In this case, we customize the displayed text and align it to the right. Here is what our custom UI component looks like:

public class WeeksListTimeSpanPickerUIComponent : ListTimeSpanPickerUIComponent
{
    public WeeksListTimeSpanPickerUIComponent(ITimeSpanPickerComponent owner) : base(owner)
    { }
 
    protected override void CreateItemsSource()
    {
        base.CreateItemsSource();
        foreach (RadListDataItem item in this.ListElement.Items)
        {
            item.Text = item.Value + " weeks";
            item.TextAlignment = ContentAlignment.MiddleRight;
        }
    }
 
    public double GetSelectedValue()
    {
        return this.GetValue();
    }
 
    public void SetSelectedValue(double value)
    {
        this.SetValue(value);
    }
}

To include our custom component, we need to override the CreateVisualElement method of our WeekTimeSpanPickerComponent. We will also define our Minimum, Maximum and Step values and set the header text. The whole class looks like this:

public class WeekTimeSpanPickerComponent : BaseTimeSpanPickerComponent
{
    public WeekTimeSpanPickerComponent(ITimeSpanPickerContentElement owner) : base(owner)
    {
        this.Minimum = 0;
        this.Maximum = 10;
        this.Step = 0.5;
        this.UpdateLocalization();
    }
 
    public override BaseTimeSpanPickerUIComponent CreateVisualElement()
    {
        return new WeeksListTimeSpanPickerUIComponent(this);
    }
 
    public WeeksListTimeSpanPickerUIComponent WeeksListTimeSpanPickerUIComponent
    {
        get { return this.TimeSpanPickerUIComponent as WeeksListTimeSpanPickerUIComponent; }
    }
 
    public override long GetValueInTicks()
    {
        double weeks = this.WeeksListTimeSpanPickerUIComponent.GetSelectedValue();
        return TimeSpan.FromDays(weeks * 7).Ticks;
    }
 
    public override void SetValue(TimeSpan value)
    {
        double weeks = value.Days / 7d;
        this.WeeksListTimeSpanPickerUIComponent.SetSelectedValue(weeks);
    }
 
    public override void UpdateLocalization()
    {
        this.TimeSpanPickerUIComponent.HeaderText = "Select Weeks";
    }
}

The last step is to tell our RadTimeSpanPicker to use this newly created component. To achieve this, we need to subscribe to the ComponentsCreated event of PopupContentElement, where we will remove all default generated components and insert our custom one.

this.radTimeSpanPicker1.PopupContentElement.ComponentsCreated += this.PopupContentElement_ComponentsCreated;
 
private void PopupContentElement_ComponentsCreated(object sender, EventArgs e)
{
    ITimeSpanPickerContentElement contentElement = this.radTimeSpanPicker1.PopupContentElement;
    contentElement.Components.Clear();
    contentElement.Components.Add(new WeekTimeSpanPickerComponent(contentElement));
}

time span picker popup weeks

In the end, we will add just a few more lines of code to show how you can add a new button next to the Close button, which will be used to reset the value of RadTimeSpanPicker.

TimeSpanPickerDoneButtonElement buttonPanel = (radTimeSpanPicker1.PopupContentElement as RadTimeSpanPickerContentElement).FooterPanel;
 
RadButtonElement clearButton = new RadButtonElement("Clear");
clearButton.Click += ClearButton_Click;
buttonPanel.LayoutPanel.Children.Add(clearButton);

And a code sample that shows how to access the panel with buttons and clear the value of RadTimeSpanPicker:

private void ClearButton_Click(object sender, EventArgs e)
{
    RadButtonElement button = (RadButtonElement)sender;
    TimeSpanPickerDoneButtonElement buttonPanel = button.FindAncestor<TimeSpanPickerDoneButtonElement>();
    buttonPanel.Owner.SetValueAndClose(null);
}

time span picker popup clear button

Try It Out and Share Your Feedback

For more details about RadTimeSpanPicker, you can visit the online documentation and take a look at the demos. For any further feature or control requests, make sure to log them into the WinForms Feedback portal.

If you in search for a comprehensive WinForms UI toolkit for your current or upcoming desktop project and have not yet had the chance to explore Telerik UI for WinForms, you can find detailed information on the product page. You can also download a free 30-day trial to explore it in-depth and evaluate whether it will be a right fit for your application.


TodorPic
About the Author

Todor Vyagov

Todor Vyagov is a Software Developer on the WinForms team at Progress.

Related Posts

Comments

Comments are disabled in preview mode.