Written by WATYF on Friday, 23 January 2009 (15658 hits)
Category: .NET Programming
This should be a quick one. Here's the problem... I've got a DateTimePicker (actually, a very large amount of them) on a form, and I want to modify those DateTimePickers so that when a user highlights the "minute" portion and clicks up or down on the spinner, the minutes go up or down in 15 minute increments (instead of one minute increments). Now, the biggest challenge to this is that the separate controls of the DateTimePicker (up, down buttons, formatted textbox, etc) are not exposed in any way (thanks Microsoft!), so I have no way of figuring out if the user clicked up, or down, or entered the minute manually. Because of this, I can't (at least, I personally haven't figured out how to) create a "perfect" solution to this problem. So I settled for the next best thing... a solution that accounts for 90% of the time and that my users will be happy with...
First off, I'd like to point out that my solution involves simply using the ValueChanged event of the DateTimePicker. One could, of course, go all out and make their own custom DateTimePicker class and override the ValueChanged event and then create an "Increment" Property for the control, so that you could easily set an increment for the DateTimePicker, but my situation doesn't call for all that, and it would require more calculations (for variable increments other than 15), and since I'm in a hurry (and suck at math) I decided to go the "quick and dirty" route.
The main part is contained in the ValueChanged event, and then there's a helper Function (for rounding up). The RoundUp function is most likely not the most efficient way to do this (since, again, I suck at math), but I couldn't find a "Round in such and such an increment" anywhere online, so I had to make do. Anyway... the basic premise of the solution is that if the minute value is a 1, 16, 31, or 46, then they clicked "up", so we increment up to the next 15 minute increment. If the minute value is a 14, 29, 44,or 59, then they clicked "down", so we round down. If it's any other number, then it's something they manually entered, so we simply round the number up to the next 15 minute increment. I'm sure you can see the "flaw" in this imperfect solution. If they manually enter a 1, or 16, or 29 (or whatever), then it will think they clicked up/down, and might not round properly. For example, if it's 5:15, and they click down, then it will round it DOWN to 5:00. But if it's 5:15 and the manually enter 5:14, then you'd think it should round UP, but instead, it will "think" they clicked "down" and therefore round the minute down to 5:00. Anyway... it's not a huge flaw, and 99% of the users will never even notice, so it's perfectly fine for me.
So here's the code:
Private Sub dtpMyTime_ValueChanged(ByVal sender As
Object, ByVal e As System.EventArgs) Handles dtpMyTime.ValueChanged
'If it's already an increment of 15, exit sub
If (sender.Value.Minute = 0) Or (sender.Value.Minute = 15) Or
(sender.Value.Minute = 30) Or (sender.Value.Minute = 45) Then Exit Sub
'If it's a 1, 16, 31, or 46 (i.e. they clicked "up")...
If sender.Value.Minute = 1 Or sender.Value.Minute = 16 Or
sender.Value.Minute = 31 Or sender.Value.Minute = 46 Then
'Add 14 minutes to the value
sender.Value = DateAdd(DateInterval.Minute, 14, sender.Value)
ElseIf sender.Value.Minute = 14 Or sender.Value.Minute = 29 Or
sender.Value.Minute = 44 Or sender.Value.Minute = 59 Then
'Otheriwse, if it's a 14, 29, 44, or 59 (i.e. they clicked
"down"), subtract 14 minutes from the value
' You have to subtract
74 (instead of 14) if it's 59, to take it down one hour
If sender.Value.Minute = 59 Then sender.Value =
DateAdd(DateInterval.Minute, 74, sender.Value) Else
sender.Value =
DateAdd(DateInterval.Minute, 14, sender.Value)
Else 'Otherwise, if it's not one digit away from a 15 minute
interval (i.e. they manually entered a number)
'Round the number up to the next 15 minute increment
sender.Value = New DateTime(sender.Value.Year,
sender.Value.Month, sender.Value.Day, sender.Value.Hour,
RoundUp(sender.Value.Minute), 0)
End If
End
Sub
Private
Function RoundUp(ByVal iMin) As Integer
Dim x As Integer
For i As Integer = 1 To 15
x = iMin + i
If x = 15 Or x = 30 Or x = 45 Or x = 0 Then Return x
Next
End Function
WATYF
