I am exporting data from excel to a word table (11 rows 7 columns). I have written a macro to do this for me but I have come across a critical problem. The data I am entering comes from 3 different workbooks and each workbook goes into a different column in the table. Although this data comes from 3 different workbooks, it is grouped together. The units are tested at 3 different temperatures, and the data from each temp. goes into the correlating column of the word table. Here is my current code
Sub CopyAndPaste()
Dim myfile, wdApp As New Word.Application, wDoc As Word.Document
'select truck report file
ChDrive "E:\"
ChDir "E:\WG\TVAL\"
myfile = Application.GetOpenFilename(, , "Browse for Document")
Dim i As Integer
'searches for row with "avg" then selects column E(avg of temperature mean) of that row.
i = Application.Match("Avg", Sheet1.Range("A1:A20"), 0)
'makes the file appear
wdApp.Visible = True
Set wDoc = wdApp.Documents.Open(myfile)
With wDoc
Dim oRow As Row
For Each oRow In wDoc.Tables(8).Rows
If Len(oRow.Range) = 16 Then
With oRow
.Cells(1).Range.Text = "Performance Review"
.Cells(2).Range.Text = Range("e" & i)
.Cells(3).Range.Text = ""
.Cells(4).Range.Text = ""
.Cells(5).Range.Text = ""
End With
Exit For
End If
Next
End With
wDoc.Save
End Sub
Opens file path and then allows user to select the desired file
Finds the value I want to copy/paste or type into the word table
In word, searches table for first blank row, then types “performance review” into column 1 and types the value I want into the corresponding column of the table. (theres a column for each temperature)
This works great for the first value I add, but when I go to add the next two values it types into a new row when I want it to be in the same row as the first value entered. I know I need to change
If Len(oRow.Range)=16 Then
But I do not know what value to change it to so that it enters data into the row that already has Cells(1) with “Performance Review” and Cells(2) with the value range, and for the 3rd temp Cells(3) would also have a value.
The value for cells(2) will be something like 22.56. Cells(3) would be like 5.21
For each temperature I was planning on running a different macro, but I would like them to be similar to:
Dim oRow As Row
For Each oRow In wDoc.Tables(8).Rows
If Len(oRow.Range) = 16 Then
With oRow
.Cells(1).Range.Text = "Performance Review"
.Cells(2).Range.Text = Range("e" & i)
.Cells(3).Range.Text = ""
.Cells(4).Range.Text = ""
.Cells(5).Range.Text = ""
End With
So I want the first macro to take fill cells(1) and Cells(2), this I have already accomplished. The second macro I need only cells(3) to have data entered into it. And for the 3rd macro I need cells(4) to have the value entered into it.
Essentially all I need to solve is how to determine the value of the rows once the previous values have been entered.
Hopefully this makes sense, I have been doing vba for about a month so I am still fresh to it. If anyone needs additional information or screenshots of examples just let me know!
If someone sees what I am trying to do here and has a better way to solve my problem please share!
I was thinking maybe for the 2nd and 3rd macros I could search for the first blank cell in the table but I have not been able to find that!
P.S. With the 3rd macro, would like to include a line of code that will change the background color of the row to white
Related
We currently have a spreadsheet that is used for scheduling, the gentleman using it doesn't want it changed so what I was thinking was create a new sheet with different formatting using VBA or a macro or?? I will then be able to import the new sheet into access where it is needed for a different program. I am attaching 2 different pictures the first is what it looks like now and 2nd is what I would like it to look like.
Old format
Better picture of Old Format
New format
. I have not done a lot of coding in Excel, normally just ='Sheet1'!E5, but didn't see how I could move the date properly and then not have the date show up any where else. The schedule may have 1 item assigned for a day or multiple items. If I have left something out that would be helpful please let me know.
If I understand you correctly...
The old format is something like this :
The expected result for the new format :
If that's what you mean...
Sub test()
Dim rg As Range: Dim cell As Range
Dim rgCnt As Range: Dim cnt As Long
Sheets("Sheet1").Copy Before:=Sheets(1)
With ActiveSheet
.Name = "TEST"
.Columns(1).Insert
.Range("A1").Value = "DATE"
Set rg = .Range("C2", .Range("C" & Rows.Count).End(xlUp))
End With
For Each cell In rg.SpecialCells(xlCellTypeBlanks)
Set rgCnt = Range(cell.Offset(1, 0), cell.Offset(1, 0).End(xlDown))
If cell.Offset(2, 0).Value = "" Then cnt = 1 Else cnt = rgCnt.Rows.Count
cell.Offset(1, -2).Resize(cnt, 1).Value = cell.Offset(0, 1).Value
Next
rg.SpecialCells(xlCellTypeBlanks).EntireRow.Delete
End Sub
There is a consistent pattern in the old format, where to the right of each blank cell in column B is the date. So we use the blank cell in column B as the benchmark to get the date in column C.
The process:
it copy sheet1 where the old format is.
name the copied sheet to "TEST"
insert one column, and put the header name "DATE"
since HD-2 now is in column C (after insert one column)
so the code make a rg variable to data range in column C.
Then it loop to only the blank cell in rg
set the range to check how many data under each date into rgCnt variable
if the looped cell offset(2,0) is blank then there is only one data under the date so it make the value of cnt variable = 1
if the looped cell offset(2,0) is not blank then there is more than one data under the date, then have the value of cnt from the rgCnt rows count.
then it fill column A (DATE header) with the date as many rows defined by the cnt value.
After the loop done, it delete the entire row of all blank cell in rg variable.
I'm new to the world of VBA and can't quite wrap my head around this macro I want to create.
Essentially, I have a monthly data set that comes in, but the data is imperfect. I often need to clear excess data values of one cell, based on the value of another.
The complex part is that the data will be displaced week to week, and the only thing that is static are the column headers that are included.
Just as an example, columns A thru E have headers Company1, Company2, Company3, etc.
Columns Q thru U have headers Product1, Product2, Product3, etc.
The product columns will contain the company names as values (often more than one, delimited by commas), and if the name of a company doesn't appear for ANY of the product columns, the cell of the same row for that company's column should be cleared.
So if Q4:U4 doesn't contain "Product1" as as value, the value at A4 (product 1 column, row 4) should be cleared.
Any insight on how to go about this would be much appreciated!
edit
Screenshot of example data:
Example 2
Try this. Create a new module within the VBA editor and copy in the below code ...
Public Sub ProcessData()
Dim objCompanyRange As Range, objProductRange As Range, objCompanyCell As Range
Dim strCompany As String, objThisProductRange As Range, rngFrom As Range
Dim rngTo As Range, objFindResult As Range, lngLastRow As Long
On Error Resume Next
' Get the range for the company data.
Set objCompanyRange = Application.InputBox("Please select the COMPANY data range, including headers ...", "Company Data", , , , , , 8)
If Err.Description <> "" Then Exit Sub
' Get the range for the product data.
Set objProductRange = Application.InputBox("Please select the PRODUCT data range, including headers ...", "Product Data", , , , , , 8)
If Err.Description <> "" Then Exit Sub
On Error GoTo 0
For Each objCompanyCell In objCompanyRange
' We want the headers in the range but want to skip processing the first row.
If objCompanyCell.Row > objCompanyRange.Cells(1, 1).Row Then
' This is the only contentious line for me. If your headers are specified as you had in your
' example, i.e. "Group: Company1" then the below will work. If that was a mocked example that
' was not 100% accurate, the below line will need to change. It is currently splitting the header
' by a colon and only storing the right hand side as the company.
strCompany = Trim(Split(objCompanyRange.Cells(1, objCompanyCell.Column).Text, ":")(1))
' Only reset objThisProductRange if the row has changed, otherwise we use the same set of
' products we used last time.
If objCompanyCell.Row <> lngLastRow Then
' Determine the range for the product data given the current row being processed
With objProductRange.Worksheet
Set rngFrom = .Range(.Cells(objCompanyCell.Row, objProductRange.Cells(1, 1).Column).Address)
Set rngTo = rngFrom.Offset(0, objProductRange.Columns.Count - 1)
End With
Set objThisProductRange = Range(rngFrom.Address & ":" & rngTo.Address)
End If
' Find the company name within the current row of Product data.
Set objFindResult = objThisProductRange.Find(strCompany, MatchCase:=False)
' Clear the cell if nothing was found.
If objFindResult Is Nothing Then
objCompanyCell.ClearContents
End If
End If
lngLastRow = objCompanyCell.Row
Next
End Sub
... now watch the animated GIF below to see how you launch it and the resulting output.
If selecting the datasets each time gives you the sh!ts then feel free to hardcode it or use your own determination method. It's the easiest approach given I don't know how you may want to do that.
Here's hoping it's what you're after. Be sure to read the comments in the code in case you have any questions.
I'm in a bad spot - any help pointing me in the right direction would be helpful and much appreciated.
I've created a Access process that copies an excel file and updates it with data from 2 record sets using Copy Recordset. The process now creates 39 workbooks by copying a 'template workbook' to a new file. Each workbook will contain at least one tab, but generally contain more, one workbook has over 20 tabs.
This is all done in Access. It copies the 'template' file and then runs a loop, and using Copy Recordset copies the 'template' worksheet to each tab.
After adding the tab, it loads the data from 2 record sets into the sheet. The 1st recordset loads one row of data - no problem. However the second loads multiple rows - which can vary in number.
This all works fine. The problem is formatting columns D through G.
If Column C = Revenue, Cost or Gross Margin (GM) I want that row's columns D through G formatted as currency.
If Column C = GM% then I want columns D through G formatted as a percentage.
If column C = Hours I don't have to format it.
What I would like is to create a function that formats these columns/rows based on Column C value, when the workbook is opened. I know there is an event that fires when the workbook is opened, and I know I have looped through all of the tabs in a workbook, o I'd want to run that function for each tab.
There may be one other issue - I think I can fix that in Access, but the EAC column is being exported as text and it should numeric. Not sure right now if that's being exported as Text - or EXCEL sees it as text.
Any help would be greatly appreciated. Thanks in advance.
I have images - but it won't let me post them just yet. If they would help - let me know, I'll try to email them to you.
Bob
I've come up with a way to do it, I've included the code.
However there may be a better way - so I'm open to suggestions.
Public Sub FormatTaskRows()
Dim Sheet As Object
Dim rngTasks As Range
Dim rngFCells As Range
Dim strTaskEnd As String
Dim ix As Integer
For Each Sheet In Sheets
If Sheet.Name = "Template" Then
Sheet.Visible = xlSheetVeryHidden
Else
Sheet.Visible = xlSheetVisible
Sheet.Activate
strTaskEnd = Range("C44").End(xlDown).Address
Set rngTasks = Range("$C$44:" & strTaskEnd)
'For Each Row In rngTasks
For ix = 1 To rngTasks.Rows.Count
Sheet.Unprotect
If rngTasks.Cells(ix) = "Revenue" Or rngTasks.Cells(ix) = "Cost" Or rngTasks.Cells(ix) = "Gross Margin (GM)" Then
Set rngFCells = Range(ActiveSheet.Name & "!D" & ix + 43 & ":G" & ix + 43)
rngFCells.Select
Selection.NumberFormat = "$#,##0.00"
Else
If rngTasks.Cells(ix) = "GM%" Then
Set rngFCells = Range(ActiveSheet.Name & "!D" & ix + 43 & ":G" & ix + 43)
rngFCells.Select
Selection.NumberFormat = "0.0000%"
End If
End If
Sheet.Protect
Next ix
End If
Next
End Sub
I am new to vba programming hence I need your expert help in trying to be able to able to read all values from the following excel sheet into a ADODB recordset object using VBA 2003
The recordset will be populated as follows
'Create new recordset with the following fields
Dim rsData as new ADODB.Recordset
rsData.Fields.Append "Month", adVarChar, 20
rsData.Fields.Append "Product", adVarChar, 20
rsData.Fields.Append "Type", adVarChar, 50
rsData.Fields.Append "Value", adVarChar, 50
rsData.Open
'for each row in spreadsheet read the following info
rsData.Addnew
rsData.Fields("Month") = 'value from row 2 Jan followed by data below
rsData.Fields("Product") = "Color" ' Value from B5
rsData.Fields("Type") = "MK1" ' value from C5
rsData.Fields("Value") = "111=" ' value from D6
'Now move to next set of values for Feb
rsData.Addnew
rsData.Fields("Month") = 'value from row 2 FEB
rsData.Fields("Product") = "Shade" ' Value from F5
rsData.Fields("Type") = "AB2" ' value from G5
rsData.Fields("Value") = "345=ABX" ' value from H5
'Now move to next set of values for Mar
rsData.Addnew
rsData.Fields("Month") = 'value from row 2 MAR
rsData.Fields("Product") = "Color" ' Value from F5
rsData.Fields("Type") = "3FG" ' value from G5
rsData.Fields("Value") = "PLZ" ' value from H5
'Now move to next row
rsData.Addnew
rsData.Fields("Month") = 'value from row 2 Jan
rsData.Fields("Product") = "Color" ' Value from F5
rsData.Fields("Type") = "MK2" ' value from C6
rsData.Fields("Value") = "234=BZX" ' value from D6
...and so on
Please note, **the data may move around but the overall layout will remain unchanged.
As you can see from the following diagram. the order has changed: Jan , march, Feb**
Your problem isn't really VBA for Excel, the problem is that that's a garbage data source. If the blocks can move around and be in any order, you really have no easy way of telling where they'll be and how much data is in them. That being the case you'd be much better off asking the questions:
Does the source data need to be in this format; and
Is there a way that we can do it better?
("Better" = more structured, more predictable.)
Also you're thinking too much in terms of each row being a record. Each block is quite separate from the others and since each one has a unique "header record" (being the month) I'd be inclined to process each block in turn rather than trying to jump from one to the other as your sample code tries to do.
The following should give you enough of a grounding to be able to navigate your way through an Excel worksheet. It's just something that I whacked together quickly and have not bullet-proofed though I did test it with a mock-up of the sheet in your second illustration and it did work. It should be enough to help set you on the right course, but I again emphasise... what you have is not a true data source. It's a report that needs to be almost arbitrarily parsed. You need to see whether that can be addressed before you do anything.
Sub DemonstrateReadingFromExcel()
'Every cell in Excel is a range.
'A range can also be a collection of cells.
'Ranges have properties that you can query. More importantly
'you can redefine a range by offsetting it from
'your current range, which makes it easy to step through a block.
Dim rng_Month As Excel.Range
Dim rng_Data As Excel.Range
'Let's define some string variables that you can use to assign
'to your recordset's fields.
Dim s_Month As String
Dim s_Product As String
Dim s_Type As String
Dim s_Value As String
Dim l_RowCurrent As Long
Dim l_RowLastType As Long
Dim l_ColumnOfMonth As Long
'We have to start with the cell containing the month.
'Rather than reading row by row, you'd be better off
'reading a whole block at a time.
'Your big problem will be telling WHERE the cell containing
'that month is and for that reason I think you need to seriously
'look at WHY the data is in the format that it is and whether
'you can actually use a much more structured data source.
'For now though let's pretend that you have some magic way of knowing
'where the range is and assign it to a range variable.
'ActiveSheet is a reference to the active worksheet but just as you
'can use a range variable to store a reference to a range,
'you can use a worksheet variable to store a reference to a worksheet
'(even one which is not the active sheet) if you want to.
'I'm only using ActiveSheet for convenience.
'You need to use the Set statement because you're assigning an object.
Set rng_Month = ActiveSheet.Range("C2")
'Ranges have properties like their column number.
'We already know the column number for this range but let's
'assume that we don't in a more general solution.
l_ColumnOfMonth = rng_Month.Column
'Now let's check that the range is valid.
'Don't worry about what the number means,
'just look at the error description.
'If this is True then there must be something wrong with the range.
If l_ColumnOfMonth < 2 Then
Err.Raise vbObjectError + 20000, , "There are no columns to the left of the month. " _
& "The range is invalid."
End If
'Now let's find out where the last Type entry occurs in the current
'block. We go up from the bottom of the column to do that.
'In this case we're passing the row and column to the Cells
'property. This defines a range for us representing the bottom
'of the Type column. Using End(xlUp) is the same as pressing the
'[End] key then the [Up arrow] key. It takes us to the last
'populated row, which we read the .Row property of.
l_RowLastType = ActiveSheet.Cells( _
ActiveSheet.Rows.Count, l_ColumnOfMonth).End(xlUp).Row
'If this is the same as the Month's own row, there's no data
'in that block.
If l_RowLastType = rng_Month.Row Then
Err.Raise vbObjectError + 20000, , "There are no Type entries in this block. "
End If
'So we've checked that the range looks OK.
'Before we proceed, let's store the month for this block.
'We just get the Value property of the range.
s_Month = rng_Month.Value
'We know that the first product should be 3 rows down and
'one row to the left of the month, so let's just keep looping
'through and reading the values for each row in the block
'until we reach the end of it. We know the end because
'we have its row stored in the l_RowLastType variable.
Set rng_Data = rng_Month.Offset(3, -1)
'Let's get the name of the first product.
s_Product = rng_Data.Value
'If that's nothing, there's a problem.
If s_Product = "" Then
Err.Raise vbObjectError + 20000, , "No valid product in the expected location. "
End If
'Now let's loop through each row.
For l_RowCurrent = rng_Month.Row + 3 To l_RowLastType
'Let's look at another way that we can get a reference to
'a range; by using the Cells property of the sheet.
'For that we specify the row number and column number.
Set rng_Data = ActiveSheet.Cells(l_RowCurrent, rng_Month.Column - 1)
'We know that there won't be a product on each row,
'so if there isn't one we just use the previous one.
'Otherwise we assign the new product.
If rng_Data.Value <> "" Then
s_Product = rng_Data.Value
End If
'Now let's get the type, which is offset 0 rows, 1 column
'to the right of the product.
s_Type = rng_Data.Offset(0, 1)
'If that's a blank cell (like row 8 in your
'second example we won't do anything else.
'We only proceed if it's populated.
If s_Type <> "" Then
s_Value = rng_Data.Offset(0, 2)
'Now at this point you have gathered all of your values
'into variables, and can feed them to your recordset.
'In this case though we'll just output
'a messagebox.
MsgBox "Row " & rng_Data.Row & " is for month " & s_Month _
& ", product " & s_Product & ", Type " & s_Type _
& ", Value " & s_Value
End If
Next
ExitPoint:
On Error Resume Next
Set rng_Month = Nothing
On Error GoTo 0
Exit Sub
ErrorHandler:
MsgBox "Error " & Err.Number & vbCrLf & Err.Description
Resume ExitPoint
End Sub
I have a pivot table in an Excel worksheet that contains the result of a query made to my database. I would like to format the information automatically based on every other data set.
The information contains 4 weeks' (1 month) worth of records for each employee sorted by an employee ID number. I would like to write a module so that it will highlight every other record (employee data set) with a different color. Is this even possible to do? Thanks for the help!
If you insist with solving your problem utilizing VBA here is an example. You'll need to specify start ranges. Please not that marking whole row will use more memory (increasing file size) so I would rather use example: range("A2:E2).select ....
Sub FormatEverySecondRow()
range("A2").EntireRow.Select
Do While ActiveCell.value <> ""
Selection.Interior.ColorIndex = 15
ActiveCell.offset(2, 0).EntireRow.Select
Loop
End Sub
use a helper column (K if I count the columns in your example)
insert into K2:
=IF(ISBlank(C2),K1,MOD(K1+1,2))
then use conditional formatting to highlight the row:
Note the formula does not have a $ sign before the 2 (i.e. $K2, not $K$2)
This might be useful to you:
Sub HighlightDifferentRows()
Dim wksht As Worksheet
Dim wkb As Workbook
Dim row As Range
Dim FloatColor As Long
FloatColor = RGB(100, 100, 100)
Set wbk = ThisWorkbook
Application.ScreenUpdating = False
For Each row In Sheets(1).UsedRange.Rows
row.Interior.Color = FloatColor
If row.Cells(1, 4).Value <> row.Cells(2, 4).Value Then
FloatColor = -FloatColor
End If
Next row
Application.ScreenUpdating = True
End Sub
It alternates row colors whenever a cell value is not the same as the one below it. Right now it is set to grayish colors but you could change it to something brighter if you wanted. You could put in your own logic to get whatever colors you wanted. Good Luck.