Whole Code Explained:
I have this code that saves a txt file as a Microsoft Excel Comma Separated Values File (.csv) then opens a blank template excel file with a sheet named Graphs. It then copies the sheet with all the data from the csv file into the template excel file, renames it to "data" Then deletes the csv after close. The code then Inserts a chart in the "graph" sheet. Next it finds the total number of rows used and number of columns used for references for the ranges in the graphs and then for later formulas. This data is Acceleration from a accelerometer at a specific frequency. Therefor there is a lot of data, 8193 rows! The data lay out is top row labels (hz, Part1, 2...), Column A is frequencys, and all other cells from B2:whatever is accelerometer readings.
The Problem is it takes 83.22 seconds
to do the following loop, which inserts the average formula:
Do While i <= LastRow
'Assign Range To Take Average
CellLeft = wbtempXl.Worksheets("Data").Cells(i, 2)
CellRight = wbtempXl.Worksheets("Data").Cells(i, LastColumn)
AvgRange = wbtempXl.Worksheets("Data").Range(CellLeft, CellRight)
Average = appXL.WorksheetFunction.Average(AvgRange)
wbtempXl.Worksheets("Data").Cells(i, LastColumn + 1).Value = Average
i = i + 1
Loop
After this Average formula I am adding peak finding logic to find the peaks and troughs in the data, but this step alone takes a minute and a half. Is there a fast, better way of doing this? Looping formulas that is.
Note: I can not just have the formulas in the template. The test could include 12 parts or 100 parts. Each part has its own column and the frequency is in the rows of column A. The rest of the Rows is acceleration readings per frequency. Would post picture but not allowed to yet.
Full Code:
Public Sub btn_Do_Click(sender As Object, e As EventArgs) Handles btn_Do.Click
Dim FileTXT As String = cbo_FileList.Text
Dim folderpath As String = "C:\Users\aholiday\Desktop\Data Dump"
Dim txtpath As String = folderpath & "\" & FileTXT & ".txt"
Dim csvpath As String = "C:\Temp\" & FileTXT & ".csv"
Dim FinalFile As String = "C:\Users\aholiday\Desktop\Test"
Try
File.Copy(txtpath, csvpath)
Catch
MsgBox("Please Choose File")
Exit Sub
End Try
appXL = CreateObject("Excel.Application")
appXL.Visible = True
wbcsvXl = appXL.Workbooks.Open(csvpath)
wbtempXl = appXL.Workbooks.Open(FinalFile)
wbcsvXl.Worksheets(FileTXT).Copy(After:=wbtempXl.Worksheets("Graphs"))
wbtempXl.Worksheets(FileTXT).Name = ("Data")
'Close Objects
wbcsvXl.Close()
File.Delete(csvpath)
'Release Objects
wbcsvXl = Nothing
' Declare Varables
Dim Chart As Excel.Chart
Dim ChartXL As Excel.ChartObjects
Dim ThisChart As Excel.ChartObject
Dim SerCol As Excel.SeriesCollection
Dim Series As Excel.Series
Dim xRange As Excel.Range
Dim xCelltop As Excel.Range
Dim xCellBottom As Excel.Range
Dim yRange As Excel.Range
Dim yCelltop As Excel.Range
Dim yCellBottom As Excel.Range
Dim CellRight As Excel.Range
Dim CellLeft As Excel.Range
Dim AvgRange As Excel.Range
Dim Average As Double
Dim LastRow As Long
Dim LastColumn As Long
Dim i As Integer
' Set i integer
i = 2
'Make Chart
ChartXL = wbtempXl.Worksheets("Graphs").ChartObjects
ThisChart = ChartXL.Add(0, 0, 800, 400)
Chart = ThisChart.Chart
Chart.ChartType = Excel.XlChartType.xlXYScatterSmoothNoMarkers
With ThisChart.Chart
.HasTitle = True
.ChartTitle.Characters.Text = "RF Graph"
' X,Y title??????
End With
'Count Rows Used
'Find last Row Used
With wbtempXl.Worksheets("Data")
LastRow = .UsedRange.Rows.Count
End With
'Count Columns Used
'Find Last Column Used
With wbtempXl.Worksheets("Data")
LastColumn = .UsedRange.Columns.Count
End With
Do Until i > LastColumn
'Excel Chart X Axis Values
xCelltop = wbtempXl.Worksheets("Data").Cells(2, 1)
xCellBottom = wbtempXl.Worksheets("Data").Cells(LastRow, 1)
xRange = wbtempXl.Worksheets("Data").Range(xCelltop, xCellBottom)
'Excel Chart Y Axis Values
yCelltop = wbtempXl.Worksheets("Data").Cells(2, i)
yCellBottom = wbtempXl.Worksheets("Data").Cells(LastRow, i)
yRange = wbtempXl.Worksheets("Data").Range(yCelltop, yCellBottom)
'Label Part in Data Sheet
wbtempXl.Worksheets("Data").Cells(1, i).Value = ("Rotor " & i - 1)
'Add New Series to Chart
SerCol = Chart.SeriesCollection
Series = SerCol.NewSeries
'Rename and Assign Values
With Series
.Name = ("Rotor " & i - 1)
Series.XValues = xRange
Series.Values = yRange
End With
Chart.Refresh()
i = i + 1
Loop
'Add Average Column Label
wbtempXl.Worksheets("Data").Cells(1, LastColumn + 1).Value = "Average"
'Rest i integer
i = 2
Do While i <= LastRow
'Assign Range To Take Average
CellLeft = wbtempXl.Worksheets("Data").Cells(i, 2)
CellRight = wbtempXl.Worksheets("Data").Cells(i, LastColumn)
AvgRange = wbtempXl.Worksheets("Data").Range(CellLeft, CellRight)
Average = appXL.WorksheetFunction.Average(AvgRange)
wbtempXl.Worksheets("Data").Cells(i, LastColumn + 1).Value = Average
i = i + 1
Loop
'Release Objects
wbtempXl = Nothing
appXL = Nothing
GC.Collect()
Me.Close()
End Sub
I'd suggest you put formulas in the cells with code then convert to values if required:
With wbtempXl.Worksheets("Data")
formularange = .Range(.Cells(i, LastColumn + 1), .Cells(LastRow, LastColumn + 1))
End With
formularange.FormulaR1C1 = "=AVERAGE(RC2:RC[-1])"
formularange.Value2 = formularange.Value2
Related
I have a data set of 100 (or 999 / any random numbers) and I wish to extract X% (x can range between 1-99) of rows after I put it on a pop out dialog box.
# Header Header 2
1 A Z
2 2 Y
3 C X
4 D 3
5 E
6 F d
7
8 H 1
9 I 8
10 J 9
Ideally, I wish to have an output of 2 new worksheets as below after I key in 20 in the pop out box.
Output Workbook 1
Output Workbook 2
Conditions to be considered:
The total number of rows & columns can be even or odd
The sheet name may vary for different workbook.
It should be able to save in master macro-enabled excel and use across.
I modified the codes from Joe's (Thanks!) but my workbook appears to crash on the bold line.
Public Sub SplitWbByPercentage()
Dim inputNum As Long
Dim firstColumn As Long
Dim headerRow As Long
Dim cutoffRow As Long
Dim lastRow As Long
Dim startingRows As Long
Dim beforeWorksheet As Worksheet
Dim afterWorksheet As Worksheet
Dim x As Long
Application.ScreenUpdating = False
inputNum = InputBox("Please enter First File Percentage: ")
Set wbOrig = ActiveWorkbook
Set ThisSheet = wbOrig.ActiveSheet
firstColumn = ThisSheet.UsedRange.Column
headerRow = 1
lastRow = ThisSheet.UsedRange.Rows.Count + headerRow
startingRows = lastRow - headerRow 'for the headers
cutoffRow = Round(startingRows * (inputNum / 100), 0) + headerRow
Set beforeWorksheet = Worksheets.Add()
Set afterWorksheet = Worksheets.Add()
beforeWorksheet.Rows(headerRow).EntireRow.Value = ThisSheet.Rows(headerRow).EntireRow.Value
afterWorksheet.Rows(headerRow).EntireRow.Value = ThisSheet.Rows(headerRow).EntireRow.Value
For x = headerRow + 1 To cutoffRow
Set wb = Workbooks.Add
**beforeWorksheet.Rows(x).EntireRow.Value = ThisSheet.Rows(x).EntireRow.Value**
wb.SaveAs wbOrig.Path & "\Data 1" & WorkbookCounter
wb.Close
Next
For x = cutoffRow + 1 To lastRow
Set wb = Workbooks.Add
afterWorksheet.Rows(headerRow + x - cutoffRow).EntireRow.Value = ThisSheet.Rows(x).EntireRow.Value
wb.SaveAs wbOrig.Path & "\Data 2" & WorkbookCounter
wb.Close
Next
Application.ScreenUpdating = True
End Sub
Since you tagged the question with excel-vba, I'm going to assume you are at least familiar with macros, so I came up with a macro that does what you want.
Edit - Updating code based on additional requirements. New code to pop up an input box and then split the data into two new workbooks, leaving the original alone.
Edit 2 - Updating code based off of provided sample file. New code copies over the entire worksheet and then deletes rows out (versus desired rows over) to help with memory usage within Excel.
Option Explicit
Public Sub SplitWbByPercentage()
Dim wbOrig As Workbook
Dim ThisSheet As Worksheet
Dim wbOutput1 As Workbook
Dim wsOutput1 As Worksheet
Dim wbOutput2 As Workbook
Dim wsOutput2 As Worksheet
Dim inputNum As Long
Dim firstColumn As Long
Dim headerRow As Long
Dim lastRow As Long
Dim rowCount As Long
Dim cutoffRow As Long
Dim x As Long
Application.ScreenUpdating = False
inputNum = InputBox("Please enter First File Percentage: ")
Set wbOrig = ActiveWorkbook
Set ThisSheet = wbOrig.ActiveSheet
firstColumn = ThisSheet.UsedRange.Column
headerRow = ThisSheet.UsedRange.Row
lastRow = ThisSheet.UsedRange.Rows.Count + headerRow
rowCount = lastRow - headerRow 'for the headers
cutoffRow = Round(rowCount * (inputNum / 100), 0) + headerRow
' Output Workbook 1
ThisSheet.Copy
Set wbOutput1 = ActiveWorkbook
Set wsOutput1 = wbOutput1.Worksheets(1)
wsOutput1.Range(wsOutput1.Rows(cutoffRow + 1), wsOutput1.Rows(lastRow)).Delete
wbOutput1.SaveAs wbOrig.Path & "\Data 1"
wbOutput1.Close
' Output Workbook 2
ThisSheet.Copy
Set wbOutput2 = ActiveWorkbook
Set wsOutput2 = wbOutput2.Worksheets(1)
wsOutput2.Range(wsOutput2.Rows(headerRow + 1), wsOutput2.Rows(cutoffRow)).Delete
wbOutput2.SaveAs wbOrig.Path & "\Data 2"
wbOutput2.Close
Application.ScreenUpdating = True
End Sub
I want to randomly select 50 rows from one sheet and pasting them in a separate workbook for data sampling. I don't know how to do it because first, I'm new to VBA, I want to learn something new and second, I tried searching this on Google but no accurate answer found.
So what's on my mind is this:
I'll get first the number of rows in that worksheet. I've already
done it with this one line of code:
CountRows = ActiveSheet.Cells(ActiveSheet.Rows.Count, "A").End(xlUp).Row
Get a random number from 1 to CountRows uniquely. The random numbers should be incremental (1,5,7,20,28,30,50 and no backward counting). Then grab that row, create a new workbook if not yet open and paste it there.
How can I achieve this process? I have no idea how to start this.
First, generate an array of 50 unique numbers between 1 and CountRows, using this routine:
' Generate a sorted array(0 to count-1) numbers between a and b inclusive
Function UniqueRandom(ByVal count As Long, ByVal a As Long, ByVal b As Long) As Long()
Dim i As Long, j As Long, x As Long
ReDim arr(b - a) As Long
Randomize
For i = 0 To b - a: arr(i) = a + i: Next
If b - a < count Then UniqueRandom = arr: Exit Function
For i = 0 To b - a 'Now we shuffle the array
j = Int(Rnd * (b - a))
x = arr(i): arr(i) = arr(j): arr(j) = x ' swap
Next
' After shuffling the array, we can simply take the first portion
ReDim Preserve arr(0 To count - 1)
'sorting, probably not necessary
For i = 0 To count - 1
For j = i To count - 1
If arr(j) < arr(i) Then x = arr(i): arr(i) = arr(j): arr(j) = x ' swap
Next
Next
UniqueRandom = arr
End Function
Now you can use the above routine to generate random, unique and sorted indexes and copy the corresponding rows. Here's an example:
Sub RandomSamples()
Const sampleCount As Long = 50
Dim lastRow As Long, i As Long, ar() As Long, rngToCopy As Range
With Sheet1
lastRow = .Cells(.Rows.count, "A").End(xlUp).row
ar = UniqueRandom(sampleCount, 1, lastRow)
Set rngToCopy = .Rows(ar(0))
For i = 1 To UBound(ar)
Set rngToCopy = Union(rngToCopy, .Rows(ar(i)))
Next
End With
With Workbooks.Add
rngToCopy.Copy .Sheets(1).Cells(1, 1)
.SaveAs ThisWorkbook.path & "\" & "samples.xlsx"
.Close False
End With
End Sub
Following code will do what you need.
Sub Demo()
Dim lng As Long
Dim tempArr() As String
Dim srcWB As Workbook, destWB As Workbook
Dim rng As Range
Dim dict As New Scripting.Dictionary
Const rowMax As Long = 100 'maximum number of rows in source sheet
Const rowMin As Long = 1 'starting row number to copy
Const rowCopy As Long = 50 'number of rows to copy
Dim intArr(1 To rowCopy) As Integer, rowArr(1 To rowCopy) As Integer
Set srcWB = ThisWorkbook
'get unique random numbers in dictionary
With dict
Do While .Count < rowCopy
lng = Rnd * (rowMax - rowMin) + rowMin
.Item(lng) = Empty
Loop
tempArr = Split(Join(.Keys, ","), ",")
End With
'convert random numbers to integers
For i = 1 To rowCopy
intArr(i) = CInt(tempArr(i - 1))
Next i
'sort random numbers
For i = 1 To rowCopy
rowArr(i) = Application.WorksheetFunction.Small(intArr, i)
If rng Is Nothing Then
Set rng = srcWB.Sheets("Sheet1").Rows(rowArr(i))
Else
Set rng = Union(rng, srcWB.Sheets("Sheet1").Rows(rowArr(i)))
End If
Next i
'copy random rows, change sheet name and destination path as required
Set destWB = Workbooks.Add
With destWB
rng.Copy destWB.Sheets("Sheet1").Range("A1")
.SaveAs Filename:="D:\Book2.xls", FileFormat:=56
End With
End Sub
Above code uses Dictionary so you have to add reference to Microsoft Scripting Runtime Type Library. In Visual Basic Editor, go to Tools->References and check "Microsoft Scripting Runtime" in the list.
Let me know if anything is not clear.
I was sent an Excel sheet that with 4 charts. The data for the charts is in another workbook that was not provided.
Goal: I want to extract the data from the charts using a VBA sub.
Problem: I am having some trouble with "type mismatch." When I try to assign the Variant array oSeries.XValues to a Range of cells.
Option Explicit
Option Base 1
' 1. Enter the following macro code in a module sheet.
' 2. Select the chart from which you want to extract the underlying data values.
' 3. Run the GetChartValues Sub. The data from the chart is placed in a new worksheet named "ChartName Data".
'
Sub GetChartValues()
'
Dim lxNumberOfRows As Long
Dim lyNumberOfRows As Long
Dim oSeries As Series
Dim lCounter As Long
Dim oWorksheet As Worksheet
Dim oChart As Chart
Dim xValues() As Variant
Dim yValues() As Variant
Dim xDestination As Range
Dim yDestination As Range
Set oChart = ActiveChart
' If a chart is not active, just exit
If oChart Is Nothing Then
Exit Sub
End If
' Create the worksheet for storing data
Set oWorksheet = ActiveWorkbook.Worksheets.Add
oWorksheet.Name = oChart.Name & " Data"
' Loop through all series in the chart and write there values to
' the worksheet.
lCounter = 1
For Each oSeries In oChart.SeriesCollection
xValues = oSeries.xValues
yValues = oSeries.values
' Calculate the number of rows of data. 1048576 is maximum number of rows in excel.
lxNumberOfRows = WorksheetFunction.Min(UBound(oSeries.xValues), 1048576 - 1)
lyNumberOfRows = WorksheetFunction.Min(UBound(oSeries.values), 1048576 - 1)
' Sometimes the Array is to big, so chop off the end
ReDim Preserve xValues(lxNumberOfRows)
ReDim Preserve yValues(lyNumberOfRows)
With oWorksheet
' Put the name of the series at the top of each column
.Cells(1, 2 * lCounter - 1) = oSeries.Name
.Cells(1, 2 * lCounter) = oSeries.Name
Set xDestination = .Range(.Cells(1, 2 * lCounter - 1), .Cells(lxNumberOfRows + 1, 2 * lCounter - 1))
Set yDestination = .Range(.Cells(1, 2 * lCounter), .Cells(lxNumberOfRows + 1, 2 * lCounter))
'Assign the x and y data from the chart to a range in the worksheet
xDestination.value = Application.Transpose(xValues)
yDestination.value = Application.Transpose(yValues)
' This does not work either
' .Range(.Cells(2, 2 * lCounter - 1), .Cells(lxNumberOfRows + 1, 2 * lCounter - 1)).value = Application.Transpose(oSeries.xValues)
' .Range(.Cells(2, 2 * lCounter), .Cells(lxNumberOfRows + 1, 2 * lCounter)).value = Application.Transpose(oSeries.values)
End With
lCounter = lCounter + 1
Next
' Cleanup
Set oChart = Nothing
Set oWorksheet = Nothing
End Sub
The main issue is the following lines:
.Range(.Cells(2, 2 * lCounter - 1), .Cells(lxNumberOfRows + 1, 2 * lCounter - 1)) = Application.Transpose(oSeries.xValues)
.Range(.Cells(2, 2 * lCounter), .Cells(lxNumberOfRows + 1, 2 * lCounter)) = Application.Transpose(oSeries.values)
Upon further inspection using the Locals window, I find the following:
The below code works while the above code does not.
Sub Test2()
Dim A(6) As Variant
'A(1) = 1
A(2) = 2#
A(3) = 3#
A(4) = 4#
A(5) = 5#
Range(Cells(1, 1), Cells(6, 1)).value = Application.Transpose(A)
End Sub
Why doesn't the first piece of code work?
Looping over many cells is slow in this case (I've tried). Please, don't use a loop unless it is seconds for 1,000,000 element.
The main cause is the built-in Transpose function. Transpose can only handle arrays with 2^16 or less elements.
The code below works well. It handles the problem of Transpose function limitation of 2^16 elements. It uses a for loop but the for loop is fast for arrays. For four series and each having 1048576 elements, the Sub took about 10 seconds to run. This is acceptable.
Option Explicit
Option Base 1
' 1. Enter the following macro code in a module sheet.
' 2. Select the chart from which you want to extract the underlying data values.
' 3. Run the GetChartValues Sub. The data from the chart is placed in a new worksheet named "ChartName Data".
'
Public Sub GetChartValues()
Dim lxNumberOfRows As Long
Dim lyNumberOfRows As Long
Dim oSeries As Series
Dim lSeriesCounter As Long
Dim oWorksheet As Worksheet
Dim oChart As Chart
Dim xValues() As Variant
Dim yValues() As Variant
Dim xDestination As Range
Dim yDestination As Range
Set oChart = ActiveChart
' If a chart is not active, just exit
If oChart Is Nothing Then
Exit Sub
End If
' Create the worksheet for storing data
Set oWorksheet = ActiveWorkbook.Worksheets.Add
oWorksheet.Name = oChart.Name & " Data"
' Loop through all series in the chart and write their values to the worksheet.
lSeriesCounter = 1
For Each oSeries In oChart.SeriesCollection
' Get the x and y values
xValues = oSeries.xValues
yValues = oSeries.values
' Calculate the number of rows of data.
lxNumberOfRows = UBound(xValues)
lyNumberOfRows = UBound(yValues)
' 1048576 is maximum number of rows in excel. Sometimes the Array is too big. Chop off the end.
If lxNumberOfRows >= 1048576 Then
lxNumberOfRows = 1048576 - 1
ReDim Preserve xValues(lxNumberOfRows)
End If
If lyNumberOfRows >= 1048576 Then
lyNumberOfRows = 1048576 - 1
ReDim Preserve yValues(lyNumberOfRows)
End If
With oWorksheet
' Put the name of the series at the top of each column
.Cells(1, 2 * lSeriesCounter - 1) = oSeries.Name & " X Values"
.Cells(1, 2 * lSeriesCounter) = oSeries.Name & " Y Values"
Set xDestination = .Range(.Cells(2, 2 * lSeriesCounter - 1), .Cells(lxNumberOfRows + 1, 2 * lSeriesCounter - 1))
Set yDestination = .Range(.Cells(2, 2 * lSeriesCounter), .Cells(lxNumberOfRows + 1, 2 * lSeriesCounter))
End With
' Arrays larger than 2^16 will fail with Transpose function. Therefore must manually transpose
If lxNumberOfRows > 2& ^ 16 Then
'Assign the x and y data from the chart to a range in the worksheet. Use the ManualTranspose for 2^16 or more elements.
xDestination.value = ManualTranspose(xValues)
yDestination.value = ManualTranspose(yValues)
Else
'Assign the x and y data from the chart to a range in the worksheet. Use the built-in Transpose for less than 2^16 elements.
xDestination.value = WorksheetFunction.Transpose(xValues)
yDestination.value = WorksheetFunction.Transpose(yValues)
End If
lSeriesCounter = lSeriesCounter + 1
Next
' Cleanup
Set oChart = Nothing
Set oWorksheet = Nothing
End Sub
' Helper function for when built-in Transpose function cannot be used. Arrays larger than 2^16 must be transposed manually.
Private Function ManualTranspose(ByRef arr As Variant) As Variant
Dim arrLength As Long
Dim i As Long
Dim TransposedArray() As Variant
arrLength = UBound(arr)
ReDim TransposedArray(arrLength, 1)
For i = 1 To arrLength
TransposedArray(i, 1) = arr(i)
Next i
ManualTranspose = TransposedArray
End Function
OK, I've got a straight-forward 2-d block of data in excel: row 1 and column 1 are labels, the rest are numbers. My task right now is to put the sum of each column in the first empty cell(row) underneath.
Whereas my practice dataset is of known dimensions, the actual datasets I'll be using this program on will have a variable number of rows and columns. To this end, I can't just say "=SUM(B2:B20)" because the last filled cell won't always be B20 (for example). The easiest way to total each column, I thought, would be a FOR..NEXT loop, but I just can't get VS to accept the summation formula. Here's what I've got so far:
`With xlWsheet2 'check for last filled row and column of transposed data'
If xlApp.WorksheetFunction.CountA(.Cells) <> 0 Then
lRow2 = .Cells.Find(What:="*",
After:=.Cells(1, 1),
LookAt:=Excel.XlLookAt.xlPart,
LookIn:=Excel.XlFindLookIn.xlFormulas,
SearchOrder:=Excel.XlSearchOrder.xlByRows,
SearchDirection:=Excel.XlSearchDirection.xlPrevious,
MatchCase:=False).Row
Else : lRow2 = 1
End If
If xlApp.WorksheetFunction.CountA(.Cells) <> 0 Then
lCol2 = .Cells.Find(What:="*",
After:=.Range("A1"),
LookAt:=Excel.XlLookAt.xlPart,
LookIn:=Excel.XlFindLookIn.xlFormulas,
SearchOrder:=Excel.XlSearchOrder.xlByRows,
SearchDirection:=Excel.XlSearchDirection.xlPrevious,
MatchCase:=False).Column
Else : lCol2 = 1
End If
lastcell2 = xlWsheet2.Cells(lRow2, lCol2) 'defines last row, column of transposed data'
emptyRow1 = xlWsheet2.Rows(lRow2).Offset(1) 'defines the first empty row'
'add in cell of SUM underneath each column'
For i As Integer = 2 To lCol2
colTop = xlWsheet2.Cells(2, i)
colBot = xlWsheet2.Cells(lRow2, i)
ELtotal = xlWsheet2.Range(emptyRow1, i)
ELtotal = .Sum(.Range(colTop, colBot))
Next i
End With
`
Now, the ELtotal statements used to be one long line, but I was trying to see what part VS had a problem with. It breaks at the first one, .Range(emptyRow1, i). Here's other iterations of that equation I've tried that weren't accepted:
.Range(emptyRow1, I).Formula = "=SUM(colTop, colBot)"
.Range(emptyRow1, I).Formula = "=SUM(.cells(2,i), (lRow2,i))"
.Range(emptyRow1, I).Formula = .sum(.range(colTop, colBot)
.Range(emptyRow1, I).Value = etc...
ad inifintum
PS- I'm pretty new to this, so I'm probably going about this whole process the wrong way...
Based on what you told me about the row and column headings, I believe that this code will do what you want, namely put a single column sum in the first empty cell underneath.
Sub find()
Dim lastrow As Long, lastcol As Long, thiscol As Long
lastrow = Cells(Rows.Count, 1).End(xlUp).Row
lastcol = Cells(1, Columns.Count).End(xlToLeft).Column
For thiscol = 2 To lastcol
Cells(lastrow + 1, thiscol).Select
ActiveCell.Value = WorksheetFunction.Sum(Range(Cells(1, ActiveCell.Column), ActiveCell))
Next
End Sub
Best of luck.
This formula will do the trick of summing two whole columns, A and B in this case:
= sum($A:$B)
If it is possible for the headers to be interpreted as numeric values that might contribute to the sum then the formula should be amended to be
= sum($A:$B) - sum($A$1:$B$1)
In order to export to excel with sum of all numeric columns from DataGridView, add a button to your form and add the following code in its click event:-
Private Sub Button4_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button4.Click
'--- Export to Excel -------------------------------------------------
Dim xlApp As Excel.Application
Dim xlWorkBook As Excel.Workbook
Dim xlWorkSheet As Excel.Worksheet
Dim misValue As Object = System.Reflection.Missing.Value
Dim i As Integer
Dim j As Integer
Dim datarange As Excel.Range
Dim save_file As New SaveFileDialog
'give its extension
''save_file.Filter = "xls files (*.xls)|*.xls|All files (*.*)|*.*"
'save_file.Filter = "xls files (*.xls)|*.xls"
save_file.Filter = "xls files (*.xls)|*.xls|Excel 2007|*.xlsx"
''Select xls
save_file.FilterIndex = 2
save_file.FileName = "My_excel_report_"
save_file.RestoreDirectory = True
Try
If save_file.ShowDialog() = DialogResult.OK Then
xlApp = New Excel.ApplicationClass
xlWorkBook = xlApp.Workbooks.Add(misValue)
xlWorkSheet = xlWorkBook.Sheets("sheet1")
For x = 0 To DataGridViewSummary1.ColumnCount - 1
xlWorkSheet.Cells(0 + 1, x + 1) = _
DataGridViewSummary1.Columns(x).HeaderText
Next
For i = 0 To DataGridViewSummary1.RowCount - 1
For j = 0 To DataGridViewSummary1.ColumnCount - 1
If IsDate(DataGridViewSummary1(j, i).Value) Then
'MsgBox("The cell value is date")
xlWorkSheet.Cells(i + 2, j + 1) = FormatDateTime(CDate(DataGridViewSummary1(j, i).Value.ToString), DateFormat.ShortDate)
xlWorkSheet.Cells(i + 2, j + 1).HorizontalAlignment = Excel.Constants.xlCenter
xlWorkSheet.Cells(i + 2, j + 1).VerticalAlignment = Excel.Constants.xlCenter
Else
xlWorkSheet.Cells(i + 2, j + 1) = _
DataGridViewSummary1(j, i).Value.ToString()
End If
Next
Next
datarange = xlWorkBook.ActiveSheet.UsedRange
datarange.Font.Name = "Consolas"
datarange.Font.Size = 10
'--- added on 07/09/2016 -------------------------------------------------------------------
Dim lastrow, lastcol As Long
With xlWorkSheet
lastcol = .Cells(1, .Columns.Count).End(Excel.XlDirection.xlToLeft).Column
lastrow = .Range("A" & .Rows.Count).End(Excel.XlDirection.xlUp).Row
End With
'MessageBox.Show("The last column in Sheet1 which has data is " & lastcol)
'MessageBox.Show("The last row in Col A of Sheet1 which has data is " & lastrow)
For i = 2 To lastcol
If IsNumeric(xlWorkSheet.Cells(lastrow, i).Value) Then
xlWorkSheet.Cells(lastrow + 1, i).Select()
xlWorkSheet.Cells(lastrow + 1, i).Value = xlApp.WorksheetFunction.Sum(xlWorkSheet.Range(xlWorkSheet.Cells(1, i), xlWorkSheet.Cells(lastrow + 1, i)))
End If
Next i
xlWorkSheet.Columns.AutoFit()
'----------------------------------------------------------------------------------------------
xlWorkSheet.SaveAs(save_file.FileName) 'sd.filename reurns save file dialog path
xlWorkBook.Close()
xlApp.Quit()
releaseObject(xlApp)
releaseObject(xlWorkBook)
releaseObject(xlWorkSheet)
'--------------------------------------
Dim proc As Process = Nothing
Dim startInfo As New ProcessStartInfo
startInfo.FileName = "EXCEL.EXE"
startInfo.Arguments = save_file.FileName
Process.Start(startInfo)
End If
Catch ex As Exception
MessageBox.Show(ex.ToString)
'GlobalErrorHandler(ex)
End Try
End Sub
I currently have an array which I populate and paste in a sheet named "T1" using a macro. My current macro uses the rowcount function to determine the used rows and pastes the array from the next available row.
The problem I am having is that when I paste this array multiple times, the arrays need to be spaced by a row so that i can differentiate different submissions. This is what I have so far, and I was hoping someone could help me with this:
Sub CopyData()
Dim Truearray() As String
Dim cell As Excel.Range
Dim RowCount1 As Integer
Dim i As Integer
Dim ii As Integer
Dim col As Range
Dim col2 As Range
i = 0
ii = 2
RowCount1 = DHRSheet.UsedRange.Rows.Count
Set col = DHRSheet.Range("I1:I" & RowCount1)
For Each cell In col
If cell.Value = "True" Then
Dim ValueCell As Range
Set ValueCell = Cells(cell.Row, 3)
ReDim Preserve Truearray(i)
Truearray(i) = ValueCell.Value
Dim siblingCell As Range
Set siblingCell = Cells(cell.Row, 2)
Dim Siblingarray() As String
ReDim Preserve Siblingarray(i)
Siblingarray(i) = DHRSheet.Name & "$" & siblingCell.Value
i = i + 1
End If
Next
Dim RowCount2 As Integer
RowCount2 = DataSheet.UsedRange.Rows.Count + 1
For ii = 2 To UBound(Truearray)
DataSheet.Cells(RowCount2 + ii, 2).Value = Truearray(ii)
Next
For ii = 2 To UBound(Siblingarray)
DataSheet.Cells(RowCount2 + ii, 1).Value = Siblingarray(ii)
Next
DataSheet.Columns("A:B").AutoFit
MsgBox ("Data entered has been successfully validated & logged")
End Sub
If you Offset two rows from the bottom cell, you will leave a blank row of separation. You should also consider filling the whole array as base 1 and writing it to DataSheet in one shot.
Sub CopyData2()
Dim rCell As Range
Dim aTrues() As Variant
Dim rRng As Range
Dim lCnt As Long
'Define the range to search
With DHRSheet
Set rRng = .Range(.Cells(1, 9), .Cells(.Rows.Count, 9).End(xlUp))
End With
'resize array to hold all the 'trues'
ReDim aTrues(1 To Application.WorksheetFunction.CountIf(rRng, "True"), 1 To 2)
For Each rCell In rRng.Cells
If rCell.Value = "True" Then
lCnt = lCnt + 1
'store the string from column 2
aTrues(lCnt, 1) = DHRSheet.Name & "$" & rCell.Offset(0, -7).Value
'store the value from column 3
aTrues(lCnt, 2) = rCell.Offset(0, -6).Value
End If
Next rCell
'offset 2 from the bottom row to leave a row of separation
With DataSheet.Cells(DataSheet.Rows.Count, 1).End(xlUp).Offset(2, 0)
'write the stored information at one time
.Resize(UBound(aTrues, 1), UBound(aTrues, 2)).Value = aTrues
End With
End Sub