I have 24 Textbox in a word Document as shown in the picture Below:
Which I am trying to populate using the content from each cell from the below range in a worksheet as shown below:
Three Rows at a time: Because there are 24 textboxes so 3 Rows and 8 Columns will have 24 Cells each time:
I would then Save it with a unique name and Make New from the next 3 Rows:
Code:
Option Explicit
Sub TransferData()
Dim FRow As Long, i As Long, j As Long
Dim wk As Worksheet, wt As Worksheet
Dim Path As String, Folder As String, File As String, CandName As String
Set wt = Sheet2 'Temp
Set wk = Sheet1 'Main
FRow = wk.Range("D" & Rows.Count).End(xlUp).Row
wt.Cells.Clear
wk.Range("D6:K" & FRow).Copy
wt.Activate
wt.Range("A1").Select
wt.Paste
Application.CutCopyMode = False
wt.Columns.AutoFit
FRow = wt.Range("A" & Rows.Count).End(xlUp).Row
wt.Range("$A$1:$H$" & FRow).RemoveDuplicates Columns:=Array(1, 2, 3, 4, 5, 6, 7, 8), Header:=xlYes
'----------Deduping is Done Now Transferring Data from eXcel to Word---------------
Path = Trim(wk.Range("A6").Text)
Folder = Trim(wk.Range("A10").Text)
File = Trim(wk.Range("A14").Text)
Dim Rng As Range
Dim r As Long, ct As Long, col As Long
Dim wdApp As Word.Application, wdDoc As Word.Document
On Error Resume Next
Set wdApp = GetObject(, "Word.Application")
If Err.Number <> 0 Then 'Word isn't already running
Set wdApp = CreateObject("Word.Application")
End If
On Error GoTo 0
Set wdDoc = wdApp.Documents.Open(Path & "\" & Folder & "\" & File)
With wt
FRow = .Range("A" & Rows.Count).End(xlUp).Row
Set Rng = .Range("A2:G" & FRow)
End With
With Rng
r = 2
Do
Set wdDoc = wdApp.Documents.Open(Path & "\" & Folder & "\" & File)
CandName = Trim(.Range("A" & r).Text)
col = 0
For i = 1 To 24
If i Mod 9 = 0 Then
r = r + 1
col = 1
Else
col = col + 1
End If
wdDoc.Shapes("Text Box " & i).TextFrame.TextRange.Text = .Cells(r, col).Value
Next i
ActiveDocument.SaveAs Filename:=Path & "\" & Folder & "\" & "New Files\" & "_" & CandName & r
Loop Until .Range("A" & r).Text <> ""
End With
End Sub
What I don't know:
How to rename a textbox (manually or by code) in the word document so that in can be used in the macro.
Save the word document and Create New Word Doc with 24 TextBoxes so that they can be filled again.
For your request I've modified your code.
I couldn't test it myself, since there are variables that are not accessible to me (Path, Folder) so if does not compile and work, just look and what I've done near the end and try to modify yourself.
basically, after 3 lines I've instructed to save the current file as new, and open the 24-blank-textboxes file again, which will be saved again after 3 lines etc'...
BTW, you mentioned you wanted to change the name of a textBox, but there is nothing about it in your code. If you want to do it you will need to write us more code about it.
Option Explicit
Sub TransferData()
Dim FRow As Long, i As Long, j As Long
Dim wk As Worksheet, wt As Worksheet
Dim Path As String, Folder As String, File As String, CandName As String
Set wt = Sheet2 'Temp
Set wk = Sheet1 'Main
FRow = wk.Range("D" & Rows.Count).End(xlUp).Row
wt.Cells.Clear
wk.Range("D6:K" & FRow).Copy
wt.Activate
wt.Range("A1").Select
wt.Paste
Application.CutCopyMode = False
wt.Columns.AutoFit
FRow = wt.Range("A" & Rows.Count).End(xlUp).Row
wt.Range("$A$1:$H$" & FRow).RemoveDuplicates Columns:=Array(1, 2, 3, 4, 5,
6, 7, 8), Header:=xlYes
'----------Deduping is Done Now Transferring Data from eXcel to Word-------
Path = Trim(wk.Range("A6").Text)
Folder = Trim(wk.Range("A10").Text)
File = Trim(wk.Range("A14").Text)
Dim Rng As Range
Dim r As Long, ct As Long, col As Long
Dim wdApp As Word.Application, wdDoc As Word.Document
On Error Resume Next
Set wdApp = GetObject(, "Word.Application")
If Err.Number <> 0 Then 'Word isn't already running
Set wdApp = CreateObject("Word.Application")
End If
On Error GoTo 0
Set wdDoc = wdApp.Documents.Open(Path & "\" & Folder & "\" & File)
With wt
FRow = .Range("A" & Rows.Count).End(xlUp).Row
Set Rng = .Range("A2:G" & FRow)
End With
With Rng
r = 2
Do
CandName = Trim(.Range("A" & r).Text)
col = 0
For i = 1 To 24
If i Mod 9 = 0 Then
r = r + 1
col = 1
Else
col = col + 1
End If
wdDoc.Shapes("Text Box " & i).TextFrame.TextRange.Text =_
.Cells(r, col).Value
Next i
if (r-2) mod 3 = 0 then
ActiveDocument.SaveAs Filename:=Path & "\" & Folder & "\" &_
"New Files\" & "_" & CandName & r
Set wdApp = Nothing
Set wdApp = GetObject(, "Word.Application")
If Err.Number <> 0 Then 'Word isn't already running
Set wdApp = CreateObject("Word.Application")
End If
Set wdDoc = wdApp.Documents.Open(Path & "\" & Folder & "\" &_
File)
end if
Loop Until .Range("A" & r).Text <> ""
End With
End Sub
Code to rename textBox1 as textBox2:
ActiveDocument.Shapes("Text Box 1").Select
ActiveDocument.Shapes("Text Box 1").Name = "Text Box 2"
without first selecting the textBox (or any other shape for that matter) , you can't modify its name.
You've already done that in the code, just re-use the line:
Set wdDoc = wdApp.Documents.Open(Path & "\" & Folder & "\" & File)
..to open a new file and to start over. Make sure you close the documents which you don't need anymore, or you'll end up with 24 open documents. I don't think you need that.
Related
I need a macro to split my data from one Excel file to few others. It looks like this:
UserList.xls
User Role Location
DDAVIS XX WW
DDAVIS XS WW
GROBERT XW WP
SJOBS XX AA
SJOBS XS AA
SJOBS XW AA
I need, to copy data like this:
WW_DDAVIS.xls
User Role
DDAVIS XX
DDAVIS XS
WP_GROBERT.xls
User Role
GROBERT XW
AA_SJOBS.xls
User Role
SJOBS XX
SJOBS XS
SJOBS XW
I need every user, to have his own file. The problem appeared when I was told that the files need to be filled using template (template.xls). Looks the same, but data in the source file starts in cell A2, and in the template file from cell A8.
To copy data without template I used this code:
Public Sub SplitToFiles()
' MACRO SplitToFiles
' Last update: 2012-03-04
' Author: mtone
' Version 1.1
' Description:
' Loops through a specified column, and split each distinct values into a separate file by making a copy and deleting rows below and above
'
' Note: Values in the column should be unique or sorted.
'
' The following cells are ignored when delimiting sections:
' - blank cells, or containing spaces only
' - same value repeated
' - cells containing "total"
'
' Files are saved in a "Split" subfolder from the location of the source workbook, and named after the section name.
Dim osh As Worksheet ' Original sheet
Dim iRow As Long ' Cursors
Dim iCol As Long
Dim iFirstRow As Long ' Constant
Dim iTotalRows As Long ' Constant
Dim iStartRow As Long ' Section delimiters
Dim iStopRow As Long
Dim sSectionName As String ' Section name (and filename)
Dim rCell As Range ' current cell
Dim owb As Workbook ' Original workbook
Dim sFilePath As String ' Constant
Dim iCount As Integer ' # of documents created
iCol = Application.InputBox("Enter the column number used for splitting", "Select column", 2, , , , , 1)
iRow = Application.InputBox("Enter the starting row number (to skip header)", "Select row", 5, , , , , 1)
iFirstRow = iRow
Set osh = Application.ActiveSheet
Set owb = Application.ActiveWorkbook
iTotalRows = osh.UsedRange.Rows.Count
sFilePath = Application.ActiveWorkbook.Path
If Dir(sFilePath + "\Split", vbDirectory) = "" Then
MkDir sFilePath + "\Split"
End If
'Turn Off Screen Updating Events
Application.EnableEvents = False
Application.ScreenUpdating = False
Do
' Get cell at cursor
Set rCell = osh.Cells(iRow, iCol)
sCell = Replace(rCell.Text, " ", "")
If sCell = "" Or (rCell.Text = sSectionName And iStartRow <> 0) Or InStr(1, rCell.Text, "total", vbTextCompare) <> 0 Then
' Skip condition met
Else
' Found new section
If iStartRow = 0 Then
' StartRow delimiter not set, meaning beginning a new section
sSectionName = rCell.Text
iStartRow = iRow
Else
' StartRow delimiter set, meaning we reached the end of a section
iStopRow = iRow - 1
' Pass variables to a separate sub to create and save the new worksheet
CopySheet osh, iFirstRow, iStartRow, iStopRow, iTotalRows, sFilePath, sSectionName, owb.fileFormat
iCount = iCount + 1
' Reset section delimiters
iStartRow = 0
iStopRow = 0
' Ready to continue loop
iRow = iRow - 1
End If
End If
' Continue until last row is reached
If iRow < iTotalRows Then
iRow = iRow + 1
Else
' Finished. Save the last section
iStopRow = iRow
CopySheet osh, iFirstRow, iStartRow, iStopRow, iTotalRows, sFilePath, sSectionName, owb.fileFormat
iCount = iCount + 1
' Exit
Exit Do
End If
Loop
'Turn On Screen Updating Events
Application.ScreenUpdating = True
Application.EnableEvents = True
MsgBox Str(iCount) + " documents saved in " + sFilePath
End Sub
Public Sub DeleteRows(targetSheet As Worksheet, RowFrom As Long, RowTo As Long)
Dim rngRange As Range
Set rngRange = Range(targetSheet.Cells(RowFrom, 1), targetSheet.Cells(RowTo, 1)).EntireRow
rngRange.Select
rngRange.Delete
End Sub
Public Sub CopySheet(osh As Worksheet, iFirstRow As Long, iStartRow As Long, iStopRow As Long, iTotalRows As Long, sFilePath As String, sSectionName As String, fileFormat As XlFileFormat)
Dim ash As Worksheet ' Copied sheet
Dim awb As Workbook ' New workbook
' Copy book
osh.Copy
Set ash = Application.ActiveSheet
' Delete Rows after section
If iTotalRows > iStopRow Then
DeleteRows ash, iStopRow + 1, iTotalRows
End If
' Delete Rows before section
If iStartRow > iFirstRow Then
DeleteRows ash, iFirstRow, iStartRow - 1
End If
' Select left-topmost cell
ash.Cells(1, 1).Select
' Clean up a few characters to prevent invalid filename
sSectionName = Replace(sSectionName, "/", " ")
sSectionName = Replace(sSectionName, "\", " ")
sSectionName = Replace(sSectionName, ":", " ")
sSectionName = Replace(sSectionName, "=", " ")
sSectionName = Replace(sSectionName, "*", " ")
sSectionName = Replace(sSectionName, ".", " ")
sSectionName = Replace(sSectionName, "?", " ")
' Save in same format as original workbook
ash.SaveAs sFilePath + "\Split\" + sSectionName, fileFormat
' Close
Set awb = ash.Parent
awb.Close SaveChanges:=False
End Sub
The problem in this one, is that I have no idea how to make name not DDAVIS.xls, but using WW_DDAVIS.xls (location_user.xls). Second problem - Use template. This code just copies whole workbook and erases all wrong data. All I need, is to copy value of the right data to this template.
Unfortunately I didn't find working code and I'm not so fluent in VBA to make it alone.
I tried other one, that worked only in half. It copied the template to every file and name it properly, but I couldn't figure out how to copy cells to the right files.
Option Explicit
Sub copyTemplate()
Dim lRow, x As Integer
Dim wbName As String
Dim fso As Variant
Dim dic As Variant
Dim colA As String
Dim colB As String
Dim colSep As String
Dim copyFile As String
Dim copyTo As String
Set dic = CreateObject("Scripting.Dictionary") 'dictionary to ensure that duplicates are not created
Set fso = CreateObject("Scripting.FileSystemObject") 'file scripting object for fiile system manipulation
colSep = "_" 'separater between values of col A and col B for file name
dic.Add colSep, vbNullString ' ensuring that we never create a file when both columns are blank in between
'get last used row in col A
lRow = Range("A" & Rows.Count).End(xlUp).Row
x = 1
copyFile = "c:\location\Template.xls" 'template file to copy
copyTo = "C:\location\List\" 'location where copied files need to be copied
Do
x = x + 1
colA = Range("G" & x).Value 'col a value
colB = Range("A" & x).Value ' col b value
wbName = colA & colSep & colB ' create new file name
If (Not dic.Exists(wbName)) Then 'ensure that we have not created this file name before
fso.copyFile copyFile, copyTo & wbName & ".xls" 'copy the file
dic.Add wbName, vbNullString 'add to dictionary that we have created this file
End If
Loop Until x = lRow
Set dic = Nothing ' clean up
Set fso = Nothing ' clean up
End Sub
sub test()
dim wb
dim temp
dim rloc
rloc= "result files location"
set wb =thisworkbook
set temp= workbook.open(template path)
' getting last row
lrow=wb.sheets(1).range("A1:A"&rows.count).end(xlup).row
icounter=0
for i=2 to lrow 'leaving out the header row
with wb.sheets(1)
if cells(i,1).value=cells(i,1).offset(1,1).value then
icounter=icounter+1
else
if icounter>0 then
range(cells(i,1):(cells(i,1).offset(-icounter,2)).copy
wb.sheet(8,1).pastespecial xlvalues
application.cutcopymode=false
filename=str(cells(i,1).value) & "_" & str(cells(i,3).value) & "".xls"
chdir rloc
temp.saveas(filename,xlworkbookdefault)
else
range(cells(i,1):cells(i,2)).copy
wb.sheets(8,1).pastespecial xlvalues
application.cutcopymode=false
filename=str(cells(i,1).value) & "_" & str(cells(i,3).value) & ".xls"
chdir rloc
temp.saveas(filename,xlworkbookdefault)
end if
end if
end with
next i
wb.close savechanges:=false
temp.close savechanges:=false
end sub
this might work. i haven't tested the code. its a bit crude. i am also just a beginner in vba. forgive me if it contains errors.
look at the logic. if its all you want create a code from scratch yourself.
#Sivaprasath V
Thanks, looks like it should work. I've changed it a little bit, to look better and to fix some issues
Sub test()
Dim wb
Dim temp
Dim rloc
rloc = "C:\LOCATION\result\"
Set wb = ThisWorkbook
Set temp = Workbooks.Open("C:\LOCATION\Template.xls")
' getting last row
lRow = wb.Sheets(1).Range("A1:A" & Rows.Count).End(xlDown).Row 'changed xlUp for xlDown
icounter = 0
For i = 2 To lRow 'leaving out the header row
With wb.Sheets(1)
Range("C2").Value = Cells(i, 1).Value
If Cells(i, 1).Value = Cells(i, 1).Offset(1, 0).Value Then 'changed offset from (1,1)
icounter = icounter + 1
Else
If icounter > 0 Then
Range(cells(i,1):(cells(i,1).offset(-icounter,7)).Copy 'error
wb.Sheet(8, 1).PasteSpecial xlValues
Application.CutCopyMode = False
Filename = Str(Cells(i, 1).Value) & "_" & Str(Cells(i, 3).Value) & ".xls"
ChDir rloc
temp.SaveAs Filename, xlWorkbookDefault
Else
Range(cells(i,1):cells(i,7)).Copy 'error
wb.Sheets(8, 1).PasteSpecial xlValues
Application.CutCopyMode = False
Filename = Str(Cells(i, 1).Value) & "_" & Str(Cells(i, 3).Value) & ".xls"
ChDir rloc
temp.SaveAs Filename, xlWorkbookDefault
End If
End If
End With
Next i
wb.Close savechanges:=False
temp.Close savechanges:=False
End Sub
I'm fighting with an error that i can't quite understand. In line:
Range(cells(i,1):(cells(i,1).offset(-icounter,7)).Copy
and this:
Range(cells(i,1):cells(i,7)).Copy
There is an error saying:
Compile error:
Expected: list separator or )
Can't figure out how to fix it. Code looks good for me.
#EDIT
Went around the error using new variable ("C" & i & ":" & "F" & i - icounter)
after some minor changes it worked, thanks :)
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 6 years ago.
Improve this question
I need to create a csv file from data set matrix, in which I have materials as a rows, people as a columns and quantity of products on intersection. Here is an example of this data set (Order id #1000):
Materials Person1 Person2
563718 20 40
837563 15 35
As a first action I have to transform this data set to a linear structure in this way on additional sheet:
Orderid Material Person Qty
1000 563718 Person1 20
1000 837563 Person1 15
1000 563718 Person2 40
1000 837563 Person2 35
And from this linear structure I have to generate a csv file with Orders for another system based on unique Persons from the list above. Each Order should have one header line and details based on the number of materials he/she ordered. General structure is the following:
H,1000-1,OUT,20160830,Person1
l,1000-1,1,563718,20,EA
l,1000-1,2,837563,15,EA
H,1000-2,OUT,20160830,Person2
l,1000-2,1,563718,40,EA
l,1000-2,2,837563,15,EA
where "H" - means Header row, "1000-1" - first Sub-Order of a Global order 1000, "20160830" requested delivery date, "l" - line row, "1" - line number, "EA" - unit of measure.
Here's a macro that will get you most of the way. It takes the data in your first table and organizes it so that your date in like columns (person1 and person2) is separated into separate rows:
This script assumes that your fixed column(s) are on the left and the columns to be combined (and split out into multiple rows) follow on the right. I hope this helps!
Option Explicit
Sub MatrixConverter2_3()
' Macro created 11/16/2005 by Peter T Oboyski (updated 8/24/2006)
'
' *** Substantial changes made by Chris Brackett (updated 8/3/2016) ***
'
' You are welcome to redistribute this macro, but if you make substantial
' changes to it, please indicate so in this section along with your name.
' This Macro converts matrix-type spreadsheets (eg. plot x species data) into column data
' The new (converted) spreadsheet name is "DB of 'name of active spreadsheet'"
' The conversion allows for multiple header rows and columns.
'--------------------------------------------------
' This section declares variables for use in the script
Dim book, head, cels, mtrx, dbase, v, UserReady, columnsToCombine, RowName, DefaultRowName, DefaultColName1, DefaultColName2, ColName As String
Dim defaultHeaderRows, defaultHeaderColumns, c, r, selectionCols, ro, col, newro, newcol, rotot, coltot, all, rowz, colz, tot As Long
Dim headers(100) As Variant
Dim dun As Boolean
'--------------------------------------------------
' This section sets the script defaults
defaultHeaderRows = 1
defaultHeaderColumns = 2
DefaultRowName = "Activity"
'--------------------------------------------------
' This section asks about data types, row headers, and column headers
UserReady = MsgBox("Have you selected the entire data set (not the column headers) to be converted?", vbYesNoCancel)
If UserReady = vbNo Or UserReady = vbCancel Then GoTo EndMatrixMacro
all = MsgBox("Exclude zeros and empty cells?", vbYesNoCancel)
If all = vbCancel Then GoTo EndMatrixMacro
' UN-COMMENT THIS SECTION TO ALLOW FOR MULTIPLE HEADER ROWS
rowz = 1
' rowz = InputBox("How many HEADER ROWS?" & vbNewLine & vbNewLine & "(Usually 1)", "Header Rows & Columns", defaultHeaderRows)
' If rowz = vbNullString Then GoTo EndMatrixMacro
colz = InputBox("How many HEADER COLUMNS?" & vbNewLine & vbNewLine & "(These are the columns on the left side of your data set to preserve as is.)", "Header Rows & Columns", defaultHeaderColumns)
If colz = vbNullString Then GoTo EndMatrixMacro
'--------------------------------------------------
' This section allows the user to provide field (column) names for the new spreadsheet
selectionCols = Selection.Columns.Count ' get the number of columns in the selection
For r = 1 To selectionCols
headers(r) = Selection.Cells(1, r).Offset(rowOffset:=-1, columnOffset:=0).Value ' save the column headers to use as defaults for user provided names
Next r
colz = colz * 1
columnsToCombine = "'" & Selection.Cells(1, colz + 1).Offset(rowOffset:=-1, columnOffset:=0).Value & "' to '" & Selection.Cells(1, selectionCols).Offset(rowOffset:=-1, columnOffset:=0).Value & "'"
Dim Arr(20) As Variant
newcol = 1
For r = 1 To rowz
If r = 1 Then RowName = DefaultRowName
Arr(newcol) = InputBox("Field name for the fields/columns to be combined" & vbNewLine & vbNewLine & columnsToCombine, , RowName)
If Arr(newcol) = vbNullString Then GoTo EndMatrixMacro
newcol = newcol + 1
Next
For c = 1 To colz
ColName = headers(c)
Arr(newcol) = InputBox("Field name for column " & c, , ColName)
If Arr(newcol) = vbNullString Then GoTo EndMatrixMacro
newcol = newcol + 1
Next
Arr(newcol) = "Data"
v = newcol
'--------------------------------------------------
' This section creates the new spreadsheet, names it, and color codes the new worksheet tab
mtrx = ActiveSheet.Name
Sheets.Add After:=ActiveSheet
dbase = "DB of " & mtrx
'--------------------------------------------------
' If the proposed worksheet name is longer than 28 characters, truncate it to 29 characters.
If Len(dbase) > 28 Then dbase = Left(dbase, 28)
'--------------------------------------------------
' This section checks if the proposed worksheet name
' already exists and appends adds a sequential number
' to the name
Dim sheetExists As Variant
Dim Sheet As Worksheet
Dim iName As Integer
Dim dbaseOld As String
dbaseOld = dbase ' save the original proposed name of the new worksheet
iName = 0
sheetExists = False
CheckWorksheetNames:
For Each Sheet In Worksheets ' loop through every worksheet in the workbook
If dbase = Sheet.Name Then
sheetExists = True
iName = iName + 1
dbase = Left(dbase, Len(dbase) - 1) & " " & iName
GoTo CheckWorksheetNames
' Exit For
End If
Next Sheet
'--------------------------------------------------
' This section notify the user if the proposed
' worksheet name is already being used and the new
' worksheet was given an alternate name
If sheetExists = True Then
MsgBox "The worksheet '" & dbaseOld & "' already exists. Renaming to '" & dbase & "'."
End If
'--------------------------------------------------
' This section creates and names a new worksheet
On Error Resume Next 'Ignore errors
If Sheets("" & Range(dbase) & "") Is Nothing Then ' If the worksheet name doesn't exist
ActiveSheet.Name = dbase ' Rename newly created worksheet
Else
MsgBox "Cannot name the worksheet '" & dbase & "'. A worksheet with that name already exists."
GoTo EndMatrixMacro
End If
On Error GoTo 0 ' Resume normal error handling
Sheets(dbase).Tab.ColorIndex = 41 ' color the worksheet tab
'--------------------------------------------------
' This section turns off screen and calculation updates so that the script
' can run faster. Updates are turned back on at the end of the script.
Application.Calculation = xlCalculationManual
Application.ScreenUpdating = False
'--------------------------------------------------
'This section determines how many rows and columns the matrix has
dun = False
rotot = rowz + 1
Do
If (Sheets(mtrx).Cells(rotot, 1) > 0) Then
rotot = rotot + 1
Else
dun = True
End If
Loop Until dun
rotot = rotot - 1
dun = False
coltot = colz + 1
Do
If (Sheets(mtrx).Cells(1, coltot) > 0) Then
coltot = coltot + 1
Else
dun = True
End If
Loop Until dun
coltot = coltot - 1
'--------------------------------------------------
'This section writes the new field names to the new spreadsheet
For newcol = 1 To v
Sheets(dbase).Cells(1, newcol) = Arr(newcol)
Next
'--------------------------------------------------
'This section actually does the conversion
tot = 0
newro = 2
For col = (colz + 1) To coltot
For ro = (rowz + 1) To rotot 'the next line determines if data are nonzero
If ((Sheets(mtrx).Cells(ro, col) <> 0) Or (all <> 6)) Then 'DCB modified ">0" to be "<>0" to exclude blank and zero cells
tot = tot + 1
newcol = 1
For r = 1 To rowz 'the next line copies the row headers
Sheets(dbase).Cells(newro, newcol) = Sheets(mtrx).Cells(r, col)
newcol = newcol + 1
Next
For c = 1 To colz 'the next line copies the column headers
Sheets(dbase).Cells(newro, newcol) = Sheets(mtrx).Cells(ro, c)
newcol = newcol + 1
Next 'the next line copies the data
Sheets(dbase).Cells(newro, newcol) = Sheets(mtrx).Cells(ro, col)
newro = newro + 1
End If
Next
Next
'--------------------------------------------------
'This section displays a message box with information about the conversion
book = "Original matrix = " & ActiveWorkbook.Name & ": " & mtrx & Chr(10)
head = "Matrix with " & rowz & " row headers and " & colz & " column headers" & Chr(10)
cels = tot & " cells of " & ((rotot - rowz) * (coltot - colz)) & " with data"
'--------------------------------------------------
' This section turns screen and calculation updates back ON.
Application.Calculation = xlCalculationAutomatic
Application.ScreenUpdating = True
MsgBox (book & head & cels)
'--------------------------------------------------
' This is an end point for the macro
EndMatrixMacro:
End Sub
Thanks #ChrisB for your answer. Actually I decided to do it my own way and here are the main steps I did:
I created an Excel file with several buttons to which I assigned below Subroutines. Also I have added some parameters, which user can modify (OrderId, Delivery Date and WH id).
I created a Subroutine ReadData(), which clears the Sheet "DATA" in original file and after reads column by column in the data file and generates a linear data set with all required fields on "DATA" Sheet.
After that I simply writes "DATA" sheet to external csv file.
the final code looks like this:
Global Const DAODBEngine = "DAO.DBEngine.36"
Global intColBeg As Integer 'Column Index with Data set to analyze
Global intRowBeg As Integer 'Row Index with Data set to analyze
Sub FileOpen()
Dim filePath As String
filePath = Application.GetOpenFilename()
If filePath = "False" Then Exit Sub
ThisWorkbook.Sheets("BASE").Cells(4, 3) = filePath
End Sub
Sub ClearData()
' Check if DATA Sheet exists
If Evaluate("ISREF('" & "DATA" & "'!A1)") Then
Application.DisplayAlerts = False
ThisWorkbook.Sheets("DATA").Delete
Application.DisplayAlerts = True
End If
Dim sheet As Worksheet
ThisWorkbook.Sheets.Add(After:=ThisWorkbook.Sheets(ThisWorkbook.Sheets.Count)).Name = "DATA"
End Sub
' This function reads data and adds it to DATA Sheet
Sub ReadData()
Dim i As Integer, l As Integer
Dim intColumn As Integer, intRow As Integer
Dim intAddRow As Integer
Dim wbCopyFrom As Workbook
Dim wbCopyTo As Workbook
Dim wsCopyFrom As Worksheet
Dim wsCopyTo As Worksheet
Dim dataLoc As String, wbLoc As String
Dim mandant As String
Dim orderId As String
Dim orderNum As Integer
Dim shipDate As Date
dataLoc = Trim(ThisWorkbook.Sheets("BASE").Cells(4, 3).Text)
Set wbCopyFrom = Workbooks.Open(dataLoc)
Set wsCopyFrom = wbCopyFrom.Worksheets(1)
ThisWorkbook.Activate
Call ClearData ' Clears all the data on DATA Sheet
Set wbCopyTo = ThisWorkbook
Set wsCopyTo = wbCopyTo.Sheets("DATA")
wbCopyTo.Activate
mandant = wbCopyTo.Sheets("BASE").Cells(11, 3).Text
orderId = wbCopyTo.Sheets("BASE").Cells(7, 3).Text
shipDate = wbCopyTo.Sheets("BASE").Cells(9, 3).Text
' Initial upper left row/column where matrix data begins
intColBeg = 4
intRowBeg = 4
intColumn = intColBeg
intRow = intRowBeg
intAddRow = 1 ' We will add data from this row
orderNum = 1
While Trim(wsCopyFrom.Cells(intRowBeg - 1, intColumn).Text) <> ""
' Header of an Order
wsCopyTo.Cells(intAddRow, 1) = "H;OUT;" & mandant & ";" & orderId & "/" & orderNum & ";" & _
";;" & Mid(shipDate, 7, 4) & Mid(shipDate, 4, 2) & Mid(shipDate, 1, 2) & ";" & _
Trim(wsCopyFrom.Cells(3, intColumn).Text) & ";" & Trim(wsCopyFrom.Cells(2, intColumn).Text) & _
";;;;;;;999;;"
Dim r As Integer
r = 1
intAddRow = intAddRow + 1
While Trim(wsCopyFrom.Cells(intRow, intColBeg - 1).Text) <> ""
If (Trim(wsCopyFrom.Cells(intRow, intColumn).Text) <> "") Then
If Round(CDbl(Trim(wsCopyFrom.Cells(intRow, intColumn).Value)), 0) > 0 Then
' Rows of an Order
wsCopyTo.Cells(intAddRow, 1) = "I;" & orderId & "/" & orderNum & ";" & r & ";" & _
Trim(wsCopyFrom.Cells(intRow, 1).Text) & ";" & Trim(wsCopyFrom.Cells(intRow, intColumn).Value) & _
";PCE;;;;;;;;;;;;;;;"
r = r + 1
intAddRow = intAddRow + 1
End If
End If
intRow = intRow + 1
Wend
intRow = intRowBeg
intColumn = intColumn + 1
orderNum = orderNum + 1
Wend
wbCopyFrom.Close
wbCopyTo.Sheets("BASE").Activate
End Sub
Sub Export()
Dim MyPath As String
Dim MyFileName As String
MyFileName = "Orders_" & Sheets("BASE").Cells(7, 3).Text & "_" & Format(Date, "ddmmyyyy")
If Not Right(MyFileName, 4) = ".csv" Then MyFileName = MyFileName & ".csv"
Sheets("DATA").Copy
With Application.FileDialog(msoFileDialogFolderPicker)
.Title = "Select a Folder"
.AllowMultiSelect = False
.InitialFileName = "" '<~~ The start folder path for the file picker.
If .Show <> -1 Then GoTo NextCode
MyPath = .SelectedItems(1) & "\"
End With
NextCode:
If MyPath <> "" Then
Application.DisplayAlerts = False
With ActiveWorkbook
.SaveAs fileName:=MyPath & MyFileName, AccessMode:=xlExclusive, FileFormat:=xlCSV, CreateBackup:=False, ConflictResolution:=Excel.XlSaveConflictResolution.xlLocalSessionChanges
.Close False
End With
Application.DisplayAlerts = True
Else
On Error Resume Next
ActiveWorkbook.Close SaveChanges:=False
If Err.Number = 1004 Then
On Error GoTo 0
End If
End If
End Sub
I believe that this code isn't optimal as I don't have any experience in VBA and it was a method of trying/changing/trying again in debugging mode and googling in case of issues.
If you can provide any suggestion how to optimise it - that would be great!
INITIAL QUESTION
Why am I not able to open all (all three) matching workbooks?
Dropdown selection:
1A:1C = Company1 Company2 Company3
2A:2C = Version2 Version1 Version1
Only the first one (Company1, Version2) will open...
Sub OpenWorkbooks()
Dim ColumnIndex1 As Integer
Dim ColumnIndex2 As Integer
Dim ColumnIndex3 As Integer
Dim ColumnIndex4 As Integer
Dim ColumnIndex5 As Integer
Dim ColumnIndex6 As Integer
For ColumnIndex1 = 1 To 3
If Cells(1, ColumnIndex1).Value = "Company1" And Cells(2,
ColumnIndex1).Value = "Version1" Then
Workbooks.Open Filename:="D:\Company1\Version1.xlsx"
End If
Next ColumnIndex1
For ColumnIndex2 = 1 To 3
If Cells(1, ColumnIndex2).Value = "Company1" And Cells(2,
ColumnIndex2).Value = "Version2" Then
Workbooks.Open Filename:="D:\Company1\Version2.xlsx"
End If
Next ColumnIndex2
For ColumnIndex3 = 1 To 3
If Cells(1, ColumnIndex3).Value = "Company2" And Cells(2,
ColumnIndex3).Value = "Version1" Then
Workbooks.Open Filename:="D:\Company2\Version1.xlsx"
End If
Next ColumnIndex3
For ColumnIndex4 = 1 To 3
If Cells(1, ColumnIndex4).Value = "Company2" And Cells(2,
ColumnIndex4).Value = "Version2" Then
Workbooks.Open Filename:="D:\Company2\Version2.xlsx"
End If
Next ColumnIndex4
For ColumnIndex5 = 1 To 3
If Cells(1, ColumnIndex5).Value = "Company3" And Cells(2,
ColumnIndex5).Value = "Version1" Then
Workbooks.Open Filename:="D:\Company3\Version1.xlsx"
End If
Next ColumnIndex5
For ColumnIndex6 = 1 To 3
If Cells(1, ColumnIndex6).Value = "Company3" And Cells(2,
ColumnIndex6).Value = "Version2" Then
Workbooks.Open Filename:="D:\Company3\Version2.xlsx"
End If
Next ColumnIndex6
End Sub
I have just started using VBA (and StackOverflow).
Thank you.
FOLLOW-UP
# Dirk Reichel:
# All:
I have tried to expand Dirk's idea a bit (See below), and I'm trying to open 5 (or less) workbooks in sequence each time copying/pasting a specific range to the 'main2' sheet of the 'main' workbook.
It works fine unless I open fewer workbooks than the number of dropdown values that are being checked (I'm currently using 5 dropdown sets instead of the original 3: see top of page):
Sub ImportData()
Dim MainWorkbook As Workbook
Dim DataWorkbook As Workbook
Dim i As Long
Set MainWorkbook = ThisWorkbook
With MainWorkbook.ActiveSheet
For i = 2 To 6
If ActiveSheet.Cells(6, i).Value <> "" Then
Set DataWorkbook = Workbooks.Open("D:\ 'some folders' \" & .Cells(6,
i).Value & "-" & .Cells(10, 2) & "-" & .Cells(7, i).Value & ".xlsx")
DataWorkbook.Sheets("Sheet1").Range("C3:Q3").Copy
MainWorkbook.Sheets("Main2").Range("A" & i).PasteSpecial
On Error Resume Next
End If
Next i
End With
End Sub
I have used 3 of the (now) 5 dropdown menus, and only 1 workbook is currently being opened and copied...
You may try an easier script like this:
Sub OpenWorkbooks()
Dim i As Long
With ThisWorkbook.ActiveSheet
For i = 1 To 3
Workbooks.Open Filename:="D:\" & .Cells(1, i).Value & "\" & .Cells(2, i).Value & ".xlsx"
Next i
End With
End Sub
if your Cells do not have any "Workbook" and "Worksheet" they will do it with the active one (after opening the first workbook, all your Cells will refer to it and not to the orginal source)
I currently have a macro which goes through a column on my master spreadsheet, then exports all the rows where the value input at the start matches the value in the column. It then saves the new worksheet as the value. Here is the code I currently have:
Option Explicit
Public Const l_HeaderRow As Long = 2 'The header row of the data sheet
Public Const l_DistanceCol As Long = 5 'The column containing the distance values
Public Sub ExportDistance()
Dim ws_Data As Worksheet, wb_Export As Workbook, ws_Export As Worksheet
Dim l_InputRow As Long, l_OutputRow As Long
Dim l_LastCol As Long
Dim l_NumberOfMatches As Long
Dim s_Distance As String, l_Distance As Long
Dim s_ExportPath As String, s_ExportFile As String, s_PathDelimiter As String
Set ws_Data = ActiveSheet
s_Distance = InputBox("Enter Distance to Export to New File", "Enter Distance")
If s_Distance = "" Then Exit Sub
l_Distance = CLng(s_Distance)
l_NumberOfMatches = WorksheetFunction.Match(l_Distance, ws_Data.Columns(5), 0)
If l_NumberOfMatches <= 0 Then Exit Sub
'Application.ScreenUpdating = False
'Application.Calculation = xlCalculationManual
Application.DisplayAlerts = False
On Error Resume Next
Call Application.Workbooks.Add
Set wb_Export = Application.Workbooks(Application.Workbooks.Count)
Set ws_Export = wb_Export.Worksheets(1)
Call wb_Export.Worksheets("Sheet2").Delete
Call wb_Export.Worksheets("Sheet3").Delete
Application.DisplayAlerts = True
ws_Export.Name = GetNextSheetname(ws_Data.Name & "-" & s_Distance, wb_Export)
Call ws_Data.Rows(1).Resize(l_HeaderRow).Copy
Call ws_Export.Rows(1).Resize(l_HeaderRow).Select
Call ws_Export.Paste
l_OutputRow = l_HeaderRow + 1
l_LastCol = ws_Data.UsedRange.Columns.Count
For l_InputRow = l_HeaderRow + 1 To ws_Data.UsedRange.Rows.Count
If ws_Data.Cells(l_InputRow, l_DistanceCol).Value = l_Distance Then
Call ws_Data.Range(ws_Data.Cells(l_InputRow, 1), ws_Data.Cells(l_InputRow, l_LastCol)).Copy
Call ws_Export.Rows(l_OutputRow).Select
Call ws_Export.Paste
l_OutputRow = l_OutputRow + 1
ElseIf ws_Data.Cells(l_InputRow, l_DistanceCol).Value = l_Distance Then
Call ws_Data.Range(ws_Data.Cells(l_InputRow, 1), ws_Data.Cells(l_InputRow, l_LastCol)).Copy
Call ws_Export.Rows(l_OutputRow).Select
Call ws_Export.Paste
l_OutputRow = l_OutputRow + 1
End If
Next l_InputRow
s_ExportPath = ThisWorkbook.Path
s_PathDelimiter = Application.PathSeparator
If Right(s_ExportPath, 1) <> s_PathDelimiter Then s_ExportPath = s_ExportPath & s_PathDelimiter
s_ExportPath = s_ExportPath & "Output" & s_PathDelimiter
If Dir(s_ExportPath) = Empty Then
Call MkDir(s_ExportPath)
End If
Select Case Application.DefaultSaveFormat
Case xlOpenXMLWorkbook
s_ExportFile = s_Distance & ".xlsx"
Case xlOpenXMLWorkbookMacroEnabled
s_ExportFile = s_Distance & ".xlsm"
Case xlExcel12
s_ExportFile = s_Distance & ".xlsb"
Case xlExcel8
s_ExportFile = s_Distance & ".xls"
Case xlCSV
s_ExportFile = s_Distance & ".csv"
Case Else
s_ExportFile = s_Distance
End Select
Call wb_Export.SaveAs(Filename:=s_ExportPath & s_ExportFile, FileFormat:=Application.DefaultSaveFormat)
Application.Calculation = xlCalculationAutomatic
Application.ScreenUpdating = True
End Sub
Public Function GetNextSheetname(s_Name As String, Optional wb_Book As Workbook) As String
Dim l_FIndex As Long
Dim s_Target As String
If wb_Book Is Nothing Then Set wb_Book = ActiveWorkbook
s_Name = Left(s_Name, 31)
If IsValidSheet(wb_Book, s_Name) Then
l_FIndex = 1
s_Target = Left(s_Name, 27) & " (" & l_FIndex & ")"
Do While IsValidSheet(wb_Book, s_Target)
l_FIndex = l_FIndex + 1
If l_FIndex < 10 Then
s_Target = Left(s_Name, 27) & " (" & l_FIndex & ")"
ElseIf l_FIndex < 100 Then
s_Target = Left(s_Name, 26) & " (" & l_FIndex & ")"
ElseIf l_FIndex < 1000 Then
s_Target = Left(s_Name, 25) & " (" & l_FIndex & ")"
End If
Loop
GetNextSheetname = s_Target
Else
GetNextSheetname = s_Name
End If
End Function
Public Function IsValidSheet(wbSearchBook As Workbook, v_TestIndex As Variant) As Boolean
Dim v_Index As Variant
On Error GoTo ExitLine
v_Index = wbSearchBook.Worksheets(v_TestIndex).Name
IsValidSheet = True
Exit Function
ExitLine:
IsValidSheet = False
End Function
Please will you help me make this loop through a list of values, rather than my having manually to run the macro each time and input the value myself?
Download this example here.
This is a simple example of how to loop through one range and loop through another range to find the values.
It loops through Column D and then loops through column A, when it finds a match it does something, so basically Column D has taken the place of your inputbox.
run the macro
The code
Sub DblLoop()
Dim aLp As Range 'column A
Dim dLp As Range, dRw As Long 'column D
Dim d As Range, a As Range
Set aLp = Columns("A:A").SpecialCells(xlCellTypeConstants, 23)
dRw = Cells(Rows.Count, "D").End(xlUp).Row
Set dLp = Range("D2:D" & dRw)
'start the loop
'loops through column D and finds value
'in column A, and does something with it
For Each d In dLp.Cells 'loops through column D
For Each a In aLp.Cells 'loops through column A
If d = a Then
'When a match, then do something
'this is where your actual code would go
Range("A" & a.Row & ":B" & a.Row).Copy Cells(Rows.Count, "F").End(xlUp).Offset(1)
End If
Next a 'keeps going through column A
Next d 'next item in column D
End Sub
I have a script which will cycle through 500+ files and make minor edits using a find and replace procedure. If the string cannot be found, however, I'd like the code to open a Modeless UserForm which will allow for manual editing. I would have like to execute a different subroutine after opening the UserForm, but because I am using a For Each loop, I cannot exit the routine and then return.
The script works, except when the Save_User is opened, it behaves as if it were Modal. There are two windows which will be open, but I can select neither of them, and the preview of each window is white in the taskbar. Any ideas as to why this might be happening?
Option Explicit
Public WB As Workbook, Template As Workbook
Public FindSelection As Range, ReplaceSelection As Range, FirstRow As Range, SecondRow As Range, StaticRange() As String
Public Target As Range, LastRow As Integer
Public FindRange As String, ReplaceRange As String, FindText As String, ReplaceText As String
Public Count As Integer, Updated As Integer
Public Const FilePath = "C:\GenericPath"
Public Sub AddApplicable()
Dim FSO As Object
Dim Folder As Object
Dim File As Object
Set FSO = CreateObject("Scripting.FileSystemObject")
Set Folder = FSO.GetFolder(FilePath)
Count = 0
Updated = 0
For Each File In Folder.Files
If File.Type Like "Microsoft Excel*" Then
Application.ScreenUpdating = False
Set WB = Workbooks.Open(File.Path)
Set FindSelection = WB.Worksheets("Repair Instruction").Range("K17:W40")
Set ReplaceSelection = WB.Worksheets("Repair Instruction").Range("X17:AJ40")
LastRow = WB.Worksheets("Repair Instruction").Range("K40:W40").End(xlUp).Row
Set Target = FindSelection.Find(FindText, LookAt:=xlWhole)
If Target Is Nothing Then
If LastRow = 39 Then
Save_User.Show vbModeless
While Save_User.Visible
DoEvents
Wend
GoTo NextAdd
Else
If LastRow Mod 2 = 0 Then
WB.Worksheets("Repair Instruction").Range("K" & LastRow + 1, "W" & LastRow + 1) = FindText
WB.Worksheets("Repair Instruction").Range("X" & LastRow + 1, "AJ" & LastRow + 1) = _
Add_User.txtFirst.Value
WB.Worksheets("Repair Instruction").Range("X" & LastRow + 2, "AJ" & LastRow + 2) = _
Add_User.txtSecond.Value
Else
WB.Worksheets("Repair Instruction").Range("K" & LastRow + 2, "W" & LastRow + 2) = FindText
WB.Worksheets("Repair Instruction").Range("X" & LastRow + 2, "AJ" & LastRow + 2) = _
Add_User.txtFirst.Value
WB.Worksheets("Repair Instruction").Range("X" & LastRow + 3, "AJ" & LastRow + 3) = _
Add_User.txtSecond.Value
End If
WB.Close True
Updated = Updated + 1
End If
Else
Set FirstRow = WB.Worksheets("Repair Instruction").Range("X" & Target.Row, "AJ" & Target.Row)
Set SecondRow = WB.Worksheets("Repair Instruction").Range("X" & Target.Row + 1, "AJ" & Target.Row + 1)
FirstRow.Value = Add_User.txtFirst.Value
SecondRow.Value = Add_User.txtSecond.Value
WB.Close True
End If
End If
NextAdd:
Count = Count + 1
Next File
Unload Applicable_User
Unload Add_User
Unload Ref_User
Unload Replace_User
MsgBox (Updated & " of " & Count & " files updated.")
End Sub
EDIT
David, thank you! I wouldn't have even considered that as the reason, but I'm grateful that it is relatively simple! I am not aware of any other functions that would hide the cycling Excel files, so I edited the If statement with the Save_User to be:
If LastRow = 39 Then
Application.ScreenUpdating = True
WB.Activate
Save_User.Show vbModeless
While Save_User.Visible
DoEvents
Wend
Application.ScreenUpdating = False
GoTo NextAdd
...
End If
The cycling Excel windows now appear for editing, but I can't actually make any changes (my cursor is now a crosshair, and I can select cells but not change their values). None of the Workbooks should be protected, so is there something that I'm missing? Or perhaps a better way to hide the activity in the background (because I don't want 500+ files to appear and disappear)?
Here's your culprit:
Application.ScreenUpdating = False
This prevents user input.