duplicate a complex (double) table row in ms word vba - vba

in Microsoft Word 2010, say I have a template with a table, which contains some ready column headers, and the first row. this row is complex - basically it spans two real rows. it also has uneven column widths in the first and the second "real" rows. e.g.
i am trying to populate this table with a dynamic number of rows, that i need to be like the first one. e.g.
the methods that i've seen this far that add rows to a table all make the new row look like the last row (or the first - depending on the way you do it) - but i want the same functionality to be extended to two rows, i.e. so that the new TWO rows would look like the last TWO rows.
the only way i found is to use clipboard. i want to avoid clipboard. maybe anyone can think of another way?
the clipboard code that works:
Sub example()
Set myrows = ActiveDocument.Range( _
ActiveDocument.Tables.Item(1).Rows.Item(1).Range.Start, _
ActiveDocument.Tables.Item(1).Rows.Item(2).Range.End _
)
myrows.Select
' this works
myrows.Copy
myrows.Paste
' this adds two rows that look like the last row in the selection
' this is not what i need
'Selection.InsertRowsBelow
End Sub
thanks

I'm sorry, it's an example of forgetting what you learned like 10 years ago. The particular question I asked seems to be easily answered.
Sub example()
Set myrows = ActiveDocument.Range( _
ActiveDocument.Tables.Item(1).Rows.Item(1).Range.Start, _
ActiveDocument.Tables.Item(1).Rows.Item(2).Range.End _
)
' this works
Set rowsbelow = ActiveDocument.Range(myrows.End, myrows.End)
rowsbelow.FormattedText = myrows.FormattedText
End Sub
if moderators deem it necessary, i may delete the post entirely

Related

Reading columns and rows from a sheet and bind to combo box VBA

Im a very beginner with VBA. Ive got a scenario to decide the perfect way for binding and manipulate the data from different sheets in a custom form designed in another sheet on the same workbook.
There are 2 sheets one with Fruit names in different year and another with Quantity of every year.
Here is Sheet 1 data.
Based on this I need to bind these years to one dropdown, fruits to another dropdown (based on year it should load dynamically). Since my previous experience in .NET so if we consider all as a Datatable its easy to manipulate anything using some filters or just some loops or with LINQ. But in VBA how can we handle these bindings and manipulation based on these data. Also if the rows could not be always in specific cell ranges, there could be more. So how can we handle when dynamic data.
What I tried for binding is as follows based on this link
Dim vArr as Variant
Dim i as Integer
vArr = WorksheetFunction.Transpose(Sheets(2).Range("A2:A10").value)
With Sheets(1).OLEObjects("ComboBox1").Object
.Clear
For i = Lbound(vArr) to Ubound(vArr)
.AddItem vArr(i)
Next i
End With
You really shouldn't be asking two questions in the same question... But because you did, I can at least answer your second question of:
"...if the rows could not be always in specific cell ranges, there could be more. So how can we handle when dynamic data"
You can use this function to grab the last row:
Public Function lastRow(ByVal ws As Worksheet, Optional ByVal col As Variant = 1) As Long
lastRow = ws.Cells(ws.Rows.Count, col).End(xlUp).Row
End Function
Which you could use like:
Range("A2:A" & lastRow(Worksheets(2)))
Now, when you make your first question make a little more sense I will update my answer.

VBA creating formulas referencing a range

After several hours of research, I still can't solve what seems to be a pretty simple issue. I'm new to VBA, so I will be as specific as possible in my question.
I'm working with a DDE link to get stock quotes. I have managed to work out most of the table, but I need a VBA to create a finished formula (i.e., without cell referencing) in order to the DDE link to work properly.
My first code is as follows:
Sub Create_Formulas()
Range("J1").Formula = "=Trade|Strike!" & Range("A1").Value
End Sub
Where J2 is the blank cell and A2 contains the stock ticker. It works fine, but when I try to fill out the rows 2 and bellow, it still uses A1 as a static value.
Sub Create_Formulas()
Dim test As Variant
ticker = Range("A1").Value
'Test to make variable change with each row
'Range("J1:J35").Formula = "=Trade|Strike!" & Range("A1:A35").Value
'not working
Range("J1:J35").Formula = "=Trade|Strike!" & ticker
'not working
End Sub
I couldn't find a way to solve that, and now I'm out of search queries to use, so I'm only opening a new topic after running out of ways to sort it by myself. Sorry if it is too simple.
You are referencing absolute cell adresses here. Like you would do when using $A$1 in a normal excel formula.
What you want to do is:
Dim row as Integer
For row = 1 to 35
Cells(row,10).Formula = "=Trade|Strike!" & Cells(row,1).Value
Next row
This will fill the range J1 to J35 with the formula. Since (row,10) indicates the intersection of row and column 10 (J)
Firstly, in your second set of code, you define a variable "test", but never give it a value.
You assign a value to the variable "ticker", and then never reference it.
Secondly, the value you have assigned to ticker is a static value, and will not change when it is entered in a different row.
Thirdly, I think your issue could be solved with a formula in Excel rather than VBA.
The "INDIRECT" function can be quite useful in situations like this.
Try inserting the formula
=INDIRECT("'Trade|Strike'!"&A1)
into cell A1, then copy down.
Note the ' ' marks around "Trade|Strike". This is Excels syntax for referencing other sheets.

Find a value from a column and quickly return the row number of its cell

What I have
I have a file with part numbers and several suppliers for each part. There are 1500 parts with around 20 possible suppliers each. For the sake of simplicity let's say parts are listed in column A, with each supplier occupying a column after that. Values under the suppliers are entered manually but don't really matter.
In another sheet, I have a list of parts that is imported from an Access database. The parts list is imported, but not the supplier info. In both cases, each part appears only once.
What I want to do
I simply want to match the supplier info from the first sheet with the parts in the imported list. Right now, I have a function which goes through each part in the list with suppliers, copies the supplier information in an array, finds the part number in the imported part list (there is always a unique match) and copies the array next to it (with supplier info inside). It works. Unfortunately, the find function slows down considerably each time it is used. I know it is the culprit through various tests, and I can't understand why it slows down (starts at 200 loop iterations per second, slows down to 1 per second and Excel crashes) . I may have a leak of some sort? The file size remains 7mb throughout. Here it is:
Function LigneNum(numAHNS As String) As Integer
Dim oRange As Range, aCell As Range
Dim SearchString As String
Set oRange = f_TableMatrice.Range("A1:A1500")
SearchString = numAHNS
Set aCell = oRange.Find(What:=SearchString, LookIn:=xlValues, _
LookAt:=xlPart, SearchOrder:=xlByRows, SearchDirection:=xlNext, _
MatchCase:=False, SearchFormat:=False)
If Not aCell Is Nothing Then
'We have found the number by now:
LigneNum = aCell.Row
Exit Function
Else
MsgBox "Un numéro AHNS n'a pas été trouvé: " & SearchString
Debug.Print SearchString & " not found!"
LigneNum = 0
Exit Function
End If
End Function
The function simply returns the row number on which the value is found, or 0 if it doesn't find it which should never happen.
What I need help with
I'd like either to identify the cause of the slow down, or find a replacement for the Find method. I have used the Find before and it is the first time this happens to me. It was initially taken from Siddarth Rout's website: http://www.siddharthrout.com/2011/07/14/find-and-findnext-in-excel-vba/ What is strange is that it doesn't start slow, it just becomes sluggish as it goes on.
I think using Match could work, or maybe dumping the range to search (the part numbers) into an array and trying to match these with the imported parts number list could work. I am unsure how to do it, but my question is more about which one would be faster (as long as it remains under 15 seconds I don't really care, though, but looping over 1500 items 1500 times right out of the sheet is out of the question). Would anyone suggest match over the array solution / spending more hours fixing my code?
EDIT
Here is the loop it is being called from. I don't think it is problematic:
For Each cellToMatch In rngToMatch
Debug.Print cellToMatch.Row
'The cellsToMatch's values are the numbers I want, rngToMatch is the column where they are.
For i = 2 To nbSup + 1
infoSup(i - 2) = f_TableMatrice.Cells(cellToMatch.Row, i)
Next
'infoSup contains the required supplier data now
'I call the find function here to find the row where the number appears in the imported sheet
'To copy the array nbSup on that line
LigneAHNS = LigneNum(cellToMatch.Value) 'This is the Find function
If LigneAHNS = 0 Then Exit Sub
'This loop just empties the array in the right line.
For i = LBound(infoSup) To UBound(infoSup)
f_symix.Cells(LigneAHNS, debutsuppliers + i) = infoSup(i)
Next
Next
If I replace LigneAHNS = LigneNum by LigneAHNS = 20, for example, the code executes extremely fast. The leak therefore comes from the find function itself.
Another way to do it without using the find function might be something like this. Firstly, put the part IDs and their line numbers into a scripting dictionary. These are really quick to lookup from. Like this:
Dim Dict As New Scripting.Dictionary
Dim ColA As Variant
Lastrow=range("A50000").end(xlUp).Row
ColA = Range("A1:A" & LastRow).Value
For i = 1 To LastRow
Dict.Add ColA(i, 1), i
Next i
To further optimise, you could declare the Dict as a public variable, populate it once, and refer to it many times in your lookups. I expect this would be faster than running a cells.find over a range every time you do a lookup.
For syntax of looking up items in the dictionary, refer to Looping through a Scripting.Dictionary using index/item number
You could achieve this with only Excel cell formulas and no VB if you are willing to devote a separate column to each supplier on your main parts sheet. You could then use conditional formatting to make it more visually appealing. I've tried it with 1500 rows and it's very quick. Increasing it to 5000 rows becomes noticeably slower, but you say you have only 1500 rows for now, so it should be suitable.
On Sheet 1, define a part number column and a separate column for each supplier.
Create a separate sheet for each supplier with all part numbers available from that supplier listed in column A. Make sure the rows on the supplier sheets are ordered by part number.
Name each of the supplier sheets the same as the associated column heading shown on Sheet 1.
Assign the following formula in each cell beneath each supplier column heading on Sheet 1:
=NOT(ISNA(VLOOKUP($A2,INDIRECT("'"&B$1&"'!A:A"),1,FALSE)))
The following screen cap shows this implemented along with conditional formatting to highlight which suppliers have which parts:
If you wanted to show quantities available from suppliers, then you could always have a second column (B) on the supplier sheets containing last known quantities for each part and use VLOOKUP to retrieve column B instead of A.

Excel Macro Autofilter issue with variable

I have a table of data with the top row being filters, I have a loop that changes which filter needs to be used inside the loop is the variable filterColumn that is being assigned a new value every time the loop runs through.
when i try to use filterColumn to determine which filter will be 'switched on' i get an error
Autofilter method of Range Class Failed
ActiveSheet.Range("$U$83:$CV$1217").AutoFilter Field:=filterColumn, Criteria1:="<>"
What is the correct syntax in order to use a variable to determine which field the filter is in?
Problem Solved I found the solution. I was referencing the filters columns position in terms of the whole worksheet when in fact I should have been referencing what number it was in the group of filters. For example the filter I wanted to change was in 'CF' which is the 84th column but my the filter I wanted to change is the 64th in the group.
Dim filterColumn As Integer
filterColumn = 2
ActiveSheet.Range("$U$83:$CV$1217").AutoFilter Field:=filterColumn, _
Criteria1:="<>"
EDIT: I tried #HeadofCatering's solution and initially it failed. However I filled in values in the referenced columns and it worked (my solution also failed under reverse conditions - make the column headers blank and it fails).
However this doesn't quite mesh with what I've (and probably you've) seen - you can definitely add filters to columns with blank headers. However one thing was consistent in the failures I saw - the filterColumn referenced a column that was outside of Application.UsedRange. You may want to try verifying that the column you are referencing is actually within Application.UsedRange (easy way: run Application.UsedRange.Select in the Immediate Window and see if your column is selected). Since you are referencing a decent amount of columns, it is possible that there are no values past a certain point (including column headers), and when you specify the column to filter, you are actually specifying something outside of your UsedRange.
An interesting (this is new to me as well) thing to test is taking a blank sheet, filling in values in cells A1 and B1, selecting columns A:G and manually adding AutoFilters - this will only add filters to columns A and B (a related situation can be found if you try to add filters to a completely blank sheet).
Sorry for the babble - chances are this isn't even your problem :)
Old solution (doesn't work when conditions described above are used)
I may be overkilling it, but try setting the sheet values as well (note I used a sample range here):
Sub SOTest()
Dim ws As Worksheet
Dim filterColumn As Integer
' Set the sheet object and declare your variable
Set ws = ActiveSheet
filterColumn = 2
' Now try the filter
ws.Range("$A$1:$E$10").AutoFilter Field:=filterColumn, Criteria1:="<>"
End Sub

How to determine the last column(last column with value) using VBA in excel?

I have a question wonder if anyone can help me out.
I'm doing a project that requires to get a summary of tests result. My flow is as follow,
First, I would need to open my collected test results, then creating a pivot table, after setting accordingly to what i need to view, i will copy the table into another spreadsheet. Over at this spreadsheet, it will change some of the names to the required names and finally, it will be copied to the summary sheet. Then i will need to tabulate the summary in % also.
I'm facing with an issue here which is, for my collected test results, when i put it into the pivot table, i cant determine the number of columns that will appear. For example
In on example, we can have
In another case, we can have
If u notice the second image has more columns used.
Therefore, I was wondering if there is any way that you guys can share with me to determine the last available column with details to be copied.
And can i get the number of the last column so that i can get the summary as well since i need to put it in the form of %.
At the moment, i'm using "Range(A1:Z1000000)" which is quite impossible for me to do anything as i cant really find the % manually.
btw, i need it in VBA code.
Will appreciate for all the opinions and options provided! Thank you in advance!
This should do the trick:
Sub test()
Dim maxColumn As Long
'Create the table here
maxColumn = getMaxUsedColumnNumber("PivotTableSheet")
'do what you need with the number
End Sub
Function getMaxUsedColumnNumber(sheetName As String) As Long
With Sheets(sheetName)
getMaxUsedColumnNumber = .Cells.Find(What:="*", after:=.Range("A1"), LookIn:=xlFormulas, _
SearchOrder:=xlByColumns, SearchDirection:=xlPrevious).Column
End With
End Function
You can use .SpecialCells() as follows:
ActiveSheet.UsedRange.SpecialCells(xlLastCell).Column
Alternatively, take a look at the ColumnRange property of the PivotTable object. It has a Count property which will return the number of columns in the Pivot Table which could also help.