I am writing a medication administration app. The main table name is ResidentMedications. The second table name is Resident24HourTable. My main form is ResidentMedicationsExtended, with a subform Resident24HourPeriod. Resident24HourPeriod contains 24 check boxes, one for each hour in the day. (12A, 1A, 2A, 3A etc).
Within the ResidentMedicationsExtended form is a combo box [Frequency]. For simplicity sake, let's say "Q4H" is selected, for "4 times per day". Another field [First Dose] allows the user to enter the time the resident will receive their first daily dose. We'll say 6:00 AM.
I have a command button that I want to check the appropriate times in the subform based on the Frequency and First Dose fields. So far, I have written the following:
Private Sub Command90_Click()
If Me.FirstDose = "12:00:00 AM" Then
Forms!ResidentMedicationsExtended.Form.Resident24HourTable![12A] = True
ElseIf Me.FirstDose = "1:00:00 AM" Then
Forms!ResidentMedicationsExtended.Form.Resident24HourTable![1A] = True
ElseIf Me.FirstDose = "2:00:00 AM" Then
Forms!ResidentMedicationsExtended.Form.Resident24HourTable![2A] = True
ElseIf Me.FirstDose = "3:00:00 AM" Then
Forms!ResidentMedicationsExtended.Form.Resident24HourTable![3A] = True
and so on and so forth - many many lines. I know I can write separate lines for each time, write more to make checkboxes False, etc. Obviously, doing it this way will result in a zillion or so lines of code, and I have yet to calculate the rest of the days medication times.
Is there a Loop or something I can use instead? I've read loads of posts and haven't been able to come up with a solution.
I thank you in advance for your time!
4 times in a day means [once every 6 hours]. In your dropdown boxes, you should display the text description, but for the key, use values that make it easier to do the math. Use the period between occurrances, in terms of hours. Then it will be easier to use a loop to run through the occurrances (until you have covered a 24-hour period).
Another alternative would be to use date calculation functions like CDate(), DateAdd() and DatePart() to calculate time values.
So, for your example: 4 times a day. Your list value would be 6 (6 hour intervals)
Private Sub Command30_Click()
Dim totalHours as integer
Dim interval as integer
interval = *** the value from your interval dropdown, eg 6 (hours) ***
Dim nextDose as integer
nextDose = Me.FirstDose ' text was 12 am, but stored in 24 hour format as 0
While totalHours < 24
Forms!ResidentMedicationsExtended.Form.Resident24HourTable![nextDose] = True
nextDose = nextDose + interval
totalHours = totalHours + interval
Wend 'or End While
Use Hour() and TimeValue() to convert "1:00:00 AM" to 1, etc., then use this number to identify the Control:
Dim hrFirst As Integer
hrFirst = Hour(TimeValue(Me.FirstDose)) 'returns 0 for 12am, 1 for 1am
hrFirst = IIF(hrFirst = 0, 12, hrFirst)
Forms!ResidentMedicationsExtended.Form.Resident24HourTable.Controls(hrFirst & "A") = True
Related
This is my first time asking in Stack Overflow :)
I have a program that lets the user pick a date in a combobox and display it on the label. The other parts of the code for that is fine but I cant make exception for months with special days (like february which only has 29 days in leap years).
I tried using the If/Else statement so that when the users click the years 2016,2012,2008 and 2004 and february it will display on the combobox named "day" 1 to 29 and if the users click february but not the specified dates it will display 1 to 28:
If month.SelectedItem = "Feb" And year.SelectedItem = "2016" Or "2012" Or "2008" Or "2004" Then
Dim jkl As Integer
For jkl = 1 To 29
day.Items.Add(jkl)
Next
ElseIf month.SelectedItem = "Feb" Then
Dim poi As Integer
For poi = 1 To 28
day.Items.Add("poi")
Next
End If
But unfortunately when i debug it, when i select dates other than the specified in the first If statement the combo box named day only displays 29 instead of 28. I tried changing the order of the conditions, changing the separator of the numbers to "&" but it is still the same.
I hope someone gets to the bottom of this. I feel like its in my structure that is wrong but even if I change the order everytime and it is still displaying the same bug.
I tried searching here but I cant find one like my condition even remotely similar.
Turn Option Strict On, it will help you detect these errors right away. VB does not handle If statement like this, what you wrote doesn't do what you think it does. You need to specify both side every time.
If month.SelectedItem = "Feb" And (year.SelectedItem = "2016" Or year.SelectedItem = "2012" Or year.SelectedItem = "2008" Or year.SelectedItem = "2004") Then
As for your logic, there are formula to detect leap day. It might be better to use it instead.
Lastly, near the end, you add the string and not the variable value.
day.Items.Add(poi)
I think you should use here the DateTime functions:
DateTime.DaysInMonth(year, month)
Docs
Dim dateString As String = String.Format("{0} 1 2001", month.SelectedItem)
Dim dDate As Date = DateTime.Parse(dateString)
Dim numberOfMonth As Integer = DateAndTime.Month(dDate)
For jkl = 1 To DateTime.DaysInMonth(numberOfMonth, year.SelectedItem)
day.Items.Add(jkl)
Next
Personally, i would have made a nested If
if month.SelectedItem = "Feb" then
if year.SelectedItem = "2016" Or "2012" Or "2008" Or "2004" Then
Dim jkl As Integer
For jkl = 1 To 29
day.Items.Add(jkl)
Next
else
Dim poi As Integer
For poi = 1 To 28
day.Items.Add("poi")
Next
end if
So I have a procedure that I thought was working last time I checked it but apparently it is not. The problem I am having is with 24:00 and the hour check. Yes a classic problem apparently. When entering 24:00 and I use =hour(A1) where A1 has 24:00 in it, I get the answer of 0, and that part I am ok with. I even have some other code that says if timehour = 0 then timehour = 24 and I carry on from there and all is peachy.
Today when I went to enter my 24:00 and used the hour function in VBA it returned run-time error '13': Type mismatch instead of 0.
SET UP
Cell E4 and E5 in excel has validated data list for the user to pick from in a drop down that ranges from 01:00, 02:00, 03:00, ..., 24:00. All formatted as text. If the user has selected 12 hour clock in set up then the times are listed 1:00 AM, 2:00 AM, 3:00 AM, ..., 11:00 PM, 12:00 AM.
CODE
Private Sub Set_Work_Hours()
Dim setStartHour As Integer
Dim setEndHour As Integer
Dim sht As Worksheet
'setting the worksheet to be working with
Set sht = ThisWorkbook.Worksheets("Daily")
sht.Activate
'hardcode location of start and end times from daily sheet
setStartHour = Hour(Cells(4, 5).Value)
setEndHour = Hour(Cells(5, 5).Value)
Call Display_Work_Hours(setStartHour, setEndHour)
End Sub
I get the error at setStartHour or setEndHour when I select 24:00 from the list.
In another cell in the spreadsheet, I had the formula =HOUR(E4) and =HOUR(E5) and they both return 0 when 24:00 is selected.
Why is the VBA version of HOUR not returning zero?
Clarification on the question.
Why am I getting two different results from excel formula to VBA function?
My work around
Private Sub Set_Work_Hours()
Dim setStartHour As Integer
Dim setEndHour As Integer
Dim sht As Worksheet
'setting the worksheet to be working with
Set sht = ThisWorkbook.Worksheets("Daily")
sht.Activate
'hardcode location of start and end times from daily sheet
If Cells(4, 5).Value = "24:00" Then
setStartHour = 0
Else
setStartHour = Hour(Cells(4, 5).Value)
End If
If Cells(5, 5).Value = "24:00" Then
setEndHour = 0
Else
setEndHour = Hour(Cells(5, 5).Value)
End If
Call Display_Work_Hours(setStartHour, setEndHour)
End Sub
Just expanding upon my comments. Excel stores dates and times as a serial number. The integer portion of the serial number is the number of days since 1/1/1900, and the decimal portion is the hours.
When you use hours as a string (e.g. "24:00"), Excel converts it to a serial number, in this case 1.0, or 1/1/1900 0:00. VBA does not implicitly convert invalid times like this, which is why you were seeing an error. The correct way to represent "24:00" in VBA is "00:00".
As a general suggestion, why don't you just run all of your DateTime variables in UNIX time on the backend, and just simply convert to a user-readable date on the spreadsheet?
This way no errors!!
Convert from human readable date to epoch
DateDiff("s", "01/01/1970 00:00:00", time field)
How to get the current epoch time
DateDiff("s", "01/01/1970 00:00:00", Now())
I have programmed a form in Visual Basic which displays the current system time (hh:mm:ss) in a label called "DigitalTime".
Now for the hard part: How do I program a second label ("WordTime") to use the current time (from "DigitalTime") and show it in the "WordTime" label in words like this:
Example 1: (time is 22:50) I want label "WordTime" to show "10 minutes to 11".
Example 2: (time is 13:15) I want label "WordTime" to show "15 minutes past 1".
For the minutes 0-30 I want it to display "... minutes past ...". For the minutes 31-59 I want it to display "... minutes to ...".
It turns out that it isn't that hard thanks to the wonderful ToString formatters available for DateTime in .NET and using String.Format. The DateTime structure in general has all you need to know on this stuff. One caveat and a gotcha, to subtract time from a current DateTime we Add negative amounts of time. There is no SubtractMinutes method on DateTime, only AddMinutes, so you add negative time.
With all that said something like this below. Ideally you'd just make it a function, but I left it fairly basic so it wouldn't get confusing.
10 minutes to 11:
Dim _textToMinute As String = String.Empty
If DateTime.Now().Minute > 30 Then
_TextToMinute = "Past"
Else
_textToMinute = "To"
End If
Dim _minutesTillNextHour = (DateTime.Now().AddHours(1).AddMinutes(-DateTime.Now().Minute) - dateTime.Now).Minutes
Dim _nextHour = DateTime.Now().AddHours(1).ToString("%h")
label1.Text = String.Format("{0} minutes {1} {2}", _minutesTillNextHour, _textToMinute, _nextHour)
15 minutes past 1
label1.Text = String.Format("{0} minutes {1} {2}", DateTime.Now().Minute, _textToMinute, DateTime.Now().ToString("%h"))
When the minutes is 30,it is common practice to use the word "half", as in "half past 6". Here's a simple little function that takes that into account and returns a formatted string that can be assigned wherever you need it:
Function TimeInCommonLang(ByVal input As DateTime) As String
Dim minutes As String = ""
Dim indicator As String = ""
If input.Minute <= 30 Then
indicator = "past"
If input.Minute = 30 Then
minutes = "half"
Else
minutes = input.Minute.ToString & " minutes"
End If
Else
indicator = "to"
minutes = (60 - input.Minute).ToString & " minutes"
input = input.AddHours(1)
End If
Return String.Format("{0} {1} {2}", minutes, indicator, input.ToString("%h"))
End Function
I'm showing a Chart to the user which has one chart area with a line chart. On this, I got, for example, one line. This line has about 200 values. Those values do all have a description (e.g. "01.01.2013", "05.02.2013" and so on).
When the Chart is shown, I can only see two descriptions, even if there would be space for much more descriptions. The line gets displayed correctly, but there are only two points described.
I rotated the Text vertically so there is more space, but this didn't help. If I display less values (5 or 10), the descriptions get shown correctly.
This is how it actually looks like (the descriptions are actually Strings, not Dates).
Thank you for your help!
EDIT: My Code:
chart.ChartAreas(0).AxisY.Maximum = 6
chart.ChartAreas(0).AxisY.Minimum = 1
chart.ChartAreas(0).AxisX.LabelStyle.Angle = -90
chart.Series.Clear()
chart.ChartAreas(0).AxisY.StripLines.Clear()
Dim myStripLine1 as new StripLine()
myStripLine1.IntervalOffset = 4
chart.ChartAreas(0).AxisY.StripLines.add(myStripLine1)
'now adding all series
chart.Series.Add("Chemie") 'just to take the example in the image above
chart.Series(chart.Series.Count - 1).ChartType = DataVisualization.Charting.SeriesChartType.Line
chart.Series(chart.Series.Count - 1).BorderWidth = 4
'now adding quite much values (on every date, every Serie has a value)
chart.Series(chart.Series.Count - 1).Points.AddXY("01.03.2011", 4.9)
On every date, a new point gets entered for all series, but only those points where they have important values get highlighted. Those values between are calculated mathematically.
One example to explain this: I got two series, one has two values (6 and 4) on point "01.01.2013" and "03.01.2013". The other series has 3 values (4,6,5.5) on "01.01.2013","02.01.2013" and "03.01.2013". When I just display them, the first series will end at the second date, even if there was a value for the third date. I solved this by filling a dummy value at the first series with date "02.01.2013" which is just the average at this point (=5). This point simply does not get highlighted with a marker bullet. This is how I draw my graph.
EDIT2:
After Skippy's answer and comment, my new trial. The variable MainForm.grades is a Dictionary(Of Integer,Dictionary(Of String, String)) which contains around 150 grades
Dim subjects As New Dictionary(Of Integer, ArrayList)
Dim allgrades As New ArrayList
For Each grade In MainForm.grades
Dim cD As New Dictionary(Of String, String)
cD.Add("SUBJECTID", grade.Value("SUBJECTID"))
cD.Add("GRADE", grade.Value("GRADE"))
cD.Add("DATE", grade.Value("DATE"))
allgrades.Add(cD)
Next
cht_main.ChartAreas(0).AxisX.IntervalType = DateTimeIntervalType.Days
cht_main.ChartAreas(0).AxisX.LabelStyle.Angle = -90
Dim gradesDateSorter = New gradesDateSorter()
allgrades.Sort(gradesDateSorter)
For Each grade In allgrades
If Not subjects.ContainsKey(Integer.Parse(grade("SUBJECTID"))) Then
subjects.Add(Integer.Parse(grade("SUBJECTID")), New ArrayList)
End If
Dim gradeDict As New Dictionary(Of String, String)
gradeDict.Add("DATE", grade("DATE"))
gradeDict.Add("GRADE", grade("GRADE"))
subjects(Integer.Parse(grade("SUBJECTID"))).Add(gradeDict)
Next
For Each subject In subjects
'adding serie
cht_main.Series.Add(MainForm.subjects(subject.Key)("NAME"))
cht_main.Series(cht_main.Series.Count - 1).ChartType = DataVisualization.Charting.SeriesChartType.Line
cht_main.Series(cht_main.Series.Count - 1).BorderWidth = 4
'cht_main.Series(cht_main.Series.Count - 1).IsXValueIndexed = True
For Each grade In subject.Value
cht_main.Series(cht_main.Series.Count - 1).Points.AddXY(Date.Parse(grade("DATE")), Double.Parse(grade("GRADE")))
Next
Next
On the 5th last row I commented IsXValueIndexed=True because when I activated it, the chart gets generated with a big red error cross.
SOLUTION
Setting the Interval on the X-Axis does the trick!
chart.ChartAreas(0).AxisX.Interval = 1
Solution by Skippy
Yes I agree with Michael. I can only add to the explanation at this point.
By setting your interval:
myStripLine1.IntervalOffset = 4
You are guaranteeing that your X-axis values will be plotted only, at frequency of 4 " generic x-axis" values:
Setting this to vale to 1 will give a value for every x-axis value, that is incremented as a whole number (in this case days)
chart.ChartAreas(0).AxisX.Interval = 1
And to declare the x-axis values to type:
DateTimeIntervalType.Days
'Declaration
Public Sub Add( _
labelsStep As Double, _
intervalType As DateTimeIntervalType, _
format As String _
)
End Sub
chart.ChartAreas(0).AxisX.IntervalType = DateTimeIntervalType.Days
'which as shown in Michael's answer is parsed to string.
Dim format as String = "MM.dd.yyyy"
Dim actualDate as Date = Date.ParseExact(yourDate, format)
As mentioned by Michael in his comment.
By setting the
mySeries.XValueIndexed = True
Every indexed X-axis value will be plotted.
As explained in the following quote, with the link provided.
Each data point in a series has X and Y values that determine its position in the plotting area. With some charts, the X value of points is not important, and does not have to be provided. In this case, the point positions in the plotting area are determined only by their point index (i.e. their location in the Points collection) and their Y values.
When X values are "indexed", the data point index, not a point's X value, is used to determine the position of points along the categorical (X) axis. For example in Figure 1 below, two charts are shown displaying the same data. However, the first chart uses non-indexed X values, therefore the X values of those points determine their location along the x-axis. The second chart is indexed, therefore its point indices are used to determine their position along the x-axis. In this case the X values are only used for the axis labels, and nothing more.
http://support2.dundas.com/onlinedocumentation/winchart2005/Data_IndexedXValues.html
I sourced my original information regarding intervals and interval offsets at the following site:
http://support2.dundas.com/Default.aspx?article=705
Here it discusses datatype and addresses your issue of highlighted values.
On every date, a new point gets entered for all series, but only those points where they have important values get highlighted
For example, assume you wish to create a re-occurring StripLine to highlight weekends. You set the interval to 7 and its type to Days. Since the first point is Sunday you set the IntervalOffset to 6 (to mean the 6th day of the week) and its type to Days. The resulting chart does not show the first StripLine.
This is a an explanation for setting the interval.
A good rule of thumb to follow when using the Interval and IntervalOffset properties of the Chart is that the IntervalOffset should be a lower interval magnitude than the Interval (ie. Interval Days / IntervalOffset Hours, Interval Years / IntervalOffset Months, etc.).
I have added these sources:
For your reference
To show I have, also, done my research after ascertaining the problem, as stated in my comments above.
Florian, can you pls show the code for the labels, properties etc of the x-axis? – yvytty yesterday
Did you ever consider 3rd party plotting components, such as ZedGraph ? Most likely such little caveats are already covered there. Give it a shot! – Neolisk yesterday
In response to ZedGraph I advised:
And: After viewing your code
Hi can I clarify, you WANT to plot values daily? I think I have your solution, just need clarification, you have all the tools within vb.net
#yvytty, nope, the dates do not have to be daily, there can also be no value for a long time and I don't want a big span in my chart where no data is. Actually, I could also write some sample text at the X axis values, the dates are only confusing. The main problem is that the VB chart somehow calculates a very big margin on those descriptions at the X axis
It doesn't show that you have formatted your date and date string. There also needs to be taken into account, that you are not using the en-US date format (I'm in Australia, so we have the same format as you). The default date type is for en-US.
Please refer to DateTime.ParseExact Method
http://msdn.microsoft.com/en-us/library/system.datetime.parseexact.aspx
I have taken snippets from MSDN.
Dim dateString, format As String
Dim result As Date
Dim provider As CultureInfo = CultureInfo.InvariantCulture
Parse date and time with custom specifier.
dateString = "Sun 15 Jun 2008 8:30 AM -06:00"
format = "ddd dd MMM yyyy h:mm tt zzz"
result = Date.ParseExact(dateString, format, provider)
See link:
http://msdn.microsoft.com/en-us/library/w2sa9yss.aspx
The DateTime.ToString(IFormatProvider) method returns the string representation of a date and time value using the short date and long time pattern of a specific culture. The following example uses the DateTime.ToString(IFormatProvider) method to display the date and time using the short date and long time pattern for the fr-FR culture.
Dim date1 As Date = #3/1/2008 7:00AM#
Console.WriteLine(date1.ToString(System.Globalization.CultureInfo.CreateSpecificCulture("fr-FR")))
' Displays 01/03/2008 07:00:00
Please see this link:
http://msdn.microsoft.com/en-us/library/system.datetime.aspx
So it should go, something like this:
'note
Imports System.Globalization
Dim format as String = "dd.MM.yyyy"
Dim actualDate as Date = Date.ParseExact(yourDate, format, provider)
chart.ChartAreas(0).AxisX.LabelStyle.Format ="dd.MM.yyyy"
cht_main.ChartAreas(0).AxisX.IntervalType = DateTimeIntervalType.Days
cht_main.ChartAreas(0).AxisX.Interval = 1
ALSO:
Double.Parse(grade("GRADE")
'grade is not of type double
I think you should convert the string date representation to an actual datetime object before adding it to the chart.
I didn't test it but something like this: (where yourDate is the string you used to pass to the chart)
Dim format as String = "MM.dd.yyyy"
Dim actualDate as Date = Date.ParseExact(yourDate, format)
chart.Series(chart.Series.Count - 1).Points.AddXY(actualDate, 4.9)
The chart can manage datetime object instead of strings and it has special code that deals with dates. If you do this you you can adjust how it is displayed by formatting:
chart.ChartAreas(0).AxisX.LabelStyle.Format ="MM.dd.yyyy"
chart.ChartAreas(0).AxisX.Interval = 1
chart.ChartAreas(0).AxisX.IntervalType = DateTimeIntervalType.Days
If you wanted to only display every other day change the interval to 2
I have 3 textboxes (day, month, year) and I want to check if input is e.g. day has to be from 1 to 31 and so on.
My code is:
If InputDan.Text < "1" Or InputDan > "31" Then Warning.Text = "Not a valid day input." Else Warning.Text = ""
Also I have day and month input limited to 2 characters and year to 4.
It works fine with numbers from 10 to 31 and it properly puts an warning message when input is 0 or 32 and on.
Here's the problem...
When I put in numbers from 4 to 9 it puts on a warning message, as I figured out later that program considers empty space after one character input as 0.
So if I enter 4 the program will read it as 40, and so on.
Can I solve this problem with converting String input as Int somehow?
You need to parse the numbers to integer before you can compare them, otherwise >"11" will compare them alphabetically and not by their numerical order.
Dim day As Integer
Dim valid As Boolean = Int32.TryParse(InputDan.Text, day)
Now you know if that input was a correct number and you could show a warning if it was not.
I would suggest a different approach to check whether or not the input was a correct day since you must take the number of days in that month into account(also leap years, different calendars etc). So use the current culture's calendar and look if the number of days is correct for the given month in this way:
Dim daysInMonth = CultureInfo.CurrentCulture.Calendar.GetDaysInMonth(year, month)
If day > daysInMonth OrElse day < 1 Then
' show warning '
End If
(assuming you have already checked the year and month part with Int32.TryParse)
Better than doing this from the code behind, asp.net has already validations here is an example of a textbox that represents the day, and it has to be between 1 and 31:
<asp:TextBox ID="TextBox2" runat="server"></asp:TextBox>
<asp:RangeValidator ID="RangeValidator1" runat="server"
ErrorMessage="You have to insert a valid day" ControlToValidate="TextBox2" MaximumValue="31" MinimumValue="1"></asp:RangeValidator>
check it out
Your problem is, that "9" (the string) IS "larger" than "31". Because sorting is done on the first char, then the second and so on.
Dim Value As Integer
' is there an (integer) NUMBER in the textbox?
If Integer.TryParse(InputDan.Text, Value) Then
If Value > 0 AndAlso Value < 31 Then
' do something
Else
MessageBox.Show("please enter a number!")
End If
Else
MessageBox.Show("please enter a number!")
End If
With "TryParse" you can test if a String can be converted to an Integer (or Double, Single, whatever implements a TryParse method) and if it can be converted, the value is stored in the second parameter.
You should use Strict on" to avoid the coding problems - basically you are comparing strings against each other. They do NOT behave like Integers for comparisons.
Try like this: (Assuming framework above/or 3.5)
If Not IsNumeric(InputDan.Text) OrElse _
Not Enumerable.Range(1, 31).Contains(CInt(InputDan.Text)) Then
Warning.Text = "Not a valid day input."
Else
Warning.Text = ""
End If
It will first validate the input must be a number and then will validate if it lies within range of 1 and 31. I assume Days can not be 1.5 so I called CInt.
OrElse is what we call ShortCircuit. The second condition will not evaluate if the first one failed.