I am new to VBA and I could use a little bit of help for a program which I am struggling with for the past 3 days.
I have lot of data in a text file arranged as 3 columns. This data has to be parsed in an excel
The column 1 corresponds to the time, column 2 the variable and column 3 the value corresponding the variable .
The excel should parse the data such a way that column 1 has time, and column 2,3,4,5,6,7 the values corresponding to the variables in column 2 of the text file. and the values are in hex datei which has to be converted to decimal.
here is the code
Sub OpenText()
Dim MyFile As Variant
Dim TempWb As Workbook
Dim DestSh As Worksheet
Dim i As Long, p As Long, LimitRow As Long
Dim LastRow As Long
Dim LastRow2 As Long
p = 2
' Ask the user for the file name to open.
MyFile = Application.GetOpenFilename()
' Check for the Cancel button.
If MyFile = False Then Exit Sub
Application.ScreenUpdating = False
Set DestSh = ThisWorkbook.ActiveSheet
'Open the Text file with the OpenText method.
Workbooks.OpenText Filename:=MyFile, Origin:=xlWindows, _
StartRow:=1, DataType:=xlDelimited, _
TextQualifier:=xlDoubleQuote, ConsecutiveDelimiter:=False, _
Tab:=False, Semicolon:=False, Comma:=False, Space:=False, _
Other:=True, OtherChar _
:=";", FieldInfo:=Array(Array(1, 1), Array(2, 1), Array(3, 1), Array(4, 1), Array(5, _
1), Array(6, 1), Array(7, 1)), _
DecimalSeparator:=".", ThousandsSeparator:=" ", TrailingMinusNumbers:= _
True
Set TempWb = ActiveWorkbook
LimitRow = 1048576 'Version Excel 2010
LastRow = Range("A" & LimitRow).End(xlUp).Row
If LastRow > 0 Then
For i = 1 To LastRow
If i = 1 Then
Cells(p, 1).Value = Cells(i, 1).Value
End If
Test = Cells(i, 2).Value
If Test = "0x005B" Then Cells(p, 2).Value = Cells(i, 3).Value Else _
If Test = "0x003E" Then Cells(p, 3).Value = Cells(i, 4).Value Else _
If Test = "0x0033" Then Cells(p, 4).Value = Cells(i, 3).Value Else _
If Test = "0x0039" Then Cells(p, 5).Value = Cells(i, 3).Value Else _
If Test = "0x003B" Then Cells(p, 6).Value = Cells(i, 3).Value Else _
If Test = "0x003D" Then Cells(p, 7).Value = Cells(i, 3).Value Else _
Next
End If
End Sub
The text file looks somewhat like this
2017-03-23_11-48-32.8;0x003E;0x1000
2017-03-23_11-48-32.8;0x0033;0x01F4
2017-03-23_11-48-32.8;0x0039;0x6720
2017-03-23_11-48-32.8;0x003B;0x6720
2017-03-23_11-48-32.8;0x003D;0x0050
2017-03-23_11-48-32.8;0x005E;0x1234ABCD
2017-03-23_11-48-33.1;0x0033;0x01F4
2017-03-23_11-48-33.1;0x0039;0x6720
2017-03-23_11-48-33.1;0x003B;0x6720
2017-03-23_11-48-33.4;0x003E;0x1000
2017-03-23_11-48-33.4;0x0033;0x01F4
2017-03-23_11-48-33.4;0x0039;0x6720
2017-03-23_11-48-33.4;0x003B;0x6720
2017-03-23_11-48-33.4;0x003D;0x0050
2017-03-23_11-48-33.4;0x005E;0x1234ABCD
2017-03-23_11-48-33.7;0x0033;0x01F4
2017-03-23_11-48-33.7;0x0039;0x6720
2017-03-23_11-48-34.0;0x003E;0x1000
2017-03-23_11-48-34.0;0x0033;0x01F4
2017-03-23_11-48-34.0;0x0039;0x6720
2017-03-23_11-48-34.0;0x003B;0x6720
2017-03-23_11-48-34.0;0x003D;0x0050
2017-03-23_11-48-34.0;0x005E;0x1234ABCD
2017-03-23_11-48-34.3;0x0033;0x01F4
2017-03-23_11-48-34.3;0x0039;0x6720
2017-03-23_11-48-34.3;0x003B;0x6720
2017-03-23_11-48-34.6;0x003E;0x1000
2017-03-23_11-48-34.6;0x0033;0x01F4
2017-03-23_11-48-34.6;0x0039;0x6720
2017-03-23_11-48-34.6;0x003B;0x6720
2017-03-23_11-48-34.6;0x003D;0x0050
2017-03-23_11-48-34.6;0x005E;0x1234ABCD
2017-03-23_11-48-34.9;0x0033;0x01F4
2017-03-23_11-48-34.9;0x0039;0x6720
2017-03-23_11-48-34.9;0x003B;0x6720
2017-03-23_11-48-35.2;0x003E;0x1000
2017-03-23_11-48-35.2;0x0033;0x01F4
2017-03-23_11-48-35.2;0x0039;0x6720
2017-03-23_11-48-35.2;0x003B;0x6720
2017-03-23_11-48-35.2;0x003D;0x0050
2017-03-23_11-48-35.2;0x005E;0x1234ABCD
2017-03-23_11-48-35.5;0x0033;0x01F4
2017-03-23_11-48-35.5;0x0039;0x6720
2017-03-23_11-48-35.5;0x003B;0x6720
And also the excel gets created in a different worksheet instead of the current worksheet.
Thanks in advance
Hope this resolve ur problem
Public Sub Append_text()
Set fso = New FileSystemObject
FLoc = "Y:\Macro\Test" & Format(Now(), "HHMMSS") & ".txt"
Set Stream = fso.OpenTextFile(FLoc, ForAppending, True)
x = 1 'Hoping the start point
Do Until Sheet1.Cells(x, 1) = "" 'U can use the end of file code here for looping till last row
Stream.Write Sheet1.Cells(x, 1) & ";" & Sheet1.Cells(x, 2) & ";" & Sheet1.Cells(x, 3) & vbNewLine
x = x + 1
Loop
End Sub
Public Sub Read_text()
Sheet2.Activate
Set fso = New FileSystemObject
Fname = Application.GetOpenFilename
x = 1
y = 1
Set Stream = fso.OpenTextFile(Fname, ForReading, True)
Do While Not Stream.AtEndOfStream
Str_text = Stream.ReadLine 'Perform your actions
rdtext = Split(Str_text, ";")
Sheet2.Cells(x, y) = rdtext(0)
Sheet2.Cells(x, y + 1) = rdtext(1)
Sheet2.Cells(x, y + 2) = rdtext(2)
x = x + 1
y = 1
Loop
Stream.Close
End Sub
Not directly on point to the problem but does answer Parsing Text file using VBA.
This is an auto-detect routine. You can add it to a customized tab by temporarily substituting a Sub line with no parameter and adding that. Then replace the Sub line with the real line with the optional parameters.
If you don't specify any of the Optional delimiters, this looks at the first 5 lines of the file and checks for common delimiters. For example, if any of those lines contains more than 8 pipes it assumes pipe is the delimiter.
BEWARE OF THE 'AUTOMATIC COMMA-SPLIT' ISSUE IN THE COMMENT. That is an EXCEL quirk, not a problem with .TextToColumns. Excel "remembers" choices previously made in the Data tab when someone used Get External Data or Data Tools > Text to Columns and might automatically re-perform that parsing when the file is opened.
Option Explicit
Sub Parse_any_delimited( _
Optional ByVal dlm_pipe As Boolean = False, _
Optional ByVal dlm_semi As Boolean = False, _
Optional ByVal dlm_comma As Boolean = False, _
Optional ByVal dlm_tab As Boolean = False, _
Optional ByVal dlm_carat As Boolean = False, _
Optional ByVal dlm_char As String = "", _
Optional ByVal no_delim_popup As Boolean = True)
' *** WARNING !!! ***
'
' The FIRST record that EXCEL will see DURING AN IMPORT CANNOT CONTAIN
' COMMAS! IF IT DOES, it interprets those as DELIMITERS and AUTOMATICALLY
' does a field split there BEFORE running any code! The result is that
' when all text SHOULD wind up in Cell A1, instead it gets parsed into
' cells at each comma. Then the REAL PARSE routine can only parse what
' IS in the Column A cells.
'
' The "comma parse" UPON LOADING occurs BEFORE any macro runs!
Dim i As Integer
Dim check_data As Boolean
check_data = False
Dim dlm_other As Boolean
dlm_other = False
Dim rcrd As Variant
Dim leave_for As Boolean
Dim have_delim As Boolean
'1 ****
If dlm_pipe Then
dlm_other = True
dlm_char = "|"
have_delim = True
'2 ****
ElseIf dlm_carat Then
dlm_other = True
dlm_char = "^"
have_delim = True
'3 ****
ElseIf dlm_tab Or dlm_semi Or dlm_comma Then
have_delim = True
'4 ****
Else
For i = 1 To 5 'Check first 5 records for common delimiters
leave_for = True
rcrd = Cells(i, "A").Value
If Count_Characters(rcrd, "|") > 5 Then
dlm_other = True
dlm_char = "|"
ElseIf Count_Characters(rcrd, ";") > 5 Then
dlm_semi = True
ElseIf Count_Characters(rcrd, ",") > 10 Then
dlm_comma = True
ElseIf Count_Characters(rcrd, vbTab) > 4 Then
dlm_tab = True
ElseIf Count_Characters(rcrd, "^") > 5 Then
dlm_other = True
dlm_char = "^"
Else
leave_for = False
End If
'===============
If leave_for Then
have_delim = True
Exit For
Else
have_delim = False
End If
Next i
'5 ****
End If
If have_delim = False Then
' B2 is checked because in certain cases Excel will
' AUTOMATICALLY parse data delimited by | or semicolons.
' When that happens, THIS sub sees it as "No delimiter Can't Parse"
' even though it HAS BEEN parsed.
If Cells(2, "B").Value = "" And no_delim_popup Then
MsgBox ("CAN'T PARSE - NO DELIMITER FOUND")
End If
Exit Sub
End If
' Stops "There's already data here--continue?"
Application.DisplayAlerts = False
Columns("A:A").Select
Selection.TextToColumns _
Destination:=Range("A1"), _
DataType:=xlDelimited, _
TextQualifier:=xlDoubleQuote, _
ConsecutiveDelimiter:=False, _
Tab:=dlm_tab, _
Semicolon:=dlm_semi, _
Comma:=dlm_comma, _
Space:=False, _
Other:=dlm_other, _
OtherChar:=dlm_char
Cells.Select
Selection.Columns.AutoFit
Range("A1").Select
Application.DisplayAlerts = True
' Sub Parse_any_delimited()
End Sub
Function Count_Characters( _
ByVal str As Variant, _
ByVal chr As Variant) _
As Long
Count_Characters = Len(str) - Len(Replace(str, chr, ""))
End Function
Related
What I want the Macro to accomplish:
I want the user to be able to fill in data from E2 to E9 on the spreadsheet. When the user presses the "Add Car" button the macro is supposed to be executed. The makro then should take the handwritten data, copy everything from E2:E9 and put it into a table that starts at with C13 and spans over 7 columns, always putting the new set of data in the next free row. It is also supposed to check for duplicates and give an alert while not overwriting the original set of data
So my problem is, that I want the Macro I'm writing to take the information put into certain cells and then copy them into a table underneath.
I'm starting the Macro like this
Sub addData()
Dim lastrow As Long, nextBlankRow As Long
lastrow = Cells.Find(What:="*", _
After:=Range("A1"), _
LookAt:=xlPart, _
LookIn:=xlFormulas, _
SearchOrder:=xlPrevious, _
MatchCase:=False).Row
nextBlankRow = lastrow + 1
Here I try to define how the Macro is supposed to find the last empty cell and also define lastrow and nextBlankRow.
After that I'm starting with a simple If statement to see if the person has at least something in E2 on the same sheet.
If Range("E2") = "" Then
MsgBox "Wählen Sie ein KFZ aus!"
Range("E2").Select
Exit Sub
End If
This works. When I'm not putting something into E2 I get the textbox with the alert.
Anyway if the IF-Statement is not triggered to exit the sub the Macro is given the instructions to get the information and put it in the table below
Cells(nextBlankRow, 3) = Range("E2")
Cells(nextBlankRow, 4) = Range("E3")
Cells(nextBlankRow, 5) = Range("E4")
Cells(nextBlankRow, 6) = Range("E5")
Cells(nextBlankRow, 7) = Range("E6")
Cells(nextBlankRow, 8) = Range("E7")
Cells(nextBlankRow, 9) = Range("E8")
Here seems to be a problem that probably relates to me failing to define variables correctly?
Because the Macro finds the right row but only overwrites into that row. So it ignores the fact that it "should" skip to the nextBlankrow which I defined earlier as
nextBlankRow = lastrow + 1
In addition to that I also have a line of code inplace which is supposed to check for duplicates
Dim p As Long, q As Long
p = 13
q = p + 1
Do While Cells(p, 3) <> ""
Do While Cells(q, 3) <> ""
If Cells(p, 3) = Cells(q, 3) And Cells(p, 4) = Cells(q, 4) Then
MsgBox "Datensatz schon vorhanden!"
Range(Cells(q, 3), Cells(q, 9)).ClearContents
Else
q = q + 1
End If
Loop
p = p + 1
q = p + 1
Loop
End Sub
Which always gives a false return. So even if the same set of Data is copied twice into the same row (as it does) it only "refreshes" the data and doesn't say "you're not allowed to do that".
I'm at a loss here.
Here's the full code for ease of use
Sub addData()
Dim lastrow As Long, nextBlankRow As Long
lastrow = Cells.Find(What:="*", _
After:=Range("A1"), _
LookAt:=xlPart, _
LookIn:=xlFormulas, _
SearchOrder:=xlPrevious, _
MatchCase:=False).Row
nextBlankRow = lastrow + 1
If Range("E2") = "" Then
MsgBox "Wählen Sie ein KFZ aus!"
Range("E2").Select
Exit Sub
End If
Cells(nextBlankRow, 3) = Range("E2")
Cells(nextBlankRow, 4) = Range("E3")
Cells(nextBlankRow, 5) = Range("E4")
Cells(nextBlankRow, 6) = Range("E5")
Cells(nextBlankRow, 7) = Range("E6")
Cells(nextBlankRow, 8) = Range("E7")
Cells(nextBlankRow, 9) = Range("E8")
Dim p As Long, q As Long
p = 13
q = p + 1
Do While Cells(p, 3) <> ""
Do While Cells(q, 3) <> ""
If Cells(p, 3) = Cells(q, 3) And Cells(p, 4) = Cells(q, 4) Then
MsgBox "Datensatz schon vorhanden!"
Range(Cells(q, 3), Cells(q, 9)).ClearContents
Else
q = q + 1
End If
Loop
p = p + 1
q = p + 1
Loop
End Sub
```![enter image description here](https://i.stack.imgur.com/dJozM.jpg)![enter image description here](https://i.stack.imgur.com/Q90Ah.jpg)
Please, test the next code:
Sub copyRangeOnLastEmptyRow()
Dim sh As Worksheet, arr, lastERow As Long, matchCel As Range
Set sh = ActiveSheet
arr = sh.Range("E2:E9").value
lastERow = sh.Range("C" & sh.rows.Count).End(xlUp).row + 1
If lastERow < 13 Then lastERow = 13
'check if the range has not been alredy copied:
Set matchCel = sh.Range("C13:C" & lastERow - 1).Find(WHAT:=sh.Range("E2").value, LookIn:=xlValues, Lookat:=xlWhole, MatchCase:=False)
If Not matchCel Is Nothing Then
MsgBox sh.Range("E2").value & " has been found in cell " & matchCel.Address & "."
'bring up the data of the existing row:
sh.Range("E3:E9").value = Application.Transpose(sh.Range(matchCel.Offset(0, 1), matchCel.Offset(0, 7)).value)
Exit Sub
End If
sh.Range("C" & lastERow).Resize(1, UBound(arr)).value = Application.Transpose(arr)
sh.Range("E2:E9").ClearContents
End Sub
I have a set of coordinates, which I would like to divide. I would like to have proper numbers with decimals, which doesn't happen in my worksheet, as I get messy data.
The first image shows the initial coordinates label. The second one shows the coordinates after split.
I need here the numbers with decimal.
I tried to divide them by the number, but it didn't work.
Sub Coordinatesfinal()
Columns("F:G").Insert Shift:=xlToRight
ActiveSheet.Range("E1").Value = "Latitude"
ActiveSheet.Range("F1").Value = "Longitude"
Dim rang As Range, cell As Range, rg As Range, element As Range, rg2 As Range
Dim r1 As Range, r2 As Range
Dim wors As Worksheet
Set wors = ActiveSheet
Dim myString As String
myString = "."
Dim LastRow As Long, i As Long, SecondLastRow As Long
LastRow = wors.Range("E" & wors.Rows.Count).End(xlUp).Row
Set rang = wors.Range("E2:E" & LastRow)
For Each cell In rang
cell = WorksheetFunction.Substitute(cell, ",", " ")
cell = WorksheetFunction.Substitute(cell, " ", " ")
cell = WorksheetFunction.Substitute(cell, ",,", " ")
Next
Set rg = [E2]
Set rg = Range(rg, Cells(Rows.Count, rg.Column).End(xlUp))
rg.TextToColumns Destination:=rg, DataType:=xlDelimited, _
TextQualifier:=xlDoubleQuote, ConsecutiveDelimiter:=False, Tab:=False, _
Semicolon:=False, Comma:=False, Space:=True, Other:=False, _
FieldInfo:=Array(Array(1, 1), Array(2, 1), Array(3, 1)), TrailingMinusNumbers:=True
If InStr(myString, ".") > 0 Then
Exit Sub
End If
With words
LastRow = .Cells(.Rows.Count, "E").End(xlUp).Row
SecondLastRow = .Cells(.Rows.Count, "F").End(xlUp).Row
End With
For Each element In wors.Range("E2:E" & LastRow)
cell.Value = cell.Value / 1000000
Next
For i = 2 To LastRow
Set r1 = Range("E" & i)
Set r2 = Range("F" & i)
If r1.Value > 54.5 Or r1.Value < 50 Then r1.Interior.Color = vbYellow
If r2.Value > 2 Or r2.Value < -7 Then r2.Interior.Color = vbCyan
'If r1.Value = 3 Then r2.Interior.Color = vbYellow
Next i
rg.TextToColumns Destination:=rg, DataType:=xlDelimited, _
TextQualifier:=xlDoubleQuote, ConsecutiveDelimiter:=False, Tab:=True, _
Semicolon:=False, Comma:=False, Space:=False, Other:=False, _
FieldInfo:=Array(Array(1, 1), Array(2, 1), Array(3, 1)), TrailingMinusNumbers:=True
MsgBox ("Coordinates prepared successfully")
End Sub
The
For Each element In wors.Range("E2:E" & LastRow)
cell.Value = cell.Value / 1000000
Next
VBA: Macro to divide range by a million
doesn't work at all, as same as:
For Each element In wors.Range("F2:F" & SecondLastRow)
If IsNumeric(element.Value) Then
If Len(element.Value) > 7 And Len(element.Value) < 9 Then
element.Value = element.Value / 1000000
ElseIf Len(element.Value) < 8 Then
element.Value = element.Value / 100000
Else
element.Value = element.Value / 10000000
End If
End If
Next
I don't know where might be the problem. As I might have several cases based on my total string length I would like to ask about some possibility of insert the "." symbol after 2nd character in my strings.
I tested this function:
Excel/VBA - How to insert a character in a string every N characters
but without any result.
Is there any way to divide these numbers or simply insert the "." symbol after 2nd number?
This is an output I would like to have
Something like this should work:
Sub Coordinatesfinal()
Dim ws As Worksheet, rngData As Range, arrIn, arrOut, r As Long, d, arr
Set ws = ActiveSheet
Set rngData = ws.Range("E2", ws.Cells(Rows.Count, "E").End(xlUp))
arrIn = rngData.Value 'get input data as array
ReDim arrOut(1 To UBound(arrIn, 1), 1 To 2) 'size array for output data
'clean raw value
For r = 1 To UBound(arrIn, 1)
d = Trim(Replace(arrIn(r, 1), ",", " ")) 'remove commas
Do While InStr(d, " ") > 0
d = Replace(d, " ", " ") 'remove any double spaces
Loop
arr = Split(d, " ") 'split on space
arrOut(r, 1) = FormatValue(arr(0)) 'Lat
If UBound(arr) > 0 Then arrOut(r, 2) = FormatValue(arr(1)) 'Long
Next r
ws.Columns("F:G").Insert Shift:=xlToRight
ws.Range("F1:G1").Value = Array("Latitude", "Longitude")
With ws.Range("F2").Resize(UBound(arrIn, 1), 2)
.NumberFormat = "General"
.Value = arrOut
End With
End Sub
'convert to decimal if numeric, according to length
Function FormatValue(ByVal v)
If IsNumeric(v) And InStr(v, ".") = 0 Then
v = CLng(v)
Select Case Len(v)
Case 8: FormatValue = v / 1000000
Case Is < 8: FormatValue = v / 100000
Case Else: FormatValue = v / 10000000
End Select
Else
FormatValue = v
End If
End Function
No a complete answer to the OP but the code below may help in returning an array of latitude and longitude doubles when provided with a composite string.
Public Const SPACE As String = " "
Public Const COMMA As String = ","
Public Enum Position
latitude
longitude
End Enum
Public Sub ttest()
Dim myArray As Variant
myArray = ConvertLatLongStringToLatLongDoubles(" 51519636 -1081282 ")
Debug.Print myArray(latitude)
Debug.Print myArray(longitude)
End Sub
' Return a variant containing an array of two doubles
' Index 0 is Latitude
' Index 1 is longitude
Public Function ConvertLatLongStringToLatLongDoubles(ByVal ipPosition As String) As Variant
' Clean up the incoming string
Dim myPosition As String
myPosition = Trimmer(ipPosition)
myPosition = Dedup(myPosition, SPACE)
' add other dedups as required as
' SPlit the string at the remaining SPACE
Dim myLatLong As Variant
myLatLong = Split(myPosition)
myLatLong(Position.latitude) = CDbl(myLatLong(Position.latitude)) / 1000000
myLatLong(Position.longitude) = CDbl(myLatLong(Position.longitude)) / 1000000
ConvertLatLongStringToLatLongDoubles = myLatLong
End Function
' Dedup replaces character pairs with a single character
' Dedup operates until no more pairs can be found.
' ipDedup should be a string (usually a single character)
' that need to be deduped
Public Function Dedup(ByVal ipSource As String, ByVal ipDedup As String) As String
Dim mySource As String
mySource = ipSource
Dim MyDedupDedup As String
MyDedupDedup = ipDedup & ipDedup
Do
Dim myLen As Long
myLen = Len(mySource)
mySource = Replace(mySource, MyDedupDedup, ipDedup)
Loop Until myLen = Len(mySource)
Dedup = mySource
End Function
' Trimmer will remove any single character specified in ipTrimChars
' from the start and end of the string
Public Function Trimmer(ByVal ipString As String, Optional ByVal ipTrimChars As String = " ,;" & vbCrLf & vbTab) As String
Dim myString As String
myString = ipString
Dim myIndex As Long
For myIndex = 1 To 2
If VBA.Len(myString) = 0 Then Exit For
Do While VBA.InStr(ipTrimChars, VBA.Left$(myString, 1)) > 0
DoEvents ' Always put a do event statement in a do loop
myString = VBA.Mid$(myString, 2)
Loop
myString = VBA.StrReverse(myString)
Next
Trimmer = myString
End Function
I wonder if you can help me modify this code ... it uses a dataset to look up against an indicator row to match field and complete a form for an indicator definition. This can be completed one or many times by modifying a column to yes or no.
It currently exports each indicator to a separate PDF - I would like the range of indicators selected combined as a single pdf - is this possible in the export? Possibly something like this to union print ranges?
Or if possible to paste each output as image to empty worksheet then export as pdf?
Sub print_selected_rows(inputData As Range, outputData As Range)
Dim data_columns, data_rows, filter_column, i, j
Dim ThisFile As Variant
data_rows = getArrayRows(inputData)
data_columns = getArrayColumns(inputData)
For i = 1 To data_columns
If (inputData.Cells(1, i).Value = "Select for report") Then
filter_column = i
Exit For
End If
Next
Sheets("Output").Visible = True
Sheets("Output").Select
For i = 1 To data_rows
If ((inputData.Cells(i, filter_column).Value = "yes") Or (inputData.Cells (i, filter_column).Value = "Yes") _
Or (inputData.Cells(i, filter_column).Value = "Y") Or (inputData.Cells(i, filter_column).Value = "y")) Then
'copy row data to output sheet
For j = 1 To data_columns
outputData.Cells(j, 3).Value = inputData.Cells(i, j).Value
Next
ThisFile = Application.GetSaveAsFilename( _
"abc" & " " & _
Range("selected_ID").Value, "PDF Files (*.pdf), *.pdf")
If VarType(ThisFile) = vbString Then
ActiveSheet.ExportAsFixedFormat Type:=xlTypePDF, Filename:=ThisFile, _
Quality:=xlQualityStandard, IncludeDocProperties:=True, _
IgnorePrintAreas:=False, OpenAfterPublish:=False
End If
End If
Next
Sheets("Introduction").Visible = True
Sheets("Introduction").Select
Sheets("Output").Visible = False
End Sub
I managed to work around this by copy and pasting as image to a new worksheet using the following code:
Sub print_selected_rows(inputData As Range, outputData As Range)
Dim data_columns, data_rows, filter_column, i, j, k
Dim ThisFile As Variant
k = 0
data_rows = getArrayRows(inputData)
data_columns = getArrayColumns(inputData)
For i = 1 To data_columns
If (inputData.Cells(1, i).Value = "Select for report") Then
filter_column = i
Exit For
End If
Next
Sheets("Output").Visible = True
Sheets("Output").Select
For i = 1 To data_rows
If ((inputData.Cells(i, filter_column).Value = "yes") Or (inputData.Cells (i, filter_column).Value = "Yes") _
Or (inputData.Cells(i, filter_column).Value = "Y") Or (inputData.Cells(i, filter_column).Value = "y")) Then
'copy row data to output sheet
For j = 1 To data_columns
outputData.Cells(j, 3).Value = inputData.Cells(i, j).Value
Next
With ThisFile
Sheets("Output").Range("A1:J55").CopyPicture xlScreen, xlBitmap
Worksheets("Sheet1").Paste _
Worksheets("Sheet1").Range("A1").Offset(k, 0)
k = k + 56
End With
End If
Next
Sheets("Introduction").Visible = True
Sheets("Introduction").Select
Sheets("Output").Visible = False
Sheets("Sheet1").Visible = False
End Sub
I keep getting runtime error 1004, on the following line:
originBook.Sheets(1).Range(Cells(1, 1), Cells(lastRow, lastCol)).Copy
Here's the full code
Sub Obtain_Source()
Application.DisplayAlerts = False
Dim theOrigin, theString, newCol As String
Dim lastRow, lastCol As Long
Dim theRange As Range
Dim originBook, originBookBackup, macroBook As Workbook
Dim originOpen As Boolean
originOpen = False
Set macroBook = Workbooks("FY_Macro_Testt (DYNAMIC).xlsm")
theOrigin = Application.GetOpenFilename(fileFilter:="Excel Files (*.xls; *.xlsm; *.xlsx), *.xls' *.xlsm' *.xlsx", _
Title:="Fiscal Year Selection: Select Only One", ButtonText:="Open", MultiSelect:=False)
If TypeName(theOrigin) = "Boolean" Then
MsgBox "Don't just stand there. Do something." & vbNewLine & _
"Quit hitting CANCEL. >.< ", vbExclamation, "WARNING. CHOKING HAZARD."
Else
originOpen = True
Set originBook = Workbooks.Open(theOrigin)
lastRow = Range("A65536").End(xlUp).Row
lastCol = Range("XFD1").End(xlToLeft).Column
lastCol = originBook.Sheets(1).Cells(1, Columns.Count).End(xlToLeft).Column
originBook.Sheets(2).Visible = False
originBook.Sheets(3).Visible = False
originBook.Sheets(1).Range(Cells(1, 1), Cells(lastRow, lastCol)).Copy
macroBook.Sheets(1).Cells(6, 1).PasteSpecial
i = 1000
Do While 1000 <= 20000
j = i - 999
If originBook.Sheets(1).Cells(i, 1).Value <> vbNullString Or _
originBook.Sheets(1).Cells(i, 1).Value <> "" Then
originBook.Sheets(1).Range(Cells(j, 1), Cells(i - 1, lastCol)).Copy
macroBook.Sheets(1).Cells(j + 5, 1).PasteSpecial
End If
i = i + 1000
Loop
originBook.Sheets(2).Visible = True
originBook.Sheets(3).Visible = True
End If
If originOpen = True Then
originBook.Close
End If
End Sub
which one should I change?
Your error will almost certainly be because you are using
originBook.Sheets(1).Range(Cells(1, 1), Cells(lastRow, lastCol)).Copy
instead of, as #ShaiRado pointed out,
originBook.Sheets(1).Range(originBook.Sheets(1).Cells(1, 1), _
originBook.Sheets(1).Cells(lastRow, lastCol)).Copy
When they are not fully qualified, Cells references refer to cells on the ActiveSheet. Excel is therefore having to try to copy all the cells on Sheets(1) that lie in the area between two cells on the ActiveSheet. It's equivalent to saying "choose all the houses in Los Angeles that lie in the area between the intersection of E 79th St and 1st Avenue and the intersection of E 86th St and York Ave New York". (Not living in the USA, I hope that analogy makes sense.)
I am updating a userform, and have added many more controls on separate tabs. I am getting ready to update my Initialize sub, and was wondering if there is a feature that will allow me to list and/or print all the control-objects on the form?
Having their other properties would be swell as well, since it would give me a map of what I need to set up, as well as use it as a checklist to make sure I complete everything that's needed. It would be more efficient to do that than run through them all, hope I have the right names and cell-references, wash/rinse/repeat.
Thanks
Sub ListControls()
Dim lCntr As Long
Dim aCtrls() As Variant
Dim ctlLoop As MSForms.Control
'Change UserForm Name In The Next Line
For Each ctlLoop In MyUserForm.Controls
lCntr = lCntr + 1: Redim Preserve aCtrls(1 To lCntr)
'Gets Type and name of Control
aCtrls(lCntr) = TypeName(ctlLoop)&":"&ctlLoop.Name
Next ctlLoop
'Change Worksheet Name In The Next Line
Worksheets("YrSheetName").Range("A1").Resize(UBound(aCtrls)).Value = Application.Transpose(aCtrls)
End Sub
This worked perfectly, adding all controls to a manually created sheet. Make sure to read comments and make changes required for individual projects.
Thanks to the folks at OzGrid who answered this question many moons ago. Lesson: keep trying different words in Google as long as you have options.
I recently had similar requirements and started with JSM's code above. With 350 controls nested within Frames and Multipages, I was having a difficult time tracing "where" each control sat within the UserForm.
The solution below stores the Control Object as a key in a dictionary and it's path as an Array of Control Objects for each ancestor. Dimming the dictionary as Public to be used in other parts of the module have helped for looping through the dictionary objects (and/or any parent objects) to find or change attributes of those objects (font, color, etc).
Creating or overwriting an existing worksheet is optional in case it is just necessary to update the dictionary. Sorting is based on Tab Index within Frames (and Index for Pages in a Multipage) and I opted to filter out Labels for the initial view.
Dimmed the following in another module so dictionary could be used elsewhere:
Public usrFm As Object
Public dPath As New Scripting.Dictionary
ex: Call DictUserFormControls("EditInvForm",True)
Public Sub DictUserFormControls(userFormName As String, Optional replaceSh As Boolean = False, Optional shName As String = "x_Controls")
Dim i As Long, a As Long, c As Long, pArrLen As Long
Dim cCont As Object, nCont As Object, pArr() As Object
Dim arrLen As Long, h As Long, pgs As Long
Dim pathName As String, tIndex As String, conType As String
Dim extArr As Variant
Set usrFm = VBA.UserForms.Add(userFormName)
If replaceSh = True Then
Dim wb As Workbook, sh As Worksheet, y As Long
Set wb = ActiveWorkbook
'Delete existing sheet if it exists
Application.DisplayAlerts = False
On Error Resume Next
wb.Sheets(shName).Delete
On Error GoTo 0
Application.DisplayAlerts = True
'Add a new worksheet
Set sh = wb.Worksheets.Add
sh.Name = shName
'Create headers and starting row
sh.Cells(1, 1).value = "Control"
sh.Cells(1, 2).value = "Type"
sh.Cells(1, 3).value = "Path"
y = 2
End If
'loop through all controls associated with UserForm. Find all parents and parents of parents until you reach an error (parent of UserForm)
'add each ancestor's Object to an array, and add the array to a dictionary with the Control Object as the key.
For Each cCont In usrFm.Controls
Set nCont = cCont.Parent
c = 1
a = a + 1
Do Until c = 0
i = i + 1: ReDim Preserve pArr(1 To i)
Set pArr(i) = nCont
dPath(cCont) = pArr
On Error GoTo ErrHandler
Set nCont = nCont.Parent
On Error GoTo 0
Loop
extArr = dPath(cCont)
arrLen = UBound(extArr) - LBound(extArr) + 1
'loop through dict item array backwards for each key to build path names from parent objects stored in array
For h = arrLen To 1 Step -1
'the last item in each array will be the root (with no index or tab index number)
If h = arrLen Then
pathName = extArr(h).Name
Else
'find tab index to help in sorting -- page numbers of multipages are stored as Index not TabIndex
If typeName(extArr(h)) = "Page" Then
tIndex = extArr(h).Index
Else
tIndex = extArr(h).TabIndex
End If
'concatenate 0 to help with sorting (otherwise 10, 11, 12 comes between 1 & 2)
If Len(tIndex) = 1 Then tIndex = "0" & tIndex
pathName = pathName & " | " & "{" & tIndex & "} " & extArr(h).Name
End If
Next h
'position of the control itself
tIndex = cCont.TabIndex
If Len(tIndex) = 1 Then tIndex = "0" & tIndex
pathName = pathName & " | {" & tIndex & "}"
If replaceSh = True Then
'populate rows
sh.Cells(y, 1).value = cCont.Name
'added special condition based on how I name my Labels that are used to display data: determine if "_LblData" is in cCont.Name, if so use LblData for typeName instead of actual typeName
If typeName(cCont) = "Label" And InStr(cCont.Name, "_LblData") <> 0 Then
sh.Cells(y, 2).value = "LabelData"
Else
sh.Cells(y, 2).value = typeName(cCont)
End If
sh.Cells(y, 3).value = pathName
y = y + 1
End If
i = 0
Next cCont
If replaceSh = True Then
Dim fullRng As Range, hdrRng As Range
Set fullRng = sh.Range(Cells(1, 1), Cells(y, 3))
Set hdrRng = sh.Range(Cells(1, 1), Cells(1, 3))
sh.Activate
'format sheet and sort
sh.Sort.SortFields.Clear
sh.Sort.SortFields.Add key:=Range( _
Cells(2, 3), Cells(y, 3)), SortOn:=xlSortOnValues, Order:=xlAscending, DataOption:= _
xlSortNormal
sh.Sort.SortFields.Add key:=Range( _
Cells(2, 2), Cells(y, 2)), SortOn:=xlSortOnValues, Order:=xlAscending, DataOption:= _
xlSortNormal
sh.Sort.SortFields.Add key:=Range( _
Cells(2, 1), Cells(y, 1)), SortOn:=xlSortOnValues, Order:=xlAscending, DataOption:= _
xlSortNormal
With sh.Sort
.SetRange Range(Cells(1, 1), Cells(y, 3))
.Header = xlYes
.MatchCase = False
.Orientation = xlTopToBottom
.SortMethod = xlPinYin
.Apply
End With
'autofit columns and show filters for header
fullRng.Columns.AutoFit
hdrRng.AutoFilter
'set initial view to display items that require coding
fullRng.AutoFilter Field:=2, Criteria1:=Array( _
"CheckBox", "ComboBox", "CommandButton", "LabelData", "OptionButton", "TextBox"), Operator:= _
xlFilterValues
End If
Exit Sub
ErrHandler:
'root reached
c = c - 1
Resume Next
End Sub
An example of the output is here:
output
col1: v1_Cmb_Name
col2: ComboBox
col3: EditInvForm | {07} tabs | {00} vndPg | {00}
vend_Frm | {00} v1_Frm | {01}
Considering 0 based index:
"v1_Cmb_Name" is a ComboBox that can be found in the UserForm > MultiPage (8th Tabbed element) > 1st Page within MultiPage > 1st Frame (vend_Frm) > 1st sub-frame (v1_Frm) > 2nd Control