When exporting Word review comments, how do you reference the sentence related to a comment? - macros

I am trying to export a Word document's review comments. I want to export the sentence selection that was commented on followed by the comment.
Screen shot of the image: http://jspeaks.com/mswordcomment.png
I have found code to loop through the document comments, but I cannot figure out how to reference the sentence selection that the comment was related to.
The current logic is:
Sub ExportComments()
Dim s As String
Dim cmt As Word.Comment
Dim doc As Word.Document
For Each cmt In ActiveDocument.Comments
s = s & cmt.Initial & cmt.Index & "," & cmt.Range.Text & vbCr
Next
Set doc = Documents.Add
doc.Range.Text = s
End Sub
I tinkered with Selection.Range, however I cannot determine the proper object or property that contains the referenced sentence.
I would like to produce output like the following (if we use the example in picture above):
Sentence: Here are more sentences that contain interesting facts - Comment: This is an interesting fact.
Sentence: Here are more sentences that contain interesting facts. Here are more sentences that contain interesting facts. - Comment: This is a very interesting fact

I found someone on another site to solve this question.
The key to the solution is: cmt.Scope.FormattedText
Here is the function revised:
Sub ExportComments()
Dim s As String
Dim cmt As Word.Comment
Dim doc As Word.Document
For Each cmt In ActiveDocument.Comments
s = s & "Text: " & cmt.Scope.FormattedText & " -> "
s = s & "Comments: " & cmt.Initial & cmt.Index & ":" & cmt.Range.Text & vbCr
Next
Set doc = Documents.Add
doc.Range.Text = s
End Sub

I have gathered several pieces of code and came to this solution:
Sub CopyCommentsToExcel()
'Create in Word vba
'TODO: set a reference to the Excel object library (Tools --> Reference --> Microsoft Excel 12.0 Object library)
Dim xlApp As Excel.Application
Dim xlWB As Excel.Workbook
Dim i As Integer
Dim HeadingRow As Integer
HeadingRow = 3
Dim cmtRef As Range
Set xlApp = CreateObject("Excel.Application")
xlApp.Visible = True
Set xlWB = xlApp.Workbooks.Add ' create a new workbook
With xlWB.Worksheets(1)
' Create report info
.Cells(1, 1).Formula = "Reviewed document:"
' Create Heading
.Cells(HeadingRow, 1).Formula = "Index"
.Cells(HeadingRow, 2).Formula = "Page"
.Cells(HeadingRow, 3).Formula = "Line"
.Cells(HeadingRow, 4).Formula = "Comment"
.Cells(HeadingRow, 5).Formula = "Reviewer"
.Cells(HeadingRow, 6).Formula = "Date"
For i = 1 To ActiveDocument.Comments.Count
.Cells(2, 1).Formula = ActiveDocument.Comments(i).Parent
.Cells(i + HeadingRow, 1).Formula = ActiveDocument.Comments(i).Index
.Cells(i + HeadingRow, 2).Formula = ActiveDocument.Comments(i).Reference.Information(wdActiveEndAdjustedPageNumber)
.Cells(i + HeadingRow, 3).Formula = ActiveDocument.Comments(i).Reference.Information(wdFirstCharacterLineNumber)
.Cells(i + HeadingRow, 4).Formula = ActiveDocument.Comments(i).Range
.Cells(i + HeadingRow, 5).Formula = ActiveDocument.Comments(i).Initial
.Cells(i + HeadingRow, 6).Formula = Format(ActiveDocument.Comments(i).Date, "dd/MM/yyyy")
' .Cells(i + 1, 3).Formula = ActiveDocument.Comments(i).Parent
' .Cells(i + 1, 3).Formula = ActiveDocument.Comments(i).Application
' .Cells(i + 1, 7).Formula = ActiveDocument.Comments(i).Author
Next i
End With
Set xlWB = Nothing
Set xlApp = Nothing
End Sub
Most valuable help from Microsoft Answers

Related

Libre Office custom Dialog Table

I am creating a custom Dialog where the user is supposed to select one of multiple possible entries. I use a List Box to list the possible entries to be selected from.
There are multiple variables for each row, therefore I would like to use a table to properly align the entries. Is there a possibility to do so?
What i have:
abcdefg hijkl mnopq
abcd efghijk lmno
What i want:
abcdefg hijkl mnopq
abcd efghilkl mno
Use a fixed-width font for the list box, and pad the strings with spaces.
Sub PaddedListboxItems
oListBox.addItems(Array(
PaddedItem(Array("abcdefg", "hijkl", "mnopq")),
PaddedItem(Array("abcd", "efghijk", "lmno"))), 0)
End Sub
Function PaddedItem(item_strings As Array)
PaddedItem = PadString(item_strings(0), 10) & _
PadString(item_strings(1), 11) & item_strings(2)
End Function
Function PadString(strSource As String, lPadLen As Long)
PadString = strSource & " "
If Len(strSource) < lPadLen Then
PadString = strSource & Space(lPadLen - Len(strSource))
End If
End Function
More ways to pad strings in Basic are at http://www.tek-tips.com/viewthread.cfm?qid=522164, although not all of them work in LibreOffice Basic.
Yes, it is possible.
Create a new dialog and at the bottom, add a label.
Create a new module and add following code:
Option Explicit
Option Base 0
Dim oDialog1 As Object, oDataModel As Object, oListener As Object
Sub OpenDialog()
Dim oGrid As Object, oGridModel As Object, oColumnModel As Object, oCol As Object
Dim oLabel1 As Object, rect(3) As Integer
DialogLibraries.LoadLibrary("Standard")
oDialog1 = CreateUnoDialog(DialogLibraries.Standard.Dialog1)
oGridModel = oDialog1.getModel().createInstance("com.sun.star.awt.grid.UnoControlGridModel")
oLabel1 = oDialog1.getModel().getByName("Label1")
rect(0) = oLabel1.getPropertyValue("PositionX")
rect(1) = 10
rect(2) = oLabel1.getPropertyValue("Width")
rect(3) = oLabel1.getPropertyValue("PositionY") - 2*rect(1)
With oGridModel
.PositionX = rect(0)
.PositionY = rect(1)
.Width = rect(2)
.Height = rect(3)
End With
oColumnModel = oGridModel.ColumnModel
oCol = oColumnModel.createColumn()
oCol.Title = "Column 1"
oColumnModel.addColumn(oCol)
oCol = oColumnModel.createColumn()
oCol.Title = "Column 2"
oColumnModel.addColumn(oCol)
oCol = oColumnModel.createColumn()
oCol.Title = "Column 3"
oColumnModel.addColumn(oCol)
oDialog1.getModel().insertByName("grid", oGridModel)
oGrid = oDialog1.getControl("grid")
oListener = (CreateUnoListener("grid_", "com.sun.star.awt.grid.XGridSelectionListener"))
oGrid.addSelectionListener(oListener)
oDataModel = oGridModel.GridDataModel
oDataModel.addRow("a", Array("abcdefg", "hijkl", "mnopq"))
oDataModel.addRow("b", Array("abcd", "efghijk", "lmno"))
oDialog1.execute()
oDialog1.dispose()
End Sub
To get the values of the selected row, add a listener for the grid_selectionChanged event:
Sub grid_selectionChanged(ev)
Dim oRows() As Object, oLabel1 As Object, sCells(2) As String
oRows = ev.Source.getSelectedRows()
oLabel1 = oDialog1.getModel().getByName("Label1")
sCells(0) = oDataModel.getRowData(oRows(0))(0)
sCells(1) = oDataModel.getRowData(oRows(0))(1)
sCells(2) = oDataModel.getRowData(oRows(0))(2)
oLabel1.setPropertyValue("Label", "Selected values: " + sCells(0) + "," + sCells(1) + "," + sCells(2))
End Sub
If you did all correctly, by running OpenDialog you should get your grid:

Export Table in Query to email VBA

I'm trying to export one of my queries to email using VBA in a table format. Similar to when you go to external data and click and E-Mail and it adds an attachment to outlook. Except I want it in the body. I put the following code in a button.
I found and made some changes to some code. This is what I have.
Private Sub Command5_Click()
Dim olApp As Object
Dim olItem As Variant
Dim db As DAO.Database
Dim rec As DAO.Recordset
Dim strQry As String
Dim aHead(1 To 4) As String
Dim aRow(1 To 4) As String
Dim aBody() As String
Dim lCnt As Long
'Create the header row
aHead(1) = "Part"
aHead(2) = "Description"
aHead(3) = "Qty"
aHead(4) = "Price"
lCnt = 1
ReDim aBody(1 To lCnt)
aBody(lCnt) = "<HTML><body><table border='2'><tr><th>" & Join(aHead, "</th> <th>") & "</th></tr>"
'Create each body row
strQry = "SELECT * From qry_email"
Set db = CurrentDb
Set rec = CurrentDb.OpenRecordset(strQry)
If Not (rec.BOF And rec.EOF) Then
Do While Not rec.EOF
lCnt = lCnt + 1
ReDim Preserve aBody(1 To lCnt)
aRow(1) = rec("Part")
aRow(2) = rec("Description")
aRow(3) = rec("Qty")
aRow(4) = rec("Price")
aBody(lCnt) = "<tr><td>" & Join(aRow, "</td><td>") & "</td></tr>"
rec.MoveNext
Loop
End If
aBody(lCnt) = aBody(lCnt) & "</table></body></html>"
'create the email
Set olApp = CreateObject("Outlook.application")
Set olItem = olApp.CreateItem(0)
olItem.Display
olItem.To = "email#email.com"
olItem.Subject = "Test E-mail"
olItem.HTMLBody = Join(aBody, vbNewLine)
olItem.Display
End Sub
When I run the code, I get a "Run-time error '3061' too few parameters. Expected 1."
If i click debug i get this highlighted in yellow. Anybody help would be greatly appreciated!
Edit
I tried a different approach which actually gave me the list in the body of the email. But it does it for the whole table instead of just the one record I want. This is what the SQL looks like of the query.
SELECT tblePMParts.[Part#], tblePMParts.PartDescription, tblePMParts.Qty, tblePMParts.Price
FROM tblePMParts
WHERE (((tblePMParts.WOID)=[Forms]![fmremail]![Text1]));
How would I go about adding the WHERE to the code below.
Private Sub Command4_Click()
'On Error GoTo Errorhandler
Dim olApp As Object
Dim olItem As Variant
Dim olatt As String
Dim olMailTem As Variant
Dim strSendTo As String
Dim strMsg As String
Dim strTo As String
Dim strcc As String
Dim rst As DAO.Recordset
Dim rs As DAO.Recordset
Dim db As DAO.Database
Dim qry As DAO.QueryDef
Dim fld As Field
Dim varItem As Variant
Dim strtable As String
Dim rec As DAO.Recordset
Dim strQry As String
strQry = "SELECT tblePMParts.[Part#], tblePMParts.PartDescription, tblePMParts.Qty, tblePMParts.Price " & _
"FROM tblePMParts; "
strSendTo = "test#email.com"
strTo = ""
strcc = ""
Set olApp = CreateObject("Outlook.application")
Set olItem = olApp.CreateItem(olMailTem)
olItem.Display
olItem.To = strTo
olItem.CC = strcc
olItem.Body = ""
olItem.Subject = "Please Quote the Following!"
Set db = CurrentDb
Set rec = CurrentDb.OpenRecordset(strQry)
If Not (rec.BOF And rec.EOF) Then
rec.MoveLast
rec.MoveFirst
intCount = rec.RecordCount
For intLoop = 1 To intCount
olItem.Body = olItem.Body & rec("[Part#]") & " - " & rec("PartDescription") & " - " & rec("Qty") & " - " & rec("Price")
rec.MoveNext
Next intLoop
End If
MsgBox "Completed Export"
Set olApp = Nothing
Set olItem = Nothing
Exit_Command21_Click:
Exit Sub
ErrorHandler:
MsgBox Err.Description, , Err.Number
Resume Exit_Command21_Click
End Sub
I got it working. Here is the code in case anybody needs it.
Private Sub Command5_Click()
Dim olApp As Object
Dim olItem As Variant
Dim db As DAO.Database
Dim rec As DAO.Recordset
Dim strQry As String
Dim aHead(1 To 3) As String
Dim aRow(1 To 3) As String
Dim aBody() As String
Dim lCnt As Long
'Create the header row
aHead(1) = "Part#"
aHead(2) = "Description"
aHead(3) = "Qty"
lCnt = 1
ReDim aBody(1 To lCnt)
aBody(lCnt) = "<HTML><body><table border='2'><tr><th>" & Join(aHead, "</th><th>") & "</th></tr>"
'Create each body row
strQry = "SELECT tblePMParts.[Part#], tblePMParts.PartDescription, tblePMParts.Qty, tblePMParts.Price " & _
"FROM tblePMParts " & _
"WHERE (((tblePMParts.WOID)=" & [Forms]![fmremail]![Text1] & "));"
Set db = CurrentDb
Set rec = CurrentDb.OpenRecordset(strQry)
If Not (rec.BOF And rec.EOF) Then
Do While Not rec.EOF
lCnt = lCnt + 1
ReDim Preserve aBody(1 To lCnt)
aRow(1) = rec("[Part#]")
aRow(2) = rec("PartDescription")
aRow(3) = rec("Qty")
aBody(lCnt) = "<tr><td>" & Join(aRow, "</td><td>") & "</td></tr>"
rec.MoveNext
Loop
End If
aBody(lCnt) = aBody(lCnt) & "</table></body></html>"
'create the email
Set olApp = CreateObject("Outlook.application")
Set olItem = olApp.CreateItem(0)
olItem.Display
olItem.To = "Email"
olItem.Subject = "Test E-mail"
olItem.HTMLBody = Join(aBody, vbNewLine)
olItem.Display
End Sub
Somewhere in your code, put a line that says
X = [Forms]![fmremail]![Text1]
Put a breakpoint in your code (hopefully you know how to do that?) on that line. When the code breaks, press F8 to step to the next line, and then type ?X in the Immediate Window. Or you can just hover your mouse over the line with the break point. The point is, you need to see what your code thinks [Forms]![fmremail]![Text1] is equal to. If it's null, you have a problem with your reference. In that case, you may need to add ".Value" or ".Text" to the end of it.
Another thing to check is your datatype for WOID. if it's text, you need to surround it with single quotes.
strQry = "SELECT tblePMParts.[Part#], tblePMParts.PartDescription, tblePMParts.Qty, tblePMParts.Price " & _
"FROM tblePMParts " & _
"WHERE (((tblePMParts.WOID)='" & [Forms]![fmremail]![Text1] & "'));"

VBA Access Compile Error - Data Member Not Found - How to ignore?

I have the code below in a button in my forms in MS Access. The problem is that sometimes not all "strCTRL"s exist. In some forms they do, in some they don't. The whole code is 900+ lines long so I won't post all of it. It's a SQL query which references controls and extracts their value.
The problem comes when not all controls are present, then I get the error: Compile error: Method or data Member not found.
Is there a way to bypass the compile error or tell VBA to compile it only if it exists? I tried If...Nothing and On Error Resume Next, but they don't seem to work. There's also other objects that will not exist on each page, not just the ones below. So...any ideas? =/
Dim strCTRL1 As String
Dim strCTRL2 As String
Dim strCTRL3 As String
Dim strCTRL4 As String
Dim strCTRL5 As String
Dim strCTRL6 As String
Dim strCTRL7 As String
Dim strCTRL8 As String
Dim strCTRL9 As String
Dim strCTRL10 As String
DoCmd.SetWarnings False
On Error Resume Next
strCTRL1 = "[Control Number] = " & Me.Text684.DefaultValue & " "
strCTRL2 = "[Control Number] = " & Me.Label2210.DefaultValue & " "
strCTRL3 = "[Control Number] = " & Me.Label2295.DefaultValue & " "
strCTRL4 = "[Control Number] = " & Me.Label73.DefaultValue & " "
strCTRL5 = "[Control Number] = " & Me.Label160.DefaultValue & " "
strCTRL6 = "[Control Number] = " & Me.Label246.DefaultValue & " "
strCTRL7 = "[Control Number] = " & Me.Label332.DefaultValue & " "
strCTRL8 = "[Control Number] = " & Me.Label417.DefaultValue & " "
strCTRL9 = "[Control Number] = " & Me.Label506.DefaultValue & " "
strCTRL10 = "[Control Number] = " & Me.Text2285.DefaultValue & " "
You can create an array or list of the label names, then:
Dim LabelName As String
Dim LabelNames As Variant
LabelNames = Array("Text684", "Label2210", ...etc.)
' ...
LabelName = LabelNames(1)
strCTRL1 = "[Control Number] = " & Me(LabelName).DefaultValue & " "
That will compile, though - of course - fail at runtime for non-existing labels.
OK, thanks to #Gustav, you got your code to compile, and his suggestions, combined with On Error Resume Next will get your code to run without errors under any circumstance.
But there is no way to tell if your code is correct, because now, the compiler won't tell you which controls are misnamed or missing.
So instead, I would suggest an array-based approach like this:
Dim Ctl As Access.Control
Dim CtlValues() As String
Dim i as Long
i = 0
ReDim CtlValues 1 To Me.Controls.Count
For Each Ctl In Me.Controls
If Ctl.ControlType = acTextBox Then
i = i + 1
CtlValues(i) = "[Control Number] = " & CStr(Nz(Ctl.DefaultValue, "Null"))
End If
Next
ReDim Preserve CtlValues 1 To i
These 12 lines of code perform the same task that the 900 lines do (going by your example). This code will work in any form, regardless of how many controls there are, and what they are named. This code is way easier to understand and work with.
See if maybe an approach like this will work here.

Macro that makes a Excel-list/log of mails in an account/folder

Here is my macro. I assumed that I could understand the codes and then edit it and get what I wanted
First: The macro abends whitout any progress at all: "Compile error: userdefined typenot defined"
I dont even know what this meens :)
(I use the macro now in an new and emty workbook)
Sub ListAllItemsInInbox()
Dim OLF As Outlook.MAPIFolder, CurrUser As String
Dim EmailItemCount As Integer, i As Integer, EmailCount As Integer
Application.ScreenUpdating = False
Workbooks.Add ' create a new workbook
' add headings
Cells(1, 1).Formula = "Subject"
Cells(1, 2).Formula = "Recieved"
Cells(1, 3).Formula = "Attachments"
Cells(1, 4).Formula = "Read"
With Range("A1:D1").Font
.Bold = True
.Size = 14
End With
Application.Calculation = xlCalculationManual
Set OLF = GetObject("", _
"Outlook.Application").GetNamespace("MAPI").GetDefaultFolder(olFolderInbox)
EmailItemCount = OLF.Items.Count
i = 0: EmailCount = 0
' read e-mail information
While i < EmailItemCount
i = i + 1
If i Mod 50 = 0 Then Application.StatusBar = "Reading e-mail messages " & _
Format(i / EmailItemCount, "0%") & "..."
With OLF.Items(i)
EmailCount = EmailCount + 1
Cells(EmailCount + 1, 1).Formula = .Subject
Cells(EmailCount + 1, 2).Formula = Format(.ReceivedTime, "dd.mm.yyyy hh:mm")
Cells(EmailCount + 1, 3).Formula = .Attachments.Count
Cells(EmailCount + 1, 4).Formula = Not .UnRead
End With
Wend
Application.Calculation = xlCalculationAutomatic
Set OLF = Nothing
Columns("A:D").AutoFit
Range("A2").Select
ActiveWindow.FreezePanes = True
ActiveWorkbook.Saved = True
Application.StatusBar = False
End Sub
As stated in the MSDN Article, which I have already provided in the comments above, you can bind with MS Outlook either by Late Binding (LB) or Early Binding (EB) from MS Excel.
Now what exactly is LB and EB? For this I would again direct you to another MSDN link.
When you are using EB, you have to set a reference to the relevant application from Tools ~~> References and set your reference to the relevant Outlook Object Library. For example
In Layman Terms, Excel now understands what Outlook and MAPIFolder is. Similarly olFolderInbox is an Outlook constant which has a value of 6. So your original code would have worked without any errors if you would have set the reference.
When you are usign LB, you do not set a reference. You declare your objects as Objects. There are analyzed by Excel during runtime. More about this has already been covered in the link above. Also since we are late binding, we have to explicitly use 6 instead of olFolderInbox as that is what it's value is.
Hope this sets you on the right track. Do ask questions if you have any.

Dlookup VBA code with 2 criteria

I have an MS Access form with a project_ID field combo box and several other fields. Once the user selects the project_ID field, majority of the subsequent fields on the form are automatically populated. I am trying to add a field on the form that displays information not only based on the project_ID but also a Trans_ID. The catch is that I want the Trans_ID to be a text box on the form, in which the user can simply type in the Trans_ID and in another text box, the Error_DTL_1 field is displayed. This is the VBA code that I have generated so far:
Private Sub cboProjectID_Change()
Dim VarComboKey As Integer
Dim VarObjective As Variant
Dim VarStartDate As Variant
Dim VarEndDate As Variant
Dim VarRiskCategory As Variant
Dim VarTarDatSet As Variant
Dim VarErrorCount As Variant
Dim VarErrorCode As Variant
Dim VarErrorDTL As Variant
VarComboKey = Me.cboProjectID.Value
VarObjective = DLookup("[Objective]", "[Project_HDR_T]", "[Project_ID]= " & VarComboKey)
Me.txtObjective = VarObjective
VarStartDate = DLookup("[Start_Date]", "[Project_HDR_T]", "[Project_ID] = " & VarComboKey)
Me.txtStartDate = VarStartDate
VarEndDate = DLookup("[End_Date]", "[Project_HDR_T]", "[Project_ID] = " & VarComboKey)
Me.txtEndDate = VarEndDate
VarRiskCategory = DLookup("[Risk_Category]", "[Project_HDR_T]", "[Project_ID] = " & VarComboKey)
Me.txtRiskCategory = VarRiskCategory
VartxtTarDatSet = DLookup("[Targeted_Dataset]", "[Project_Targeted_Dataset]", "[Project_ID] = " & VarComboKey)
Me.txtTarDatSet = VartxtTarDatSet
VarErrorCount = DLookup("[Count_Error_Codes]", "[Project_Error_Final]", "[project_ID] = " & VarComboKey)
Me.txtErrorCount = VarErrorCount
VarErrorCode = DLookup("[ErrorCode]", "[Project_Error_Final]", "[project_ID] = " & VarComboKey)
Me.txtErrorCode = VarErrorCode
VarErrorDTL = DLookup("[Error_DTL_1]", "[Project_DTA_REV_T]", "[project_ID] = " & VarComboKey And "[Trans_ID] = forms![Quality Risk Assessment]!me.stTransID")
Me.txtErrorDTL = VarErrorDTL
End Sub
The two lines before the "End Sub" are my attempt at attacking this code. But every time i make a selection in the Project_ID combo box on the form, i get an error "Run time Error 13, Type Mismatch".
Can anyone help?
In the line...
VarErrorDTL = DLookup("[Error_DTL_1]", "[Project_DTA_REV_T]", "[project_ID] = " & VarComboKey And "[Trans_ID] = forms![Quality Risk Assessment]!me.stTransID")
...the "And" is outside the quotes, and the second clause seems to mix both the Forms! and me. ways of referencing. Try...
VarErrorDTL = DLookup("[Error_DTL_1]", "[Project_DTA_REV_T]", "[project_ID] = " & VarComboKey & " And [Trans_ID] = forms![Quality Risk Assessment]!stTransID.Value")
...and see if it works better. Alternatively, you could try...
VarErrorDTL = DLookup("[Error_DTL_1]", "[Project_DTA_REV_T]", "[project_ID] = " & VarComboKey & " And [Trans_ID] = " & me.stTransID.Value)
A recordset:
Dim rs As DAO.Recordset
sSQL = "SELECT p.Objective, p.Start_Date, p.End_Date FROM Project_HDR_T p " _
& "WHERE p.Project_ID = " & VarComboKey
Set rs = CurrentDb.OpenRecordset(sSQL)
If rs.EOF Then
MsgBox "oops"
Else
VarObjective = rs!Objective
VarStartDate = rs!Start_Date
VarEndDate = rs!End_Date
End If
And given that all your tables contain Project_ID, it should be possible to create a query that includes all the tables, furthermore, the query coud be saved and referenced with a parameter in code.
See also:
What is a Recordset in VBA? ... what purpose does it serve?
Recordset Object