Fill a ListBox with Excel files older than x month - excel

I'm making a WinForm project that shows me all the Excel files older than x months of a selected folder.
I can populate a ListBox with all the Excel files (see code below).
In selecting the older than x month, I don't succeed. I think I should work with the LastWriteTime but can’t find how to use it.
Can anyone help me to find a solution on this?
Private Sub ListFiles(ByVal folderPath As String)
filesListBox.Items.Clear()
Dim fileNames As String() =
System.IO.Directory.GetFiles(folderPath,
"*.xl*", System.IO.SearchOption.TopDirectoryOnly)
For Each fileName As String In fileNames
fileslistbox.Items.Add(fileName)
Next
End Sub

The information on the a File Creation Time / Last Write Time is returned by the FileInfo class, which provides the Creation Time, Last Write Time and Last Access Time of a file through a FileSystemInfo class.
See whether you need the File Creation Time or the Last Write Time, depending on what your requirements are.
The Last Access Time may return just the file Creation DateTime, depending on the System.
An example, with a modified ListFiles method:
I've added a parameter to the method, OlderThanMonths, which is used to specify how old a file should be to be included in the list.
Here, the DateTime reference is LastWriteTime.
Private Sub ListFiles(ByVal folderPath As String, OlderThanMonths As Integer)
filesListBox.Items.Clear()
Dim fileNames As String() = Directory.GetFiles(folderPath, "*.xl*", SearchOption.TopDirectoryOnly)
ListBox1.BeginUpdate()
For Each fileName As String In fileNames
Dim FIinfo As New FileInfo(fileName)
If FIinfo.LastWriteTime.AddMonths(OlderThanMonths) <= Date.Now Then
filesListBox.Items.Add(fileName)
End If
Next
ListBox1.EndUpdate()
End Sub
Or with a LINQ Where() filter:
Private Sub ListFiles(ByVal folderPath As String, OlderThanMonths As Integer)
filesListBox.Items.Clear()
filesListBox.Items.AddRange(
Directory.GetFiles(folderPath, "*.xl*", SearchOption.TopDirectoryOnly).
Where(Function(f) New FileInfo(f).LastWriteTime.AddMonths(OlderThanMonths) <= Date.Now).
ToArray())
End Sub

You are almost there. Just need to add an IF clause to check the Modified Date.
Check this code sample:
Private Sub ListFiles(ByVal folderPath As String)
filesListBox.Items.Clear()
Dim fileNames As String() =
System.IO.Directory.GetFiles(folderPath,
"*.xl*", System.IO.SearchOption.TopDirectoryOnly)
For Each fileName As String In fileNames
Dim dtFileModifiedDate As DateTime = IO.File.GetLastWriteTime(fileName)
Dim dtCustomDate As DateTime = DateTime.ParseExact(20180401, "yyyyMMdd", Globalization.CultureInfo.InvariantCulture)
If dtFileModifiedDate > dtCustomDate Then
filesListBox.Items.Add(fileName)
End If
Next
End Sub

Related

Excel vba function with specific part of a path as variable

I have a function in excel that gives hour of last modification using path , only one part of the path changes exp:
\c:\xcl\report\sudtrack\20200324\dossier22
the part is 20200324
i want to do something like that : function('20200324') the code will place it in the path
path="\\c:\xcl\report\sudtrack\" & 20200324 &"\dossier22"
my current code
Function End_hour(path As String)
End_hour = Format(FileDateTime(path), "hh:mm:ss")
End Function
i want to do something like that : function('20200324') the code will place it in the path
No need to use use a function. You can directly do a Replace. Just set up a base string as shown below and do the replace.
Option Explicit
Sub Sample()
Dim myPath As String
myPath = "\\c:\xcl\report\sudtrack\HOUROFLASTMOD\dossier22"
MsgBox Replace(myPath, "HOUROFLASTMOD", "20200324")
End Sub
Note: I have used HOUROFLASTMOD. You can change it to whatever string you want.
If you still want to use a function then try this
Option Explicit
Sub Sample()
MsgBox ReturnNewPath("20200324")
End Sub
Function ReturnNewPath(TimeString As String)
Dim myPath As String
myPath = "\\c:\xcl\report\sudtrack\HOUROFLASTMOD\dossier22"
ReturnNewPath = Replace(myPath, "HOUROFLASTMOD", TimeString)
End Function

VBA Index Match function

I am new to vba and totally lost in writing the above mentioned function in vba. Actually, I want to do the same thing I would do with the usual excel formula.
Update:
Based on the answer of Scott I have adjusted my code. Now I have a Type mismatch error. The Type of Mname by definition is a string. The values in the lookup Range (B18:B38) are (not exclusive) integer. Should I tell Excel to take them as a string? If yes, how?
Summary:
I have a range (D18:D38) where I want to chose a value from based on the row number determined by a match between a string Variable (Mname) and another range (B18:B38). The string Variable is determined by the name of a file in a folder.
My Problem is that I get the error message: 'Unable to get the match property of the worksheetfunction class'
Thanks for your help!
Sub Test()
Application.ScreenUpdating = False
Dim Mname As String
Set WSCockpit = ThisWorkbook.ActiveSheet
Dim strFileName As String
Dim strFolder As String: strFolder = WSCockpit.Range("D9").Value
Dim strFileSpec As String: strFileSpec = strFolder & "*.xls*"
strFileName = Dir(strFileSpec)
Do While Len(strFileName) > 0
Dim strFilePath As String: strFilePath = strFolder & strFileName
Mname = Mid(strFileName, 13, Len(strFileName) - 17)
Dim rw As Long
rw = Application.Match(Mname, WSCockpit.Range("B18:B38"), 0)
Dim VarImp As Boolean
VarImp = Application.WorksheetFunction.Index(WSCockpit.Range("D18:D38"), rw)
'some other task'
Loop
Application.DisplayAlerts = True
End Sub
Backup:
Sorry for the code being messy. I have no clue about the general rules for writing vba. Like mentioned before, my goal is to get the lookup running. The looked up value will be "TRUE" or "FALSE". Afterwards I will use this in order to determine whether the file found in the folder needs to be imported or not. If you have some other suggestions for my coding or for the task I want to perform, I would be glad to hear.
Break it into steps and also ensure the data you expect to find in the lookup is in indeed there. Meaning, check the value of Mname at run-time (via debugging code) and verify you can find it manually.
Lastly, use Application.Match instead.
Dim rw as Long
rw = Application.Match(Mname, WSCockpit.Range("B18:B38"),0)
Dim import as Boolean
import = Application.WorksheetFunction.Index(WSCockpit.Range("D18:D38"),rw)

Excel VBA reference issues

I was following some examples online in which im trying to pass a value from one sub into a another sub in VBA but get the error:
Compile Error:
Procedure declaration does not match description of event or procedure having the same name.
Sub next_sat_click()
Dim iweekday As Integer
Dim nextsat As String
Dim index As Integer
Dim str_game_date As String
iweekday = weekday(Now(), vbSunday)
nextsat = Format((Now + 7 - iweekday), "mm-dd-yy")
Call set_button_Click(nextsat)
End Sub
Sub set_button_Click(ByRef nextsat As String)
......
End Sub
Change the sub name in something else like SetButtonOnClick.
The _Click keyword is reserved by excel for the Click event on buttons if you have a button called with the same name.
You can't change the parameters for an event handler (except for the parameter name). That also means you can't add any parameters if none are expected. Not even Optional ByRef nextsat As String will work.
There are three ways to pass a value between event handlers in a UserForm:
Using a global variable (not recommended, ever);
Via the UserForm.tag property (recommended for simple values such as strings). Obviously cannot be used if it already has a permanent use;
Via one or more hidden controls (recommended for multiple or complex values as well as simple ones).
I've used the second method:
Sub next_sat_click()
Dim iweekday As Integer
Dim nextsat As String
Dim index As Integer
Dim str_game_date As String
iweekday = Weekday(Now(), vbSunday)
nextsat = Format((Now + 7 - iweekday), "mm-dd-yy")
Me.Tag = nextsat
End Sub
Sub set_button_Click()
Dim nextsat As String
nextsat = Me.Tag
......
End Sub
A better solution in your case might be to have a visible TextBox in which you store the calculated date when the user clicks next_sat, so that the user can see it. Then in your set_button handler, grab it from TextBox.Text.

CSV to XLSX VB.NET

enter image description hereenter image description hereI am just learning VB.NET and unfortunately I have been tasked with something I do not have a clue how to do.
I need to create a quick windows based application to export csv files into an XLSX file.
Yes, I know that other posts may have a similar topic however this one I believe is unique.
The CSV file will have 5 headers, `Line, Component, Picked, Placed and Missed. We have part numbers in column 2 that would be placed under Component. I am understanding from the powers that be, this file sums the total part numbers i.e. 0-5490045 and the line JUKI 3 and totals Picked, Placed and Missed parts. I have provided a sample rows below. First row is the csv formatted, the second is the output. I am not sure which loop would be best a FOR loop, WHILE loop etc. I am assuming I will need a loop of some sort to get through all the data in the csv file.
The only code I have opens the dialog box and allows for file selection and attempts to read into a datatable. I am attempting to get this working and then restructure some code.
Imports Spire.Xls
Imports System.Windows.Forms
Imports System.Data
Public Class Form1
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim dialog As OpenFileDialog = New OpenFileDialog
dialog.Filter="CSV document(*.csv)|*.csv"
Dim result As DialogResult = dialog.ShowDialog
If(result=DialogResult.OK) Then
Dim csvFile As String = dialog.FileName
Dim workbook As Workbook = New Workbook
workbook.LoadFromFile(csvFile,",")
Dim worksheet As Worksheet = workbook.Worksheets(0)
Dim dt As DataTable=worksheet.ExportDataTable
Me.dataGridView1.DataSource=dt
End If
End Sub
End Class
JUKI 3 0-5490045 96 96 3
Line Component Picked Placed Missed
JUKI 3 0-5490045 99 96 3
I hate to make a suggestion and not show how it would work. Below is an example using a custom object called Machine to hold the data. This class is a bare minimum for an object and is only used as an example to get you started. It has some fields that will come in handy when looping thru the list to do your computations. It is also here you could add some custom functions/subs to help in some task that involves “Machine” objects. Also here you could add some compare functions which will enable you to sort among other things. After you put all this together you should end up with a list of valid Machine objects.
It is this list you could use to help you move on to the computing/removing duplicates part of your task. In the process of computing the data you could create a final list of Machine objects that you could use to export to excel with headers or display it to a DataGridView. Hope this helps.
Machine Class
Public Class Machine
Private name As String
Private partNumber As String
Private inventoryIn As Integer
Private inventoryOut As Integer
Private inventoryMissing As Integer
Public Sub New(inName As String, inPartNum As String, inInvIn As Integer, inInvOut As Integer, InInvMis As Integer)
name = inName
partNumber = inPartNum
inventoryIn = inInvIn
inventoryOut = inInvOut
inventoryMissing = InInvMis
End Sub
Property GetName As String
Get
Return name
End Get
Set(value As String)
name = value
End Set
End Property
Public Overrides Function ToString() As String
Return "Name: " + name + " #: " + partNumber + vbTab + " In:" + inventoryIn.ToString() + " Out:" + inventoryOut.ToString() + " Miss:" + inventoryMissing.ToString()
End Function
End Class
Now to your issue of reading the file
I did not use anything involving excel. Since you have a simple csv file we will use it. Also we will use the Machine class above. Using your open file dialog we get the name of the file to read. A variable partsList is created to hold the Machine objects created when reading the file. Then a for each loop goes through the list and displays the results in a text box on the form.
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim dialog As OpenFileDialog = New OpenFileDialog
dialog.Filter = "CSV document(*.csv)|*.csv"
Dim result As DialogResult = dialog.ShowDialog
If (result = DialogResult.OK) Then
Dim csvFile As String = dialog.FileName
Dim partsList As List(Of Machine) = ReadText(csvFile)
For Each curMac As Machine In partsList
TextBox1.AppendText(curMac.ToString() + Environment.NewLine)
Next
End If
End Sub
Function to read the csv file
Private Function ReadText(filePath As String) As List(Of Machine)
Dim fileReader As System.IO.StreamReader
Dim data As List(Of Machine) = New List(Of Machine)
fileReader = My.Computer.FileSystem.OpenTextFileReader(filePath)
Dim curline As String = ""
While (Not curline Is Nothing)
curline = fileReader.ReadLine()
'' need to check for valid data
'' if anything is invalid simply ignore it... i.e. your bad rows
'' keep in mind this will also ignore good rows that have a single piece of data bad
If (StringOK(curline)) Then
Dim newMac = GetMac(curline)
data.Add(newMac)
End If
End While
Return data
End Function
A couple of helper functions to validate the data
Private Function StringOK(inString As String) As Boolean
If (String.IsNullOrEmpty(inString)) Then
Return False
End If
Dim splitArray() As String = inString.Split(",")
Try
If ((String.IsNullOrEmpty(splitArray(0))) Or (String.IsNullOrEmpty(splitArray(1)))) Then
Return False
End If
Dim value As Integer
If ((Not Integer.TryParse(splitArray(2), value)) Or
(Not Integer.TryParse(splitArray(3), value)) Or
(Not Integer.TryParse(splitArray(4), value))) Then
Return False
End If
Return True
Catch ex As Exception
Return False
End Try
End Function
Function GetMac(inString As String) As Machine
Dim splitArray() As String = inString.Split(",")
Dim value As Integer
Dim name As String = splitArray(0)
Dim number As String = splitArray(1)
Integer.TryParse(splitArray(2), value)
Dim invIn As Integer = value
Integer.TryParse(splitArray(3), value)
Dim invOut As Integer = value
Integer.TryParse(splitArray(4), value)
Dim invMis As Integer = value
Return New Machine(name, number, invIn, invOut, invMis)
End Function
If you are trying to accomplish how to import the data into a datatable below is a fast way of handling that. This will bring your whole csv into a datatable which you could then do logic on and create your xlsx file.
Friend Shared Function GetExcelFile(ByVal strFileName As String, ByVal strPath As String) As DataTable
Try
Dim dt As New DataTable
Dim ConStr As String = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" & strPath & ";Extended Properties=""Text;HDR=Yes;FMT=Delimited\"""
Dim conn As New OleDb.OleDbConnection(ConStr)
Dim da As New OleDb.OleDbDataAdapter("Select * from " & strFileName, conn)
da.Fill(dt)
Return dt
Catch ex As Exception
Return Nothing
End Try
End Function

Arrays in Excel VBA macro

I have a folder with many files, out of which I need to: open the files for this week, store them in an array, pass them to a sub, and loop through them for getting summary information.
I am able to get the desired day files from the below code. But the code is throwing an error for storing it in the array and passing it to the array.
Sub BatchProcessing()
firstday = Date - Weekday(Date) + 2 'To get the 1st day of week
lastday = Date - Weekday(Date) + 6 'To get the 5th day of week
MyPath = "P:\Data\" 'Path where my files were present
Dim Day
Dim strArray(0 To 5) As String
iCount=0
For Day = firstday To lastday 'To loop through all 5 day files
formatted_date = Format(Day, "yyyyMd")
MyTemplate = "TestFile" & formatted_date & ".xlsx" ' Set the template.
Workbooks.Open MyPath & MyTemplate
strArray(iCount) = ActiveWorkbook.Name
iCount = iCount+1
Next
CreateStats(strArray) 'Calling a sub which will do the required calculation
End Sub
Sub CreateStats(strArray As String)
For Each element in strArray
set OriginalWorkbook = strArray(i)
'Do the processing'
Next
End Sub
Your strArray variable is of type Single. If you want that variable to be a string array, you must declare it as such:
Dim strArray(0 to 5) As String
EDIT:
Now that you've changed your code to use strArray() As String rather than strArray As Single, you should update your CreateStats sub procedure to accept an array as a parameter. It should now look something like this:
Private Sub CreateStats(myArray() As String)
As you have it now, your procedure only accepts a single string. It must accept an array of strings. Once you have that, you can loop through each string and do your processing.
By naming your array strArray it appears that you are going to have an array of strings, and in fact you attempt to store workbook names in it. However, you declared array to be Single, which is a numeric data type. Depending on what your CreateStats(strArray) sub does, you may need to change that from Single to String or possibly set up another array to hold the Single and this one to hold the String.

Resources