Indexing of repeating content control items - excel

My question is based on this question and this solution:
I have a similar problem, but I need to insert items in order, but I could not index the inserted repeating content controls correctly. I do not know how many items I should insert in advance, so inserting could be fully dynamic.
Could anybody help me?
Here is a simple code:
Dim wordApp As Variant
Dim wDoc As Variant
Set wordApp = CreateObject("word.application")
wordApp.DisplayAlerts = False
Set wDoc = wordApp.Documents.Open(ThisWorkbook.Path & "/example.docm")
wordApp.Visible = True
Dim i As Integer
Dim counter As Integer
counter = 1
Dim cc As Variant
Dim repCC As Variant
Set cc = wDoc.SelectContentControlsByTag("container").Item(1)
For i = 1 To 4
If counter <> 1 Then
Set repCC = cc.RepeatingSectionItems.Item(cc.RepeatingSectionItems.Count)
repCC.InsertItemAfter
End If
wDoc.SelectContentControlsByTag("number").Item(counter).Range.Text = counter
counter = counter + 1
Next i
A picture of my word doc:
The tag name of the repeating content control is "container". The tag name of the rich text content control is "number".
A picture of the wrong result:
And what I would like to get :)
Thank you for your help in advance!

Finally I could resolve my problem:
TASK: This is a simply example of inserting Repeating Section Content Controls (RSCC) dynamically from vba and fill out their inner Content Controls in order.
PROBLEM: When inserting a new RSCC like here, their Content Controls will get the same tags (titles), and indexes are assigned randomly.
SOLUTION: Content controls must be filled out on the fly, when a new RSCC has been just inserted.
Dim cc As Variant
Dim repCC As Variant
Dim i As Integer
Set cc = wDoc.SelectContentControlsByTag("container").Item(1)
For i = 1 To 4 'it could be any number
If i = 1 Then 'because already has a RSCC in the doc file, so I need only 3 more.
Set repCC = cc.RepeatingSectionItems.Item(1)
Else
repCC.InsertItemAfter
Set repCC = cc.RepeatingSectionItems.Item(i) '(or .Item(cc.RepeatingSectionItems.Count))
End If
For Each cc_current In repCC.Range.ContentControls
Select Case cc_current.Tag
Case Is = "number"
cc_current.Range.Text = i
'Case Is = .....
End Select
Next cc_current
Next i

Related

How to create a document and then replace the values of the fields in the specific document

I have an agent which imports records from Excel to Notes. At the moment each time, it runs it creates a new document. I would like it:
The first time it runs to create the document.
The next time it will run, to replace the field values of the specific document NOT to create a new one.
How can I fix my agent which is:
Sub Initialize
Dim session As New NotesSession
Dim db As NotesDatabase
Dim doc As NotesDocument
Dim xlApp As Variant, xlsheet As Variant, xlwb As Variant, xlrange As Variant
Dim filename As String, currentvalue As String
Dim batchRows As Integer, batchColumns As Integer, totalColumns As Integer
Dim x As Integer, y As Integer, startrow As Integer
Dim curRow As Long, timer1 As Long, timer2 As Long
Dim DataArray, fieldNames, hasData
Dim view As NotesView
Set db = session.CurrentDatabase
Set view = db.GetView("test-forecast")
Set doc = view.GetFirstDocument
timer1=Timer
filename="C:\DM\Forecast\forecast-a.xlsx"
batchRows=2 'process 2 rows at a time
Set db=session.CurrentDatabase
Set xlApp = CreateObject("Excel.Application")
xlApp.Visible = True 'set Excel program to run in foreground to see what is happening
Set xlwb=xlApp.Workbooks.Open(filename)
Set xlsheet =xlwb.Worksheets(1)
Redim fieldNames(1 To 5) As String
DataArray=xlsheet.Range("A1").Resize(batchRows, 5).Value 'get worksheet area of specified size
For y=1 To 5 'we assume max 5 columns in the sheet
currentvalue=Cstr(DataArray(1,y))
If currentvalue<>"" Then 'abort counting on empty column
fieldNames(y)=currentvalue 'collect field names from the first row
totalColumns=y
Else
y=2
End If
Next
Redim Preserve fieldNames(1 To totalColumns) As String
curRow=2
hasData=True
While hasData=True 'loop until we get to the end of Excel rows
If curRow=2 Then startrow=2 Else startrow=1
For x=startrow To batchRows
curRow=curRow+1
If Cstr(DataArray(x,1))+Cstr(DataArray(x,2))<>"" Then 'when 2 first columns are empty, we assume that it's the end of data
Print Cstr(curRow-2)
Set doc=New NotesDocument(db)
doc.Form="test-forecast"
doc.Type="test-forecast"
For y=1 To totalColumns
currentvalue=Cstr(DataArray(x,y))
Call doc.ReplaceItemValue(fieldNames(y), currentvalue)
Next
Call doc.save(True, False)
Else
hasData=False
x=batchRows
End If
Next
If hasData=True Then DataArray=xlsheet.Range("A"+Cstr(curRow)).Resize(batchRows, totalColumns).Value 'get worksheet area
Wend
timer2=Timer
Call xlApp.Quit() 'close Excel program
End Sub
Thank you in advance.
As of your comment you only have 2 rows in your excel- file: The first row contains the fieldnames, the second row contains the values.
Every import only contains one document. And you want to update this single document on every run.
There are multiple ways to get that document, the fastest is by using a view containing that document. Create a view with Selection formula: SELECT Form = "test-forecast". Give it a speaking name like (ViwLkpDocument). If you really have only one import with one document, then you can keep the default column, otherwise you might sort the first column by some sort of key to identify the matching document.
I would add a function for that so that you can change the method later if your requirments change:
Function GetDocument(db as NotesDatabase) As NotesDocument
Dim viwLkp as NotesView
Dim docTmp as NotesDocument
Set viwLkp = db.GetView( "(ViwLkpDocument)" )
Set docTmp = viwLkp.GetFirstDocument
If docTmp is Nothing then
Set docTmp = New NotesDocument( db )
docTmp.Form="test-forecast"
docTmp.Type="test-forecast"
End If
Set GetDocument = docTmp
End Function
Then change your code like that:
...
Print Cstr(curRow-2)
Set doc=GetDocument(db)
For y=1 To totalColumns
...

Get Value and Position of Checkbox in Word Table to Copy Entire Table to Excel

I have a Word file with approximately 10 pages and 20 tables. Some of these tables have checkboxes. I want to copy these tables into an Excel file.
The following code copies all tables from my Word file into my Excel file:
Sub Import()
Option Explicit
Dim wb As Workbook
Dim sh As Worksheet
Dim sheet As Worksheet
Dim lzeile As Integer
Set wb = ActiveWorkbook
Set sh = wb.ActiveSheet
Set sheet = wb.Worksheets("Tabelle1")
Dim Btxt As Object
Set Btxt = CreateObject("Word.Application")
Btxt.Visible = True
Btxt.documents.Open "C:\Users\*.doc" '*=path
lzeile = 0
For i = 1 To 20
Btxt.ActiveDocument.Tables(i).Range.Copy
Application.Goto sheet.Cells(1 + lzeile, 1)
sheet.PasteSpecial Format:="HTML", Link:=False, DisplayAsIcon:=False
lzeile = sheet.Cells(Rows.Count, 1).End(xlUp).Row
lzeile = lzeile + 1
sheet.Cells(lzeile, 1) = "Tabelle" & i
Next i
Btxt.Quit
Set Btxt = Nothing
Set wb = Nothing
Set sh = Nothing
End Sub
It does not copy checkboxes or the value (0 = not checked / 1 = checked) of the checkbox.
I can write the value of a checkbox into a cell in my excel sheet with this line:
sheet.Cells(j, 10) = Btxt.ActiveDocument.Tables(i).FormFields.Item("Kontrollkästchen" & j).Result
With a loop j over all "Kontrollkästchen" (german translation of contentcontrol or formfield item) so basically the name of all formfield items in this Word file.
How can I get the position of these formfield items or identify which formfield item / ContentControl is in which table?
I tried to go through all rows and columns in each table because none of them are larger than 10x10. But I can´t find a way to check if a checkbox is maybe in table 3 on column 5 row 5 and then read the name of this checkbox to a safe the value (0 / 1) in the Excel cell on the same position in my copied table.
The solution depends on whether they're formfields or content controls.
Assuming they're formfields:
Sub Demo()
Dim i As Long, j As Long, Rng As Range
With ActiveDocument
For i = .FormFields.Count To 1 Step -1
With .FormFields(i)
If .Type = wdFieldFormCheckBox Then
j = Abs(.CheckBox.Value)
Set Rng = .Range
.Delete
Rng.Text = j
End If
End With
Next
End With
End Sub
Assuming they're content controls:
Sub Demo()
Dim i As Long, j As Long, Rng As Range
With ActiveDocument
For i = .ContentControls.Count To 1 Step -1
With .ContentControls(i)
If .Type = wdContentControlCheckBox Then
j = Abs(.Checked)
Set Rng = .Range
.Delete
Rng.Text = j
End If
End With
Next
End With
End Sub
For the sake of simplicity and clarity, the sample code below leaves out the parts having to do with Excel, as well as creating the instance of the Word Application. It shows only how to access the Word document's checkboxes and convert those to static values.
At the end, also, the document is closed without saving changes, which means forms protection and the checkboxes should be left intact - the macro will not have affected them.
Note: You should have Option Explicit at the top of the code page, not inside a "Sub".
How it works
The document to be processed is opened and at that moment set to an object (doc). Use this instead of ActiveDocument as it will be much clearer and, in case the user would try to do something, won't affect macro execution.
If the document has forms protection, this must be turned off in order to delete the checkboxes and insert static values.
Then all the form fields are looped. If they are checkboxes, the value is determined, the checkbox removed and the value assigned to the range the checkbox occupied.
After this has completed comes the code to transfer data to Excel. Then the document is closed without saving changes.
Sub ConvertCheckBoxesToValues()
Dim ff As Object ' Word.FormField
Dim doc As Object ' Word.Document
Dim cbValue As String
Dim rngFF As Object ' Word.Range
Set doc = Btxt.Documents.Open("C:\Users\*.doc") '*=path
If doc.ProtectionType <> -1 Then 'wdNoProtection
doc.Unprotect
End If
For Each ff In doc.FormFields
If ff.Type = 71 Then 'wdFieldFormCheckBox
If ff.CheckBox.value = True Then
cbValue = "1"
Else
cbValue = "0"
End If
Set rngFF = ff.Range
ff.Delete
rngFF = cbValue
End If
Next
'Transfer the information to Excel, then
doc.Close 0 'wdDoNotSaveChanges
End Sub

How do I get my Excel data into Word's ContentControl

I have placed a Plain Text Content Control on my Document.
I opened the Macro and have the following code
Sub PrefillDocument()
'
' PrefillDocument Macro
'
'
Dim docName As ContentControls
Dim objExcel As Object
Dim FileName As String
FileName = ActiveDocument.Path & "\CountyData.xlsx"
Set objExcel = CreateObject("Excel.Application")
Set exWb = objExcel.Workbooks.Open(FileName)
MsgBox exWb.Sheets("4").Cells(1, 2) // Works
' Having problems trying to get the data from Excel into the content control
Set docName = ActiveDocument.SelectContentControlsByTag("Name") // Get
docName.Item.Title = exWb.Sheets("4").Cells(1, 2)
MsgBox docName.Title
'ActiveDocument.FormFields("Name").Result =
'ThisDocument.m_name.Caption = exWb.Sheets("Member's Data").Cells(2, 1)
exWb.Close
Set exWb = Nothing
End Sub
I have been told NOT to use any legacy controls so I am forced to use the newer ContentControls
docName is a collection of controls, and in this case, Word isn't going to let you apply a Title to every control in the Collection.
So you will need to iterate, e.g.
Dim cc as ContentControl
For Each cc In docName
cc.Title = exWb.Sheets("4").Cells(1, 2)
Next
or you could probably drop your docName declaration and do
Dim cc as ContentControl
For Each cc In ActiveDocument.SelectContentControlsByTag("Name")
cc.Title = exWb.Sheets("4").Cells(1, 2)
Next
For the question you posted in the comments, to update the actual content of the Control rather than the title, you need to know that the content is represented by a Word Range, and that you need to set the text of the range, e.g.
cc.Range.Text = exWb.Sheets("4").Cells(1.2)
You will still need to iterate through the collection of controls.

Formula or Macro for Open Office calc to retrieve Comments (annotation) from a Cell

I need to harvest and colate data from an oOcalc workbook.
Part of the information is presented as comments on the cell.
I cant figure out a formula to do it and Im not familiar with oOcalc DOM's to manipulate the item.
Hope someone can help me out.
Thanks.
Just had to figure this one out myself, so here is a macro that will copy the comments of cells in one sheet to actual cells in another sheet.
It could be better, but it gets the job done, so its not worth putting anymore (of my) time into!
REM ****** BASIC *********
Sub ExtractCommentAnnotationThings
Dim myDoc as Object
Dim originalSheet as Object
Dim newSheet as Object
Dim originalCell as Object
Dim newCell as Object
Dim commentString As String
REM DEFINE VAR FOR OUR LOOP
Dim iTargetRow, iTargetColumn As Long
Const kEndRow = 950
Const kEndColumn = 20
REM SET DOC
myDoc = ThisComponent
REM GET SHEET
originalSheet = myDoc.Sheets(0)
newSheet = myDoc.Sheets(1)
REM START LOOP
For iTargetRow = 0 To kEndRow: DoEvents
For iTargetColumn = 0 To kEndColumn: DoEvents
originalCell = originalSheet.getCellByPosition(iTargetColumn,iTargetRow)
REM commentString = Trim(originalCell.Comment.Text)
If originalCell.Annotation.isVisible = True Then
commentString = originalCell.getAnnotation().String
newCell = newSheet.getCellByPosition(iTargetColumn,iTargetRow)
newCell.String = commentString
End If
Next
Next
REM CONTINUE LOOP
End Sub
Set the kEndRow and kEndColumn to include only the range of cells you want copied.
Set the originalSheet and newSheetappropriately as well (might need to create a new sheet first), so they're copied where you want them to be.
Hope it helps!

How can I dynamically add a radio button on a form using VBA

I want to dynamically add a radio button on a form, using VBA.
I tried writing this code, but it crashes with 'Type Mismatch'
Dim optionBtn As OptionButton
Set optionBtn = UserForm1.Controls.Add("Forms.OptionButton.1", "name", True)
optionBtn.Left = 10
optionBtn.Top = 10
optionBtn.Width = 30
optionBtn.Group = "q1"
I also tried doing this:
Dim optionBtn As Control
Set optionBtn = UserForm1.Controls.Add("Forms.OptionButton.1", "name", True)
optionBtn.Left = 10
optionBtn.Top = 10
optionBtn.Width = 30
optionBtn.Group = "q1"
but I get a Control, not a OptionButton - how can I cast it to a OptionButton ? (sorry, I'm new to VB)
I was able to get it work with this (Excel 2003):
Dim lbl As Variant
Set lbl = UserForm1.Controls.Add("Forms.Label.1", "lblFoo", True)
lbl.Caption = "bar"
Update to reflect your change from a Label to an OptionButton
Again, the key is use a Variant type for the variable that you are assigning the returned control to:
Dim opt As Variant
Set opt = UserForm1.Controls.Add("Forms.OptionButton.1", "radioFoo", True)
opt.Caption = "Bar"
Keep in mind that autocomplete won't work on the variables that are defined as Variants. However you can still refer to properties and methods of those variables by typing them manually.
Actually, I believe your problem lies in the fact that you are naming optionBtn as an object button. It needs to be named as a MSForms Option Button. Since a Variant can be an object, it will work when using a variant.
I used the following and it works fine.
Dim TempForm As Object
Dim newOptionButton as MSForms.OptionButton
Dim sUserformName as string
Dim i as integer
Dim x as integer
Dim y as integer
' other junk removed for example sake...
sUserformName = sheet1.cells(1,1)
' create form
Set TempForm = ThisWorkbook.VBProject.VBComponents.Add(3)
With TempForm
.Properties("Caption") = sUserformName
.Properties("Width") = 450
.Properties("Height") = 300
End With
for i = 3 to sheet1.range("A65536").End(XlUp).Row
sDefault = sheet1.cells(i,5)
iGN = sheet1.cells(i,6)
' additional code removed... for sake of example... the code would add labels, text boxes, etc...
Set newOptionButton = TempForm.designer.Controls.Add("Forms.optionbutton.1")
With newOptionButton
.Caption = sDefault
.GroupName = "Group" & iGN
.Top = 20 + (20 * x)
.Left = y
.Height = 16
.Font.Size = 8
.Font.Name = "Ariel"
End With
' here the code changes x and y depending on where the user (excel template) directs the next control.
next i
Good luck....
mark's code should work, but I often prefer to create the item manually then show/hide it based on the need.
You need to define the object as an optionbutton from the msforms library.
Dim optionBtn As MSForms.OptionButton
Set optionBtn = UserForm1.Controls.Add("Forms.OptionButton.1", "name", True)

Resources