Parameters in "cells"-method - vb.net

I'm new in VB.NET. I want to export Excel from VB.NET, and I'm using EPPlus for my project.
What are the four parameters of ws.cells() in this code?
Code:
Imports OfficeOpenXml
Imports OfficeOpenXml.Style
Imports System.IO
Public Class excelExport
Private Access As New DBControl
Public Sub myReport()
Dim saveDialog As New SaveFileDialog
saveDialog.Filter = "Excel File (*.xlsx)|*.xlsx"
saveDialog.FilterIndex = 1
If saveDialog.ShowDialog() = DialogResult.OK Then
Try
Dim file As FileInfo = New FileInfo(saveDialog.FileName)
' Ensures we create a new workbook
If (file.Exists) Then
file.Delete()
End If
Dim pck As ExcelPackage = New ExcelPackage(file)
' Add a new worksheet to the empty workbook
Dim ws As ExcelWorksheet = pck.Workbook.Worksheets.Add("Sheet1")
' Load data from DataTable to the worksheet
ws.Cells("A1").Value = "new"
ws.Cells.AutoFitColumns()
' Add some styling
Dim rng As ExcelRange = ws.Cells(1, 1, 1, 10) '<---------- This code
rng.Style.Font.Bold = True
rng.Style.Fill.PatternType = ExcelFillStyle.Solid
rng.Style.Fill.BackgroundColor.SetColor(System.Drawing.Color.FromArgb(79, 129, 189))
rng.Style.Font.Color.SetColor(System.Drawing.Color.White)
' Save the new workbook
pck.Save()
MessageBox.Show(String.Format("Excel file {0} generated successfully.", file.Name))
Catch ex As Exception
MessageBox.Show("Failed to export to Excel. Original error: " + ex.Message)
End Try
End If
End Sub
End Class

Your first question is mainly asking for an opinion, which is not what this site is for. For what it is worth, I have found EPPlus useful for creating Excel files.
The answer to your second question is that the four arguments in the Cells method are:
The number of the first row to be included in the Range.
The number of the first column to be included in the Range.
The number of the last row to be included in the Range.
The number of the last column to be included in the Range.

Related

Cannot rename excel file

Imports System
Imports System.IO
Imports Microsoft.VisualBasic.FileIO
Imports Microsoft.Office.Interop
Module Program
Dim oxl As Excel.Application
Dim owbs As Excel.Workbooks
Dim owb As Excel.Workbook
Dim osheets As Excel.Worksheets
Dim osheet As Excel.Worksheet
Dim owr As Excel.Range
Dim tempName As String
Sub Main()
oxl = CreateObject("Excel.Application")
oxl.Visible = False
Dim path As String = "G:\Matthews Asia\Matthews Raw Data"
Dim names As String() = Directory.GetFiles(path, "*.xlsx")
Dim newDetails(,) As Object
'Get the new names and the boundaries of the data set
newDetails = getNewNames(names)
'Printing the detials to check getNewNames works or not - works fine
printNewDetails(newDetails) 'Working fine
'Rename files
rename(names, newDetails)
Console.ReadLine()
End Sub
Function getNewNames(ByVal names() As String) As Object(,)
'Declare Object type array to be returned with the details
Dim newDetails(names.Length - 1, 2) As Object
Dim lastRow, lastColumn As Integer
For i =0 To names.GetUpperBound(0)
'point to the excel file
owb = CType(oxl.Workbooks.Open(names(i)), Excel.Workbook) 'Sometimes error comes here
osheet = CType(owb.Worksheets("Holdings"), Excel.Worksheet)
owr = CType(osheet.Range("A7"), Excel.Range)
'Pick new name of file and add the excel extension
tempName = CStr(owr.Value) & ".xlsx"
'row & column number of last data point in the dataset
lastColumn = CType(osheet.Range("A13").End(Excel.XlDirection.xlToRight), Excel.Range).Column
lastRow = CType(osheet.Range("A13").End(Excel.XlDirection.xlDown), Excel.Range).Row
newDetails(i, 0) = tempName
newDetails(i, 1) = lastRow
newDetails(i, 2) = lastColumn
Next
owb.Close()
Return newDetails
End Function
Function printNewDetails(ByVal details As Object(,)) As Integer
For i = 0 To details.GetUpperBound(0)
Console.WriteLine("New name: {0}", details(i, 0))
Console.WriteLine("Last row: {0}", details(i, 1))
Console.WriteLine("Last Column: {0}", details(i, 2))
Next
Return 1
End Function
Sub rename(ByVal oldName As String(), ByVal tempArray As Object(,))
For i = 0 To oldName.GetUpperBound(0)
FileSystem.RenameFile(oldName(i), CStr(tempArray(i, 0))) 'Error Here
Next
End Sub
End Module
i am trying to rename some excel files all of which is in a particular directory. The code does the following:
It opens each file which has just one sheet
Then it picks the string in cell A7 in each of those files
It also finds out the last row and last column of the data set (cell A13 is the starting point of the dataset in each of the files)
Finally, in an object array newDetails we store the string in cell A7 in the first column, the last row of the dataset (column 2) and last column of the dataset (column 3). Each row has data corresponding to one excel file
After that, the code renames the files using the rename subroutine -- the idea is to swap the old names which is stored in the names array with the string value in the first column of the newDetails array.
But When I run the code, the following error message comes: The process cannot access the file because it is being used by another process. I have opened task manager, manually closed all excel processes and even restarted the computer - even then this error comes. Have attached the screenshot of the error. Requesting help.
Strangely, when I run the code more than once, sometimes I am getting the error in the line owb = CType(oxl.Workbooks.Open(names(i)), Excel.Workbook) and that error warns me to check if the files are corrupted or not. The files are not corrupted because when I manually open them there is no problem.
When a filename starts with ~$, it usually indicates that the file is already open (in Excel). However, sometimes this file doesn't get deleted. If you're sure that Excel is no longer running, such as after a reboot, and such a file exists, one can delete it. Of course, one could also just ignore it when getting a list of files.
You haven't mentioned if you're using .NET or .NET Framework and which version. VS 2019 supports .NETCore 3.1, .NET 5 (no longer supported), and .NET Framework versions.
One may consider using NuGet package DocumentFormat.OpenXml or ClosedXml instead. However, if one desires to use Excel Interop, try the following:
Add a reference: Microsoft Excel xx.x Object Library (ex: Microsoft Excel 16.0 Object Library)
Project
Add Project Reference...
COM
Microsoft Excel xx.x Object Library (ex: Microsoft Excel 16.0 Object Library)
OK
Create a class (name: XLInfo.vb)
Public Class XLInfo
Public Property OriginalFilename As String
Public Property LastRow As Integer
Public Property LastColumn As Integer
Public Property RenamedTo As String
End Class
Create a module (name: HelperExcel.vb)
Imports Microsoft.Office.Interop
Imports System.IO
Module HelperExcel
Private Function GetExcelFilenames(folderPath As String) As List(Of String)
Dim filenames As List(Of String) = New List(Of String)
For Each fqFilename As String In Directory.GetFiles(folderPath, "*.xlsx")
'get only the filename
Dim fn As String = Path.GetFileName(fqFilename)
If Not fn.StartsWith("~") Then
Debug.WriteLine($"Info: adding '{fqFilename}'...")
filenames.Add(fqFilename) 'add
End If
Next
Return filenames
End Function
Public Function ProcessExcelFiles(folderPath As String) As List(Of XLInfo)
#Disable Warning CA1416
Dim infos As List(Of XLInfo) = New List(Of XLInfo)
Dim oxl As Excel.Application = Nothing
Dim owbs As Excel.Workbooks = Nothing
Dim owb As Excel.Workbook = Nothing
Dim osheets As Excel.Worksheets = Nothing
Dim osheet As Excel.Worksheet = Nothing
Dim owr As Excel.Range = Nothing
'get filenames
Dim names As List(Of String) = GetExcelFilenames(folderPath)
Try
'create new instance
oxl = New Excel.Application()
oxl.Visible = False
For i As Integer = 0 To names.Count - 1
'create new instance
Dim info As XLInfo = New XLInfo()
'create reference
Dim fn As String = names(i)
'set value
info.OriginalFilename = fn
'open workbook
'owb = oxl.Workbooks.Open(Filename:=fn, [ReadOnly]:=True)
owb = oxl.Workbooks.Open(Filename:=fn)
'open worksheet
osheet = owb.Worksheets(1)
'set value - this is the new filename
info.RenamedTo = Path.Combine(Path.GetDirectoryName(fn), $"{osheet.Range("A7").Value.ToString()}.xlsx")
'ToDo: get last column
'set value - last column
'info.LastColumn = DirectCast(osheet.Range("A13").End(Excel.XlDirection.xlToRight), Excel.Range).Column
'ToDo: get last row
'set value - last row
'info.LastRow = DirectCast(osheet.Range("A13").End(Excel.XlDirection.xlDown), Excel.Range).Row
'add
infos.Add(info)
If osheet IsNot Nothing Then
'release all resources
System.Runtime.InteropServices.Marshal.FinalReleaseComObject(osheet)
'set value
osheet = Nothing
End If
If owb IsNot Nothing Then
'save
owb.SaveCopyAs(info.RenamedTo)
'owb.SaveAs2(Filename:=info.RenamedTo)
'close
owb.Close(False)
'release all resources
System.Runtime.InteropServices.Marshal.FinalReleaseComObject(owb)
'set value
owb = Nothing
End If
Next
Finally
If osheet IsNot Nothing Then
'release all resources
System.Runtime.InteropServices.Marshal.FinalReleaseComObject(osheet)
'set value
osheet = Nothing
End If
If owb IsNot Nothing Then
'close
owb.Close(False)
'release all resources
System.Runtime.InteropServices.Marshal.FinalReleaseComObject(owb)
'set value
owb = Nothing
End If
If oxl IsNot Nothing Then
'quit
oxl.Quit()
'release all resources
System.Runtime.InteropServices.Marshal.FinalReleaseComObject(oxl)
'force garbage collection
GC.Collect()
End If
End Try
#Enable Warning CA1416
'sleep
System.Threading.Thread.Sleep(250)
'delete original filenames
If Not Directory.Exists(Path.Combine(folderPath, "Original Files")) Then
'create folder if it doesn't exist
Directory.CreateDirectory(Path.Combine(folderPath, "Original Files"))
End If
For i As Integer = 0 To names.Count - 1
If File.Exists(names(i)) Then
'move file to .\Original Files\<filename>
File.Move(names(i), Path.Combine(folderPath, "Original Files", Path.GetFileName(names(i))), True)
Debug.WriteLine($"File moved to '{Path.Combine(folderPath, "Original Files", Path.GetFileName(names(i)))}'")
'ToDo: if one desires to delete the original filenames,
'uncomment the line below
'delete file
'File.Delete(names(i))
End If
Next
Return infos
End Function
End Module
Note: The code above was tested with VS 2022 (.NET 6) since .NET 5 is no longer supported. See here for more info. If using .NET Framework, one can remove #Disable Warning CA1416 and #Enable Warning CA1416.
Usage:
Sub Main(args As String())
'ToDo: replace folder name with desired folder name
Dim infos As List(Of XLInfo) = ProcessExcelFiles("C:\Temp")
For Each info As XLInfo In infos
Dim msg As String = $"OriginalFilename: '{info.OriginalFilename}' RenamedTo: '{info.RenamedTo}' LastRow: '{info.LastRow}' LastColumn: '{info.LastColumn}'"
Debug.WriteLine(msg)
Console.WriteLine(msg)
Next
End Sub
Resources:
Excel Interop
What is .NET Framework
Microsoft .NET Framework
Microsoft .NET and .NET Core
System.IO.File
System.IO.Path
Interpolated Strings (Visual Basic Reference)
Collections (Visual Basic)
Objects and classes in Visual Basic
Option Strict Statement
Additional Resources
Programmatically getting the last filled excel row using C#

Changing SaveFileDialog behaviour with filters

I have to export, under pressure of a button, some data in different extensions. To do this, I provided a SaveFileDialog, but depending on the filter (and so the output file extension), I have to write data with a different process:
if I have to export it as .txt or .csv, I have to use writer
otherwise if I have to export it as .xlsx, .xls or .ods I have to use NPOI
I provided the code both for writer and NPOI and they work singularly. If I try to put them together, .txt and .csv works, but for .xlsx, it exports a corrupted file.
This is the code I'm using:
Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
SaveFileDialog1.Filter = "TXT Files (*.txt*)|*.txt|CSV Files (*.csv*)|*.csv|Excel 2010 Workbook (*.xlsx*)|*.xlsx|Excel 2000 Workbook (*.xls*)|*.xls|OpenOffice Spreadsheet (*.ods*)|*.ods"
SaveFileDialog1.CheckFileExists = False
Dim t As Integer
t = CInt(Form10.Label13.Text)
If SaveFileDialog1.ShowDialog() = DialogResult.OK Then
If SaveFileDialog1.Filter = "TXT Files (*.txt*)|*.txt|CSV Files (*.csv*)|*.csv|" Then
Using writer = New StreamWriter(SaveFileDialog1.FileName)
For Each o As Object In Form10.ListBox2.Items
writer.WriteLine(o)
Next
End Using
End If
End If
If SaveFileDialog1.Filter = "Excel 2010 Workbook (*.xlsx*)|*.xlsx|Excel 2000 Workbook (*.xls*)|*.xls|OpenOffice Spreadsheet (*.ods*)|*.ods|" Then
Dim fs As New FileStream(SaveFileDialog1.FileName, FileMode.Create, FileAccess.Write)
Dim workbook As IWorkbook = New XSSFWorkbook()
Dim worksheet As ISheet = workbook.CreateSheet()
Dim ich As ICreationHelper = workbook.GetCreationHelper()
Dim rows As New List(Of IRow)
Dim rowz As IRow = worksheet.CreateRow(0)
rowz.CreateCell(0).SetCellValue(ich.CreateRichTextString("Time [s]"))
rowz.CreateCell(1).SetCellValue(ich.CreateRichTextString("HRR [kW]"))
For i As Integer = 1 To t
Dim row As IRow = worksheet.CreateRow(i)
row.CreateCell(0).SetCellValue(CDbl(Form10.ListBox1.Items(i)))
row.CreateCell(1).SetCellValue(CDbl(Form10.ListBox2.Items(i)))
rows.Add(row)
Next i
workbook.Write(fs)
fs.Close()
End If
End If
End Sub
I'd like to know if the "if loop" is set properly for filters. Thanks all are gonna answer me. Best regards.
Just get the file path selected by the user and check its extension, e.g.
Select Case Path.GetExtension(SaveFileDialog1.FileName)
Case ".txt", ".csv"
'...
Case ".xlsx", ".xls", ".ods"
'...
End Select

Access built-in document properties information without opening the workbook

I am using below code to get the created date of a workbook.
Dim mFile As String
mFile = "C:\User\User.Name\Test\Test.xlsx"
Debug.Print CreateObject("Scripting.FileSystemObject").GetFile(mFile).DateCreated
However to my surprise, this returns the date when the file is created in the directory. If you copy the file to another folder, above will return that time and date it was copied (created).
To actually get the original created date, I tried using BuiltinDocumentProperties method. Something like below:
Dim wb As Workbook
Set wb = Workbooks.Open(mfile) '/* same string as above */
Debug.Print wb.BuiltinDocumentProperties("Creation Date")
Above does return the original date the file was actually created.
Now, I have hundreds of file sitting in a directory that I need to get the original creation date. I can certainly use above and look over the files, but opening and closing all of it from a shared drive takes some time. So I was wondering, if I can get the BuiltinDocumentProperties without opening the file(s) like using the first code above which is a lot faster and easier to manage.
If you somebody can point me to a possible solution, that would be great.
Try something like this. The key is the special DSO object.
Imports Scripting
Private Sub ReadProperties()
Dim pathName As String = "C:\yourpathnamehere"
Dim Fso As FileSystemObject = New Scripting.FileSystemObject
Dim fldr As Folder = Fso.GetFolder(pathName)
Dim objFile As Object = CreateObject("DSOFile.OleDocumentProperties")
Dim ResValue As String = Nothing
For Each f In fldr.Files
Try
objFile.Open(f)
ResValue = objFile.SummaryProperties.DateCreated
' Do stuff here
objFile.Close
Catch ex As Exception
'TextBox1.Text = ex.Message
End Try
Application.DoEvents()
Next
End Sub

How to transfer a data table in VB.NET to Excel

I have a data table in VB.net that I am trying to send to a specific range in an Excel spreadsheet. However, upon running the program I get the error:
An exception of type 'System.Runtime.InteropServices.COMException' occurred in MeasurementFinder.dll but was not handled in user code
Additional information: Exception from HRESULT: 0x800A03EC
The error alerts on the following sub:
Private Sub WriteDataTableToRng(targetWs As Excel.Worksheet, anchor As Excel.Range, tbl As System.Data.DataTable)
'This sub writes the given tbl to the targetWs as a range with its top left cell acting as anchor
Dim wRange As Excel.Range = anchor 'wRange = write range. This range represents the cell being written to over every iteration
For Each colm As DataColumn In tbl.Columns 'This loop writes the column names into the target ws
targetWs.Range(wRange).Value2 = colm.ColumnName '**THIS LINE IS CALLED OUT BY THE ERROR
wRange = wRange.Offset(0, 1)
Next colm
wRange = anchor.Offset(1, 0)
For Each row As DataRow In tbl.Rows
For Each col As DataColumn In tbl.Columns
targetWs.Range(wRange).Value2 = tbl.Rows.Item(tbl.Rows.IndexOf(row)).Item(tbl.Columns.IndexOf(col)) '**THIS LINE IS CALLED OUT BY THE SAME ERROR IF THE PREVIOUS LOOP IS COMMENTED OUT
Next col
Next row
End Sub
The sub that calls the previous one is:
Private Sub ReportOnTube(TubeID As Integer)
'This sub creates an Excel workbook that acts as a report on a tube, given its ID
'The report has a worksheet for each measurement tied to the tube (From the Gauge DB)
'Verify the tube is in the DB
Dim TubeExists As Boolean
TubeExists = VerifyTube(TubeID)
If TubeExists Then
'Create a new excel workbook and name/time stamp it
Dim wb As Excel.Workbook = Me.Application.Workbooks.Add()
wb.SaveAs("C:\Gauge Reports\Tube " & TubeID & System.DateTime.Now.ToString(" HH_mm_ss dd-MM-yyyy"))
'Add a worksheet for each measurement tied to the tube
Dim ws As Excel.Worksheet
ws = wb.Worksheets.Add
Dim aRng As Range
aRng = ws.Range("B2")
TubesConn.Close()
TubesConn.Open()
Dim selectTbl As New SqlCommand("SELECT * FROM [Tubes]", TubesConn)
Dim rdr As SqlDataReader
Dim aTbl As New System.Data.DataTable
rdr = selectTbl.ExecuteReader()
aTbl.Load(rdr)
Call WriteDataTableToRng(ws, aRng, aTbl)
TubesConn.Close()
End If
End Sub
I am using the following imports:
Imports System.Data.Sql
Imports System.Data.SqlClient
Imports System.Data
Imports System.IO
Imports Microsoft.Office.Interop.Excel
What I intend to do is iterate through the given data table and write the values in the table to a range in the spreadsheet whose top left corner is given by the "anchor" range variable. I have no warnings from Visual Stuio's IntelliSense, so I don't really know where to start with this one.
Thanks in advance!
targetWs.Range(wRange).Value2 = colm.ColumnName is redundant/wrong. The excel range object stored in wRange is already a property of the worksheet in which it's contained.
In other words, if you have it print out wRange.Parent.Name you will get the worksheet that the range is in. You can't have a range point to two different worksheets (well maybe through like a range union, I've never tried, but who would do that anyway, you probably can't do it... </streamOfConciousness>)
Instead, just use:
wrange.value = colm.columnName

Loop through excel workbooks in vb.net

I am working with a folder of xls files that are all in identical format (automatically generated by entering numbers into a pricing app). I need to pull the data that is in cell D54 on the worksheet of the same name in every file. Can't seem to get anything to work to make it loop.
Any ideas how this can be done?
If you can have the files generated in XLSX format then this is what I would do.
http://epplus.codeplex.com/
This is an amazing component library for dealing with Excel XLSX sheets.
Example...
Sub temp()
Dim out As New List(Of String)
Using pac As New ExcelPackage(New IO.FileInfo("c:\temp.xlsx"))
For Each wb As ExcelWorksheet In pac.Workbook.Worksheets
out.Add(wb.Cells("D54").Value.ToString)
Next
End Using
End Sub
Otherwise the option is to reference the Excel Com+ Library from office to open an XLS sheet with what should be similar code as below.
Sub temp2()
Dim out As New List(Of String)
Dim app As New Microsoft.Office.Interop.Excel.Application
app.DisplayAlerts = False
Dim wb As Microsoft.Office.Interop.Excel.Workbook = app.Workbooks.Open("c:\temp.xls")
For Each ws As Microsoft.Office.Interop.Excel.Worksheet In wb.Worksheets
Dim r As Microsoft.Office.Interop.Excel.Range = ws.Cells(54, 4)
out.Add(r.Value.ToString)
Next
app.close()
End Sub