B4ndt
B4ndt

Reputation: 586

How to contol the time interval in a DateTimePicker

I have a DateTimePicker control on a form specified like so:

dtpEntry.Format = DateTimePickerFormat.Custom;
dtpEntry.CustomFormat = "dd/MM/yyyy hh:mm:ss";
dtpEntry.ShowUpDown = true;

I would like the user to only be able to increment or decrement the time by 5 minute increments.

Any suggestions on how one would accomplish this?

Upvotes: 7

Views: 16552

Answers (8)

EvanBasalik
EvanBasalik

Reputation: 11

By combining the math logic above with an override class I found at Getting the previous value of an object at ValueChanged event, you can do this in a generic fashion rather than having to precreate a variable for every DateTimePicker on the form.

Here's the override class:

internal class LastDateTimePicker : DateTimePicker
{
    protected override void OnValueChanged(EventArgs eventargs)
    {
        base.OnValueChanged(eventargs);

        LastValue = Value;
    }

    public DateTime LastValue { get; private set; }

    public new DateTime Value
    {
        get { return base.Value; }
        set
        {
            base.Value = value;
        }
    }
}

And then here is the updated calculation method where I also generalized the increment:

private bool navigatingDateTimePicker = false;
private int incrementMinutes = 15;

private void DateTimePicker_ValueChanged(object sender, EventArgs e)
{
    LastDateTimePicker picker = sender as LastDateTimePicker;

    //TODO: make this actually work
    if (picker != null)
    {
        if (!navigatingDateTimePicker)
        {
            /* First set the navigating flag to true so this method doesn't get called again while updating */
            navigatingDateTimePicker = true;

            /* using timespan because that's the only way I know how to round times well */
            TimeSpan tempTS = picker.Value - picker.Value.Date;
            TimeSpan roundedTimeSpan;

            TimeSpan TDBug = picker.Value - picker.LastValue;
            if (TDBug.TotalMinutes == 59)
            {
                // first: if we are going back and skipping an hour it needs an adjustment
                roundedTimeSpan = TimeSpan.FromMinutes(incrementMinutes * Math.Floor((tempTS.TotalMinutes - 60) / incrementMinutes));
                picker.Value = picker.Value.Date + roundedTimeSpan;
            }
            else if (picker.Value > picker.LastValue)
            {
                // round up to the nearest interval
                roundedTimeSpan = TimeSpan.FromMinutes(incrementMinutes * Math.Ceiling(tempTS.TotalMinutes / incrementMinutes));
                picker.Value = picker.Value.Date + roundedTimeSpan;
            }
            else
            {
                // round down to the nearest interval from prev
                roundedTimeSpan = TimeSpan.FromMinutes(incrementMinutes * Math.Floor(tempTS.TotalMinutes / incrementMinutes));
                picker.Value = picker.Value.Date + roundedTimeSpan;
            }
            navigatingDateTimePicker = false;
        }
    }
}

Upvotes: 1

Jason Leisring
Jason Leisring

Reputation: 1

@Hans Passant Just needed to handle 59 for it to work right.

private void dateTimePickerReportScheduleTime_ValueChanged(object sender, EventArgs e)
     {
         if (!mBusy)
         {
             mBusy = true;
             DateTime dt = dateTimePickerReportScheduleTime.Value;
             if ((dt.Minute * 60 + dt.Second) % 300 != 0)
             {
                 TimeSpan diff = dt - mPrevDate;
                 if (dt.Minute == 59) 
                 { 
                     dateTimePickerReportScheduleTime.Value = mPrevDate.AddMinutes(-15); 
                 }
                 else
                 {
                     if (diff.Ticks < 0) dateTimePickerReportScheduleTime.Value = mPrevDate.AddMinutes(-15);
    
                     else dateTimePickerReportScheduleTime.Value = mPrevDate.AddMinutes(15);
                 }
             }
             mBusy = false;
         }
         mPrevDate = dateTimePickerReportScheduleTime.Value;
     }

Upvotes: 0

Otto Knudsen
Otto Knudsen

Reputation: 21

Or simply try this:

private void dateTimePicker1_ValueChanged(object sender, EventArgs e)
{
    if (this.dateTimePicker1.Value.Minute % 5 == 0)
        return;

    if (this.dateTimePicker1.Value.Minute % 5 == 1)
        this.dateTimePicker1.Value = this.dateTimePicker1.Value.AddMinutes(4);

    if (this.dateTimePicker1.Value.Minute % 5 == 4)
        this.dateTimePicker1.Value = this.dateTimePicker1.Value.AddMinutes(-4);
}

Upvotes: 2

ilconfo
ilconfo

Reputation: 81

I've changed a little the answer from SixOThree, to eliminate the bug found by Necromporph. It should be ok like this:

in the class

private DateTime prevTimePicker1;
private bool navigatingDateTimePicker = false;

in the constructor

prevTimePicker1 = dateTimePickerStart.Value;
navigatingDateTimePicker = false;

the event

private void dateTimePickerStart_ValueChanged(object sender, EventArgs e)
{
  if (!navigatingDateTimePicker) {
    /* First set the navigating flag to true so this method doesn't get called again while updating */
    navigatingDateTimePicker = true;

    /* using timespan because that's the only way I know how to round times well */
    TimeSpan tempTS = dateTimePickerStart.Value - dateTimePickerStart.Value.Date;
    TimeSpan roundedTimeSpan;

    TimeSpan TDBug = dateTimePickerStart.Value - prevTimePicker1;
    if (TDBug.TotalMinutes == 59)
    {
        // first: if we are going back and skipping an hour it needs an adjustment
        roundedTimeSpan = TimeSpan.FromMinutes(5 * Math.Floor((tempTS.TotalMinutes - 60) / 5));
        dateTimePickerStart.Value = dateTimePickerStart.Value.Date + roundedTimeSpan;
    }
    else if (dateTimePickerStart.Value > prevTimePicker1)
    {
        // round up to the nearest interval
        roundedTimeSpan = TimeSpan.FromMinutes(5 * Math.Ceiling(tempTS.TotalMinutes / 5));
        dateTimePickerStart.Value = dateTimePickerStart.Value.Date + roundedTimeSpan;
    } else {
        // round down to the nearest interval from prev
        roundedTimeSpan = TimeSpan.FromMinutes(5 * Math.Floor(tempTS.TotalMinutes / 5));
        dateTimePickerStart.Value = dateTimePickerStart.Value.Date + roundedTimeSpan;
    }
    navigatingDateTimePicker = false;
  }
  prevTimePicker1 = dateTimePickerStart.Value;
}

Upvotes: 8

Willy
Willy

Reputation: 1729

you could add this code

int minuteDiff = dtpJamAppointmentDokter.Value.Minute - prevTimePicker1.Minute;

if (minuteDiff == 59)
{
    dtpJamAppointmentDokter.Value =  dtpJamAppointmentDokter.Value.AddHours(-1);
}

before

TimeSpan tempTS = dtpJamAppointmentDokter.Value - dtpJamAppointmentDokter.Value.Date;

Upvotes: 0

SixOThree
SixOThree

Reputation: 771

I know this is an old article, but I created a better solution to this problem based on the answer above.

in the class

private DateTime prevTimePicker1;
private bool navigatingDateTimePicker = false;

in the constructor

prevTimePicker1 = dateTimePickerStart.Value;
navigatingDateTimePicker = false;

the event

private void dateTimePickerStart_ValueChanged(object sender, EventArgs e)
{
  if (!navigatingDateTimePicker) {
    /* First set the navigating flag to true so this method doesn't get called again while updating */
    navigatingDateTimePicker = true;

    /* using timespan because that's the only way I know how to round times well */
    TimeSpan tempTS = dateTimePickerStart.Value - dateTimePickerStart.Value.Date;
    TimeSpan roundedTimeSpan;

    if (dateTimePickerStart.Value > prevTimePicker1) {
      // round up to the nearest interval
      roundedTimeSpan = TimeSpan.FromMinutes(5 * Math.Ceiling(tempTS.TotalMinutes / 5));
      dateTimePickerStart.Value = dateTimePickerStart.Value.Date + roundedTimeSpan;
    } else {
      // round down to the nearest interval from prev
      roundedTimeSpan = TimeSpan.FromMinutes(5 * Math.Floor(tempTS.TotalMinutes / 5));
      dateTimePickerStart.Value = dateTimePickerStart.Value.Date + roundedTimeSpan;
    }
    navigatingDateTimePicker = false;
  }
  prevTimePicker1 = dateTimePickerStart.Value;
}

Upvotes: 0

Hans Passant
Hans Passant

Reputation: 942538

It's possible by watching the ValueChanged event and override the value. This sample form worked well:

public partial class Form1 : Form {
    public Form1() {
        InitializeComponent();
        dateTimePicker1.CustomFormat = "dd/MM/yyyy hh:mm";
        dateTimePicker1.Format = DateTimePickerFormat.Custom;
        dateTimePicker1.ShowUpDown = true;
        dateTimePicker1.Value = DateTime.Now.Date.AddHours(DateTime.Now.Hour);
        mPrevDate = dateTimePicker1.Value;
        dateTimePicker1.ValueChanged += new EventHandler(dateTimePicker1_ValueChanged);
    }
    private DateTime mPrevDate;
    private bool mBusy;

    private void dateTimePicker1_ValueChanged(object sender, EventArgs e) {
        if (!mBusy) {
            mBusy = true;
            DateTime dt = dateTimePicker1.Value;
            if ((dt.Minute * 60 + dt.Second) % 300 != 0) {
                TimeSpan diff = dt - mPrevDate;
                if (diff.Ticks < 0) dateTimePicker1.Value = mPrevDate.AddMinutes(-5);
                else dateTimePicker1.Value = mPrevDate.AddMinutes(5);
            }
            mBusy = false;
        }
        mPrevDate = dateTimePicker1.Value;
    }
}

Upvotes: 11

ljs
ljs

Reputation: 37847

The problem is that the up/down control automatically increments or decrements the currently highlighted portion of the date/time picker (i.e. year/month/day/hour/etc.).

You are probably better off adding your own up/down control (perhaps a very small vscrollbar) immediately adjacent to the date/time picker and wiring it up to increment/decrement five minute intervals from the date/time picker's value.

Upvotes: 1

Related Questions