VB.NET : Populating Gridview with Dates (30 days interval) - vb.net

I am trying to populate a gridview with dates.
For example the date today is 2/18/2019 and the next date within 30 days is 3/20/2019. so the desired output shoul'd look like this
No. Date
1 3/20/2019
2 4/19/2019
3 5/19/2019
etc.
but the result is
No. Date
1 3/20/2019
2 3/20/2019
3 3/20/2019
etc.
Here is my code so far.
Dim termCounter As Integer = 36
Dim today As DateTime = DateTime.Today
Dim dueDate As DateTime = today.AddDays(30)
Dim dtable As DataTable = New DataTable()
dtable.Columns.Add("No.")
dtable.Columns.Add("Payment Date")
Dim RowValues As Object() = {"", ""}
Dim dRow As DataRow
Dim tmpDate As Date
For i As Integer = 1 To termCounter
If GridAmortSched.RowCount <= 0 Then
RowValues(0) = i
RowValues(1) = dueDate.ToShortDateString
dRow = dtable.Rows.Add(RowValues)
Else
tmpDate = GridAmortSched.Rows(GridAmortSched.RowCount - 1).Cells(1).Value.ToString()
RowValues(0) = i
RowValues(1) = tmpDate.AddDays(30).ToShortDateString
dRow = dtable.Rows.Add(RowValues)
End If
Next
dtable.AcceptChanges()
GridAmortSched.DataSource = dtable

As is often the case, the code is behaving as you have told it to do, not at you want it to do :-).
Every time you go through the loop, you set tmpDate from the same source and then two lines later, you put the value into RowValues. :
tmpDate = GridAmortSched.Rows(GridAmortSched.RowCount - 1).Cells(1).Value.ToString()
You do not adjust your source by i, nor do you increment it. When you AddDays, you do not adjust the original value either (which would not work because of how and when you assign it).
Two easy fixes (two options)
One. The first is to index the days to be added against the loop value - the simple modification to the code below will achieve this. It is a quick fix and may be harder to maintain in the future - more to the point, is harder to spot if you change something such as the loop starting point.
RowValues(1) = tmpDate.AddDays(30*i).ToShortDateString
Two. The other option is to address the code logic. Set your baseline value and then increment it within the loop.
Dim tmpDate As Date
tmpDate = GridAmortSched.Rows(GridAmortSched.RowCount - 1).Cells(1).Value.ToString() '*** Moved outside of loop
For i As Integer = 1 To termCounter
If GridAmortSched.RowCount <= 0 Then
RowValues(0) = i
RowValues(1) = dueDate.ToShortDateString
dRow = dtable.Rows.Add(RowValues)
Else
RowValues(0) = i
tmpDate = tmpDate.AddDays(30) '*** Increment date every time it passes here
RowValues(1) = tmpDate.ToShortDateString '*** Assign the new value
dRow = dtable.Rows.Add(RowValues)
End If
Next
dtable.AcceptChanges()
GridAmortSched.DataSource = dtable

Related

For on a dataset, get column value

I am updating some old software in VB, and fixed this, but I'd like to know how to make the second option work.
The problem is in the second For loop in the code below. I'm not sure how to get a column of a specific row.
I have not worked much with VB and not used it for 10 years or so, so bare the question.
Dim i As Integer = 0
Dim dsSettings As New DataSet
dsSettings.Locale = System.Globalization.CultureInfo.InvariantCulture
If System.IO.File.Exists("QuaData\Settings.xml") Then
dsSettings.ReadXml("QuaData\Settings.xml")
ReDim Preserve IdPrefixes(dsSettings.Tables(0).Rows.Count - 1)
For Each Row As DataRow In dsSettings.Tables(0).Rows
For Each Coll As DataColumn In dsSettings.Tables(0).Columns
IdPrefixes(i) = Row("IdPrefix").ToString()
Next
i = i + 1
Next
' Here I cannot see how I can get a column of a row -
' like Rows(1)
' I cannot select Rows(index)(column name)
For i = 0 To dsSettings.Tables(0).Rows.Count - 1
IdPrefixes(i) = dsSettings.Tables(0).Rows(i)("IdPrefix").ToString()
Next
End If

Why does my stacked column chart print incorrect x axis labels?

A bit of information:
I would like to populate a stacked column chart with transactions from the last 12 months.
Every column should be a month in the year and the transactions are of course stacked on top of each other.
At the moment, I just have a test document where I pull some data from. I first tried to directly put the data (from the csv) in the chart, but that just resulted in multiple columns with the multiple duplicate x axis label and nothing stacked.
I did some digging and found this solution. unfortunately, the result is not what I'm looking for. The transactions are stacked but the x axis labels are incorrect
At the moment I have
Dim rowsTra() As String = File.ReadAllLines(".\data\transactions.csv")
Dim traVal() As String
Dim preYear As DateTime = DateTime.Now.AddYears(-1)
Dim j As Integer = 0
Dim dtTest As DataTable = New DataTable
dtTest.Columns.Add("col1", GetType(Double))
dtTest.Columns.Add("col2", GetType(String))
dtTest.Columns.Add("col3", GetType(String))
For i As Integer = 0 To rowsTra.Length - 1 Step +1 ' Looping through all transactions
traVal = rowsTra(j).ToString().Split(",")
Dim traDate As String = Convert.ToDateTime(traVal(1))
If (traDate >= preYear) Then ' Check if date is not older than 1 year
Dim conMonth As Date = CDate(traVal(1))
Dim month = conMonth.ToString("MMM yyyy")
dtTest.Rows.Add(traVal(6), month, traVal(4))
Else
i = rowsTra.Length - 1 ' Quit loop if year ends (will only work if csv is chronological
End If
j += 1
Next
Dim dv As DataView = New DataView(dtTest)
dv.Sort = "col2 asc"
chTrend.Series.Clear()
chTrend.Titles.Clear() ' Clear
chTrend.DataBindCrossTable(dv, "col3", "col2", "col1", "Label=col1") ' Populate chart
For Each cs As Series In chTrend.Series ' Stack values
cs.ChartType = SeriesChartType.StackedColumn
Next
By using this csv file I get this result:
Account 1,19 Dec 2021,Man 1,Cat 1,Subcat 1,test,5
Account 2,01 Dec 2021,Man 2,Cat 2,Subcat 2,test,10
Account 5,01 Nov 2021,Man 4,Cat 2,Subcat 2,test,10
Account 4,27 Oct 2021,Man 4,Cat 4,Subcat 4,test,20
Account 3,10 Oct 2021,Man 3,Cat 3,Subcat 3,test,15
Account 1,03 Sep 2020,Man 1,Cat 1,Subcat 1,test,25
= col2 =col3 =col1
Why would it in this case add 4 transactions under the "Dec 2021" when only 2 transactions actually are?
I've noticed that when I change the 3rd listed transaction to another Subcat, all transactions fall under Dec 2021.
Ive also tried to just give col2 in dtTest the Date type but this just gave an even weirder chart and I'm not sure how to then format the date to "MMM yyyy". This is my reason to move from datetime to string
I'm not sure what I'm doing wrong.
Thanks for the input. Sorry for the messy code, still learning.
So I did some experimenting myself and I believe I found "A" solution to my problem.
I'm sharing it, since it might be to helpful to someone in the future.
In the end I came up with this solution.
It not only stacks correctly by adding the extra rows needed (as mentioned in my comment), but also adds up values from already existing categories.
This is something that I was aiming for did not mention in my original question.
I'm definitely not saying this is the best solution and I know it can cause some performance issues with scalability. I'd go so far as to say it might even be just spaghetti code so I'd love to see someone with more experience find a more optimal solution to this problem.
Dim rowsTra() As String = File.ReadAllLines(".\transactions.csv")
Dim traVal() As String
Dim strCat As String = ""
Dim strDate As String = ""
Dim preYear As DateTime = DateTime.Now.AddYears(-1) ' Start date for all visible transactions in chart
Dim dtTrend As DataTable = New DataTable
dtTrend.Columns.Add("outflow", GetType(Double)) ' Columns for DataTable
dtTrend.Columns.Add("date", GetType(String))
dtTrend.Columns.Add("category", GetType(String))
Dim valExist As Boolean
Dim l As Integer = 0 'l
Dim m As Integer = 0 'm
For i As Integer = 0 To rowsTra.Length - 1 Step +1 ' Loop all transactions to add to DataTable and list all dates and categories
traVal = rowsTra(l).ToString().Split(",")
Dim traDate As String = Convert.ToDateTime(traVal(1))
Dim traMonth = CDate(traVal(1)).ToString("MMM yyyy") ' Convert to Month Year
If (traDate >= preYear) Then ' Check if date is not older than 1 year
If dtTrend.Rows.Count = 0 Then ' First transaction loop
dtTrend.Rows.Add(traVal(6), traMonth, traVal(4)) ' Add row to DataTable in order "outflow/date/category"
Else ' If not first transaction
m = 0
valExist = False ' Reset valExist to false
While m < dtTrend.Rows.Count ' Loop DataTable, used to add outflow to existing categories
If dtTrend.Rows(m)(1).ToString = traMonth And dtTrend.Rows(m)(2) = traVal(4) Then ' Check if date and category match between DataTable and Transactions
dtTrend.Rows(m)(0) = dtTrend.Rows(m)(0) + traVal(6) ' Add outflow to existing DataTable row
m = dtTrend.Rows.Count
valExist = True
End If
m += 1
End While
If valExist = False Then ' If combo of date and subcategory does not exist:
dtTrend.Rows.Add(traVal(6), traMonth, traVal(4)) ' Add new line to DataTable
End If
End If
If strCat = "" Then ' If string has no value (first loop). Used to create list of subcategories
strCat = traVal(4)
ElseIf strCat.Contains(traVal(4)) Then ' Check if category already exist in the string
Else
strCat = strCat + "," + traVal(4)
End If
If strDate = "" Then ' Ditto about but for Date
strDate = traMonth
ElseIf strDate.Contains(traMonth) Then
Else
strDate = strDate + "," + traMonth
End If
Else
i = rowsTra.Length - 1 ' Quit loop if year ends (will only work if csv is chronological
End If
l += 1
Next
Dim arrCat() As String = strCat.Split(",") ' Split string to use in array
Dim arrDate() As String = strDate.Split(",")
Dim tfMatch As Boolean
For i As Integer = 0 To arrCat.Length - 1 Step +1 ' Loop existing categories, used to add non existing row
For j As Integer = 0 To arrDate.Length - 1 Step +1 ' Loop existing dates, rows need to be added to get full series
Dim dtRows = dtTrend.Rows.Count
For k As Integer = 0 To dtRows - 1 ' Loop through original DataTable values
If dtTrend.Rows(k)(1) = arrDate(j) And dtTrend.Rows(k)(2) = arrCat(i) Then ' Check if date and categorys match anywhere in the DataTable
tfMatch = True
End If
Next
If tfMatch = True Then ' If match, reset and search with next date
tfMatch = False
Else
dtTrend.Rows.Add(0, arrDate(j), arrCat(i)) ' If no match, add as new row in DataTable
tfMatch = False ' Reset, moving to next category
End If
Next
Next
Dim dv As DataView = New DataView(dtTrend)
dv.Sort = "date desc"
chTrend.Series.Clear() ' Clear chart before fill so no exceptions are generated
chTrend.Titles.Clear()
chTrend.DataManipulator.FilterSetEmptyPoints = True ' Filter to get correct stack
chTrend.DataManipulator.FilterMatchedPoints = True
chTrend.DataBindCrossTable(dv, "category", "date", "outflow", "Label=outflow") ' Populate chart
For Each cs As Series In chTrend.Series ' Removes labels of newly added rows with value "0"
chTrend.DataManipulator.Filter(DataVisualization.Charting.CompareMethod.EqualTo, 0, cs) 'Compare if equal to zero, filter out
cs.ChartType = SeriesChartType.StackedColumn ' Chart type
'Dim dpcp As DataPointCustomProperties = New DataPointCustomProperties
Next

How to order Datatable by row value?

I have the below datatable, how can I sort the datatable by Today name, so for example if today is Monday, it should show at the end of the Datatable
I tried the following, but it is not working
Dim dv As DataView = dt3.DefaultView
dv.Sort = " DayLog desc"
Dim sortedDT = dv.ToTable()
Been some time since I've done some VB, surely you could do the following?
Dim data() AS DataRow = dt.Select()
Dim sortedData = data.OrderBy(Function(x) CInt([Enum].Parse(GetType(DayOfWeek), x.Item("DayLog"))) )
Dim sortedDataTable = dt.Clone()
For Each row In sortedData
sortedDataTable.ImportRow(row)
Next
Assuming the spelling of your days of the week is 100% correct
If you have the string value of the current day of the week or you know the int value, then you can adjust the sort using modulus
eg.
Dim todayStringVal = "Tuesday" // say you have this value from somewhere
Dim today = CInt([Enum].Parse(GetType(DayOfWeek), todayStringVal)) // convert to day of the week enum
Dim sortedData = data.OrderBy(Function(x) CInt(([Enum].Parse(GetType(DayOfWeek), x.Item("DayLog")) + 7 - today)) Mod 7) // Apply sorting
Finally if you want the above snippet to sort so that today's value is at the end of the list just change OrderBy to OrderByDescending
EDIT
To ensure today's value appears first in your list even though DayOfWeek's index starts at 0 you need to + 7 and subtract the index then mod by 7 to get the correct index to sort by
eg. Saturday's index on the DayOfWeek enum is 6 therefore 6 + 7 (Days of the week) = 13 then subtract by 6 = 7 finally 7 mod 7 is 0, so Saturday's new index becomes 0 and gets ordered first in the list

Excel VBA / Formula . Fill a range of cells with each Friday of the current month

So the titles a bit of a mess but im trying to do the following:
I have a range of cells from Q8:Q12, T8:T12, Q16:Q20 and T16:T20
Im trying to have these cells populate with the date for each friday of this current month. Essentially, using July (The current month) it would look something like this:
Q8/T8 = 06/07
Q9/T9 = 13/07
Q10/T10 = 20/07
Q11/T11 = 27/07
Q12/T12 = -
The reason Q/T12 would be blank is to handle months of the year that have 5 fridays in them rather than 4. Its kind of a way of error handling.
I have a cell that currently tracks the month within the Cell A9 and the formula looks like this:
=TEXT(NOW(),"mmmm")
Im not quite sure how to handle this logically really. Either VBA or a Formula would do in my eyes.
Ideally, because i have a different sheet for every month July, August etc. The formula above changes depending on what month it is currently. I would need to convert the Formula/VBA script from cells Q8:Q12 OR A9 into a value AFTER populating the date range cells Q8:Q12.
Anyone have any ideas. Im sorry its a bit of a messy question
excel-formula
Put this in the first cell and copy down 5
=IFERROR(AGGREGATE(15,6,ROW(INDEX(A:A,EOMONTH(TODAY(),-1)+1):INDEX(A:A,EOMONTH(TODAY(),0)))/(WEEKDAY(ROW(INDEX(A:A,EOMONTH(TODAY(),-1)+1):INDEX(A:A,EOMONTH(TODAY(),0))),1)=6),ROW(1:1)),"-")
Then format to your specifications.
There are probably more elegant formulas but this is what came to mind.
Here is a macro version without needing a date value in Range("A9")...
Dim SoM As Date
Dim EoM As Date
Dim rw As Long
SoM = DateSerial(Year(Now), Month(Now) + 0, 1)
EoM = DateSerial(Year(Now), Month(Now) + 1, 0)
rw = 8
While SoM < EoM
If Weekday(SoM) = vbFriday Then
Cells(rw, 17).Value = SoM
Cells(rw, 17).NumberFormat = "m/d/yyyy"
rw = rw + 1
End If
SoM = SoM + 1
Wend
I made a user defined function that works with any date range, then show how it could be applied to this example with a few formulas. This would account for year to year transitions.
Function DAYOFWEEKFREQUENCY(ByVal dayOfWeekType As String, ByVal startDate As String, ByVal endDate As String) As Long
Dim myStartDate As Date
myStartDate = CDate(startDate)
Dim myEndDate As Date
myEndDate = CDate(endDate)
Dim includeStartDate As Long
includeStartDate = 1
Dim daysBetweenDatesInclusive As Long
daysBetweenDatesInclusive = Application.WorksheetFunction.Days(endDate, startDate) + includeStartDate
Dim vbStartDay As Long
vbStartDay = Weekday(startDate)
Dim dateCheckedIncremented As Date
dateCheckedIncremented = myStartDate
For dayCounter = 1 To daysBetweenDatesInclusive
If Weekday(dateIncrementedChecked) = dayOfWeekType Then
DAYOFWEEKFREQUENCY = DAYOFWEEKFREQUENCY + 1
End If
dateIncrementedChecked = DateAdd("d", 1, dateIncrementedChecked)
Next
End Function

Get row index if some column value is equal to something

In this datatable there are no duplicates, I need the row index where column x value equals 2. I would do it like this:
Dim rowIndex As Integer = 0
For i = 0 To mtable.Rows.Count - 1
If mtable.Rows(i)("x") = 2 Then
rowIndex = i
Exit For
End If
Next
I will be calling this process multiple times per second. Is there a faster way to do this in .NET?
DataTable select could work, i think it should be faster than iterating over the collection of rows.
var index = mtable.Rows.IndexOf(mtable.Select("x = 2").FirstOrDefault());
Multiple times per second is a bit vague - tens or thousands?
You could create a hash table mapping the value of "x" to the row number:
Dim nLookups = mtable.Rows.Count - 1
Dim lookupHash As New Hashtable(nLookups)
For i = 0 To nLookups
lookupHash.Add(CInt(mtable.Rows(i)("x")), i)
Next
then
Dim rowSought As Integer = -1
If lookupHash.ContainsKey(2) Then
rowSought = lookupHash(2)
End If
Or if the range of possible values of "x" is suitable, you could use an array to map the value to the row number.