I'm trying to create a userform that will add text to the Word document based on a mixture of inputs coming from the userform. It could be checkboxes, lists, or textboxes where one writes content. As I was looking around for codes to preserve the bookmarks that I use to transfer the inputs into Word text, I came across one that allowed me to successfully preserve the bookmark for future editing. I thought I could use the same method for multiple bookmarks, but it seems not to work. With an example, now.
I have TextBox1, TextBox2 (in the userform), Bookmark1, Bookmark2 (in the Word document), and CommandButton1 to give the ok to transfer info from the userform to the Word document. I want to make TextBox1's content appear where Bookmark1 is, and TextBox2's content where Bookmark2 is. Code I tried is:
Private Sub CommandButton1_Click()
Dim BMRange01 As Range
Set BMRange01 = ActiveDocument.Bookmarks("Bookmark1").Range
BMRange01.Text = Me.TextBox1.Value
ActiveDocument.Bookmarks.Add "Bookmark1", BMRange01
Dim BMRange02 As Range
Set BMRange02 = ActiveDocument.Bookmarks("Bookmark2").Range
BMRange02.Text = Me.TextBox2.Value
ActiveDocument.Bookmarks.Add "Bookmark2", BMRange02
End Sub
When I tried with a single bookmark (from the first "Dim" to the last BMRange01) it worked just fine, and I could edit and re-use the bookmark multiple times. When I try with two of them, however, only one of them is preserved (or rather, deleted and created back again). Specifically, the second one seems to be preserved, while the first one (Bookmark1) is deleted.
Make your life a bit easier and create a separate sub to handle the work. Eg:
Private Sub CommandButton1_Click()
ReplaceBookmarkText ActiveDocument, "Boomark1", me.TextBox1.Value
ReplaceBookmarkText ActiveDocument, "Boomark2", me.TextBox2.Value
End Sub
Sub ReplaceBookMarkText(doc As Document, bmName As String, txt)
Dim rng As Range
Set rng = doc.Bookmarks(bmName).Range
rng.Text = txt
doc.Bookmarks.Add bmName, rng
End Sub
Related
My task: I have multiple forms in one Word document and it has to be filled out with the same information such as company name, address, tax number, etc.
My experience with VBA is very limited so I used bookmarks and wrote some code:
Private Sub OKbutton_Click()
Dim FirmaName As Range
Set FirmaName = ActiveDocument.Bookmarks("FirmaName").Range
FirmaName.Text = Me.TextBox1.Value
Dim FirmaNameRio As Range
Set FirmaNameRio = ActiveDocument.Bookmarks("FirmaNameRio").Range
FirmaNameRio.Text = Me.TextBox1.Value
Me.Repaint
stinfo.Hide
The issue or challenge for me is, that I want to be able to "dynamically" change the data in the pop-up form. Now it works the way every time I press OK-button, it adds new data behind the previous, which is undesirable. If I make a mistake I want to change it only in the pop-up window, not in the document.
So is there any way to program it that the information typed in the pop-up window can rewrite the previous information?
It´s not necessary to use bookmarks or whatsoever. It just has to work, that it´s easy for other employees to fill out these forms and save some time by skipping mechanical copy-pasting.
Use code like:
Sub UpdateBookmark(StrBkMk As String, StrTxt As String)
Dim BkMkRng As Range
With ActiveDocument
If .Bookmarks.Exists(StrBkMk) Then
Set BkMkRng = .Bookmarks(StrBkMk).Range
BkMkRng.Text = StrTxt
.Bookmarks.Add StrBkMk, BkMkRng
End If
End With
Set BkMkRng = Nothing
End Sub
which you would call with code like:
Call UpdateBookmark "FirmaName" Me.TextBox1.Value
And, since you're using bookmarks, instead of writing the same value multiple times to different bookmarks, simply insert a cross-reference to the first bookmark whose value is to be repeated and, when you've updated all the bookmarks, use a single code line to update all the cross-references:
ActiveDocument.Fields.Update
Since my documents usually have certain phrases that can be used over and over again, I want to hotkey them all to save time. To be more specific, I plan to make, let say, Macro1 (insert "Of course"), Macro2 (insert "The issue is"), and Macro3 (insert "by the time"), hotkeyed to Alt +1,2,3 respectively. If I press Alt + 2, "The issue is" got inserted instantly at my cusor.
Here's the base macro I'm using:
Sub Macro1()
Selection.TypeText Text:="sample text"
End Sub
The problem is for any new document, the content of Macro1, 2, and 3 will often need to be changed. I plan on creating an excel file to house all the phrases for ease of edit, but don't know how to link its specific cells back to the word macros. Is there a way to do that?
Note: I'm aware of the AutoText or Building Block function in word, but I don't find them as easy to edit as an excel table.
Here is an example, assuming yor Excel file is located in "C:\temp\" and yor phrases are saved in first Worksheet and First column:
This Function will read the first cell (A1) from this excel file:
Function Read_Excel_Cell(cellRin As Long) As String
Dim oExcel As Excel.Application
Dim myWB As Excel.Workbook
Set oExcel = New Excel.Application
Set myWB = oExcel.Workbooks.Open("C:\temp\phrases.xlsx")
Read_Excel_Cell = myWB.Sheets(1).Cells(cellRin, 1)
myWB.Close
Set myWB = Nothing
Set oExcel = Nothing
End Function
And this Macro will insert it into document:
Sub InsertText1()
Selection.TypeText Text:=Read_Excel_Cell(1)
End Sub
So you can create in this way as many macros as you need and using the same function Read_Excel_Cell() you can refere to another row.
To make this running in the Word vba you must add Excel Reference (in Tools\References tick Microsoft Excel Object Library).
I have a workbook with a custom right click function that extracts cell values from another workbook depending on what the user chooses. It works very well, I just take in the cell's value from the other workbook. Some cells contain hyperlinks though, and I'd like to import the functional hyperlink, not the value of what's shown in the cell. For example, the following image contains a hyperlink in cell (Y216) of sheet BOS of the input workbook.:
This is an image of the cell I want to copy. It is indeed a hyperlink.
?application.Workbooks(2).Sheets("BOS").Range("Y216").value
returns MKB 70-203 Wicket Shear Pin Detection System, which is indeed correct.
But how do I take the hyperlink's destination? I tried several things including
?application.Workbooks(2).Sheets("BOS").Range("Y216").Hyperlinks.count
returns 0 even though you can see in the image that the hyperlink does have an address. In the same fashion the following sub doesn't enter the For Each because it counts 0 hyperlinks.
Sub HLtester()
Dim HL As Hyperlink
For Each HL In Application.Workbooks(2).Sheets("BOS").Range("Y216").Hyperlinks
Debug.Print HL.Address
Next
End Sub
Expected output would be the link's target J:\SOUM\3191.... as shown in image.
EDIT
If it's important the cell's formula is
=LIEN_HYPERTEXTE("J:\SOUM\3191 M - Old Hickory Dam\11_BOS_FT\02_FT_MECT\21-200 Headcover";"21-200 Headcover")
That's the =HYPERLINK function of French Excel, by the way. I guess in last resort I can take the formula and cut off the function parts to retrieve the link part?
Your command works for me, I don't know why you set the range if you want to loop through all the hyperlinks in the sheet -neither why you set as application. workbooks-, anyways, this worked fine for me:
Sub HLtester()
Dim HL As Hyperlink
For Each HL In Sheets("Sheet1").UsedRange.Hyperlinks
Debug.Print HL.Address
Next
End Sub
You may get it as well within range methods with the following
ActiveCell.Hyperlinks(1).Address
You may get more info here
Edit:
Probably the count is wrong because of the "application.workbook", try to declare it as a variable instead of using it all over the code
Sub HLtester()
Dim HL As Hyperlink
Dim WBAnalyzed As Workbook: Set WBAnalyzed = Workbooks("MyWB.xlsm")
For Each HL In WBAnalyzed.Sheets("Sheet1").UsedRange.Hyperlinks
Debug.Print HL.Address
Next
End Sub
Edit 2:
This is the approach suggested when the hyperlink it's given by its formula
Sub test()
On Error Resume Next 'means no formula
x = Evaluate(Range("A1").Formula)
x1 = Sheets("Sheet1").UsedRange.Hyperlinks.Count
Debug.Print x
Debug.Print x1
End Sub
PS: I saved my variable declaration -just cause-, but, you should always have a neat control for them and use option explicit at the beginning of the module.
I have a set of ActiveX controls and subs that fit together like this:
User types into an ActiveX TextBox,
TextBox_Click triggers execution of 3 subs.
The first sub updates the value of a named range on a sheet; based on that updated value a table is created in excel using an offset function
The second sub copies the table range updated in 3 and paste.values into a range on the sheet (to eliminate formulas)
Here's where it is breaking down.
When I set the sheet up I create one ListBox (ListBox1). The third of the three subs mentioned in #2 above tests to see if ActiveX 'ListBox1' exists in the sheet. If not, the intention is the code will determine what the ListBox is called (LB1 or LB2 or LB2 etc), changes the name to 'ListBox1', and pastes the table from #4 above into ListBox1. Then a ListBox1_Click command could trigger a unique set of code to execute.
Right now I'm just testing switching between the names ListBox1 and ListBox2.
The idea is that as a user types into TextBox1 for example, ListBox1 updates at every keystroke. So type "g" and the list shows 20 names starting with the first "g" in the database sheet. Then type "ge" and the list shows 20 names starting with the first "ge" in the database sheet etc etc. The user clicks on a name and ListBox1_click does something unique. If the user was typing in TextBox2 they would see different data in the ListBox (which I would like to rename as ListBox2) and if they click a unique set of code is executed by ListBox2_Click.
The problem in #5 is the I get error 'Object doesn't support this property or method' the FIRST time I try to execute. The second time it is ok. If I do something else on the sheet and come back to it i get the error again.
Here is the code for #5 which is in a Module:
Sub PutListInListBox()
Dim OBJ As Object
On Error Resume Next
Set OBJ = ActiveSheet.OLEObjects("ListBox1")
On Error GoTo 0
If OBJ Is Nothing Then
ActiveSheet.ListBox2.Name = "ListBox1"
End If
ActiveSheet.ListBox1.Clear
ActiveSheet.ListBox1.List = Sheets("Search Criteria Control").Range("G1:G21").Value
End Sub
I have no idea what I'm doing wrong and any help is appreciated.
I hope I'm making this clear.
UPATED CODE
I have a bunch of ActiveX Lables and Text boxes so I used the first form you suggested to search for specific name Cases. I've been reading about it and can't see what I have wrong here. After typing in TB5, the LB doesn't update. I click back to Design Mode and can see that the name is still ListBox2.
Any ideas?
Private Sub TextBox5_Change()
Call UpdateValues(TextBox5.Value)
Call CopyTable
Dim OLEOBJ As OLEObject
For Each OLEOBJ In ActiveSheet.OLEObjects
Select Case OLEOBJ.Name
Case "ListBox1", "ListBox2", "ListBox3"
OLEOBJ.Name = "ListBox1"
OLEOBJ.ListFillRange = Sheets("Search Criteria Control").Range("G1:G21").Address(external:=True)
End Select
Exit For
Next
End Sub
I'll concentrate on your Code#5.
First, if you are changing the name of the ListBox Object, it is better to iterate the Object Collection it belongs to since you are not sure of it's name. Something like this:
Dim oleobj As OLEObject
Dim sh As Worksheet: Set sh = ActiveSheet
For Each oleobj In sh.OLEObjects
Select Case oleobj.Name
Case "LB1", "LB2", "ListBox1", "ListBox2" 'put the possible names here
oleobj.Name = "ListBox1" 'change it to the name you want
End Select
Exit For 'if you have more than 1 ListBox
Next
If you just want to change the name of an existing ListBox, then skip the checking.
For Each oleobj In sh.OLEObjects
oleobj.Name = "ListBox1"
Exit For 'if you have more than 1 ListBox
Next
Now, to assign values or list to it, you can directly assign the source Range, using ListFillRange Property after you've change its name.
oleobj.ListFillRange = Sheets("Search Criteria Control") _
.Range("G1:G21").Address(, , , True) 'add this line within the loop
Take note that you need not clear the previous list. It will automatically update. Since I'm not sure what you want to achieve entirely, I'll stop here. HTH though.
I have a form with listbox which dynamically provides a list of the worksheets in the current workbook (code below). I wish to take the selected Sheet and refer to it in a formula later in the process. From hours of playing around I cannot seem to accomplish this. I believe I read somewhere that you cannot take the string back to the sub and use it to refer to to an object. So I thought maybe I can create two listboxes
for sheet name
for sheet index
that I could pass the index number to and maybe use that in my formula to lookup items from the correct sheet.
For the life of my I cannot seem to find a way to connect the two since the items will always be changing; the code will be ran on multiple workbooks by multiple operators so the layout will most likely change between users. I can easily add the second list box with index #'s but I have a block on how to associate the name which will have meaning to the user and the index which I can pass back to the sub. I realize the "On click" procedure for the list box to associate the two but with the dynamic nature of the fields I cannot come up with the logic to put that into code.
For N = 1 To ActiveWorkbook.Sheets.Count
With ListBox1
.AddItem ActiveWorkbook.Sheets(N).Name
End With
Next N
Try this out.
Declare a public variable above the code for the UserForm, making it available throughout your workbook from any module or code.
Public listChoice As String
Using your code to get the sheet names for the ListBox rowsource.
Private Sub UserForm_Activate()
For n = 1 To ActiveWorkbook.Sheets.count
With ListBox1
.AddItem ActiveWorkbook.Sheets(n).name
End With
Next n
End Sub
Including an update event for the ListBox
Private Sub ListBox1_AfterUpdate()
listChoice = ListBox1.Text
End Sub
I included a test just to demonstrate that the result is still retained. You don't need this, it demonstrates the results on the screenshot.
Private Sub cmdTestChoice_Click()
MsgBox ("The choice made on the ListBox was: " & listChoice)
End Sub
edit: To access that sheet later, you can call it using something like this:
Some examples of different ways to access a cell, using .Range, or .Cells, with numbers or letters.
Using lRow & lCol as Long to set row and column numbers.
Sheets(listChoice).Cells(lRow, lCol).Value = TextBox1.Value 'Set cell on sheet from TextBox
TextBox2.Value = Sheets(listChoice).Range("A2").Value 'Set TextBox From Cell on Sheet
'Set a cell on another sheet using the selected sheet as a source.
Sheets("AnotherSheet").Cells(lRow, "D") = Sheets(listChoice).Range("D2")