"ActiveWindow.ScrollIntoView Selection.Range, True" does not work - vba

I need VBA (Word) to scroll to a certain location in the document before a messagebox (msgbox) in the macro is displayed.
I have tried:
ActiveWindow.ScrollIntoView Selection.Range, True
I have added DoEvents before the Msgbox call. Didn't work.
I have added a bookmark and then selected the bookmark before the Msgbox
I have added a sleeptimer, and other delays, before the Msgbox. Didn't work.
I have tried Application.Screenrefresh
Nothing works. Msgbox always displays with the cursor in the location it was located before the macro ran.
HOWEVER, if I step through the code in the VBA editor, all works fine. It's only when I am running the code 'straight through' that it never observes the ScrollIntoView command before showing the msgbox.
(Start at top of document)
(Search for text on, let's say page 3; if found . . .)
ActiveWindow.ScrollIntoView Selection.Range, True
DoEvents
Application.ScreenRefresh
Selection.Bookmarks.Add "Prompt"
Selection.Bookmarks("Prompt").Select
DoEvents
Selection.Range.Select
MsgBox " TASK: " & PromptIs & String(2, 13) & " Click OK below and perform the stated task."
The cursor should be at the found text and the text should be 'in view' before the message box is displayed. It's not.

Related

MS Word filename from bookmarks with VBA

Assuming I have:
a word template including macro: custom_template.dotm;
two bookmarks in this template: 'first_name" and "last_name".
I would like that on "Save" event, in the dialog box, the application proposes to user, instead of "document1", and only if relative bookmarks exist, the filename "Document of first_name second_name.docx".
Can anybody explain me how to achieve this with VBA?
Thanks.
=== UPDATE ===
Now I've this code, working well when I execute it.
I would like it runs automatically when user clicks on "save document".
Sub Demo()
Dim sFlNm As String
With ActiveDocument
sFlNm = "Document of " & .Bookmarks("first_name").Range.Text & " " & .Bookmarks("last_name").Range.Text
End With
With Dialogs(wdDialogFileSaveAs)
.Name = sFlNm
.Show
End With
End Sub
For a macro to run at the client's end, you would have to send a macro-enabled template or document. Many people who are running anti-virus software will get a warning of a possible Word virus. Then the user will have to manually enable the macros. Are they going to bother?
Making it run automatically with a Save command may have unintended consequences. You'll have to check whether the bookmarks have actually been filled, and using the Save command while you're revising the document can save it with a new file name. But you asked, so here's how: rename the macro as FileSave. Then when you choose Ctrl + S or File>Save in Word, the dialog will automatically pop up:
Sub FileSave()
Dim sFlNm As String
With ActiveDocument
sFlNm = "Document of " & .Bookmarks("first_name").Range.Text & " " & .Bookmarks("last_name").Range.Text
End With
With Dialogs(wdDialogFileSaveAs)
.Name = sFlNm
.Show
End With
End Sub
In Word, to create a macro that runs automatically when you choose a Word command, follow these steps:
Choose Developer>Macros.
Change the Macros in dropdown to Word commands.
Choose the command name you want to re-purpose.
Change the Macros in dropdown back to the macro-enabled document or template that you're developing.
Click on the Create button. A new macro is created in the VBE with the correct command name. Fill in the macro with whatever you want the macro to do.

How to determine if text is selected in Microsoft Word VBA..?

In Word VBA, I would like to know the proper method to determine if text is selected. I may have the answer (shown below), but I want to ask if it's correct.
This is confusing, because most of the properties show no difference between nothing selected and one character selected.
An obvious possibility is If Selection.End - Selection.Start > 0, but this seems unnecessary. Is there not a specific property that is True or False..?
I see the property Selection.Type, but the documentation is very thin and unhelpful. It does not explain any details, such as the definitions of Normal, Inline, Block or any of the others. And searching Google for these answers was not helpful.
Through experimentation, I may have found the answer:
Selection.Type=wdSelectionIP seems to be when nothing is selected.
Selection.Type=wdSelectionNormal seems to be when any text is selected.
Is this the correct & reliable way to do it..?
Compare Selection.Type against wdSelectionIP. I have provided the code sample as below:
.
'Simple check if text is selected:
If (Selection.Type <> wdSelectionIP) Then
Debug.Print "You have selected: " & Selection.Text
Else
Debug.Print "No text is selected."
End If
.
'Graceful exit with message - if no selection found:
If Not (Selection.Type <> wdSelectionIP) Then
MsgBox "Text not selected." & vbNewLine & vbNewLine & "Please select text before proceeding", vbOKOnly & vbExclamation, "Selection not found"
Exit Sub
End If
.
This was also righty suggested by #Cindy Meister.
Reference: Microsoft Documentation (Selection.Type property).

Can you interrupt the vba code to make a sheet selection?

I will try to be as clear as possible in the description, so here goes nothing:
I have created a code in which the user selects his excel file and then the macro copies the Sheet from that file into my macro Workbook.
MyFile = Application.GetOpenFilename()
Workbooks.Open (MyFile)
ActiveSheet.Copy After:=wbook.Sheets(1)
ActiveSheet.Name = "Selected file"
Workbooks.Open (MyFile)
ActiveWorkbook.Close SaveChanges:=False
This is working, but what I realized is, that there might be cases where the selected file has multiple Sheets.
Is there a way to write the macro in which if my selected file has 1 sheet it runs the above code and if it has more than one sheet to let me select the sheet I want and then run the rest of the code?
Edit:
I thought of another way to handle this — perhaps closer to what you were looking for . . .
It's just an expansion of the basic pause routine that I use occasionally.
This is my "regular" Pause routine (using the Timer function):
Sub Pause(seconds As Single)
Dim startTime As Single
startTime = Timer 'get current timer count
Do
DoEvents 'let Windows "catch up"
Loop Until Timer > startTime + seconds 'repeat until time's up
End Sub
...so, it gave me an idea.
Honestly, I was a little surprised to discover that this works, since it's basically running two sections of code simultaneously.
Code for WaitForUserActivity :
Here's the code I used in the demo above:
Option Explicit
Public isPaused As Boolean
Sub WaitForUserActivity() 'THE 'RUN DEMO' BUTTON runs this sub.
Dim origSheet As String
isPaused = True 'flag "pause mode" as "on"
origSheet = ActiveSheet.Name 'remember current worksheet name
MsgBox "This will 'pause' code execution until you" & vbLf & _
"click the 'Continue' button, or select a different a worksheet."
Application.StatusBar = "PAUSED: Click ""Continue"", or select a worksheet."
Do 'wait for button click or ws change
DoEvents 'yield execution so that the OS can process other events
Loop Until (Not isPaused) Or (ActiveSheet.Name <> origSheet)
If isPaused Then 'the active worksheet was changed
MsgBox "Worksheet '" & ActiveSheet.Name & "' was selected." _
& vbLf & vbLf & "Now the program can continue..."
Else 'the button was clicked
MsgBox "The 'Continue' button was clicked." _
& vbLf & vbLf & "Now the program can continue..."
End If
Application.StatusBar = "Ready"
End Sub
Sub btnContinue() 'THE 'CONTINUE' BUTTON runs this sub.
isPaused = False 'flag "pause mode" as "off"
End Sub
To run the demo:
place the above code in a regular module
make sure the workbook has at least two worksheets
create two command buttons:
one for the "Run Demo" button, assign macro: WaitForUserActivity
one for the "Continue" button, assign macro: btnContinue
click the "Run Demo" button
The key command in the code is the DoEvents Function, which "yields execution so that the operating system can process other events."
DoEvents passes control to the operating system. Control is returned after the operating system has finished processing the events in its queue and all keys in the SendKeys queue have been sent.
DoEvents is most useful for simple things like allowing a user to cancel a process after it has started, for example a search for a file. For long-running processes, yielding the processor is better accomplished by using a Timer or delegating the task to an ActiveX EXE component - and the operating system takes care of multitasking and time slicing.
Any time you temporarily yield the processor within an event procedure, make sure the procedure is not executed again from a different part of your code before the first call returns; this could cause unpredictable results.
Further details (and warnings) at the source.
Original Answer:
Some suggested solutions:
Instead of "stopping" the code you could prompt the user to specify which worksheet.
The easiest way would be with an InputBox where the user would enter an ID number or otherwise identify the worksheet.
More complicated but more robust and professional-looking would be a custom dialog box with the help of a userform. There are several examples and tutorials online such as this one.
You could "pause" execution to give the user a set amount of time to select a worksheet, with a simple timer loop, ad you could even check the worksheet name to see if the user picked a new one, something like this:
Dim startTime As Single, shtName As String
If ThisWorkbook.Worksheets.Count = 1 Then
MsgBox "There is only one worksheet in this workbook."
Else
shtName = ActiveSheet.Name 'get name of active sheet
MsgBox "You have 5 seconds to select a worksheet after clicking OK.", _
vbOKOnly + vbInformation, "Select a worksheet... fast!"
startTime = Timer
Do
DoEvents
Loop Until Timer > startTime + 5
'check if user picked a new worksheet
If ActiveSheet.Name = shtName Then
MsgBox "You didn't select a new worksheet!"
Else
MsgBox "Thanks for selecting a new worksheet!"
End If
End If
It's a little hoakey but could work, especially if proper checks to make sure you've got the correct worksheet now.
I suppose you could create an worksheet event procedure that would run when a worksheet is activated, and checked a global variable to see if your "import procedure" was running, and if so, resume your code... but that would be messy and confusing and would require the code to exist in the workbook you're "importing".
Or, better than any of those would be to programmatically/logically determine which worksheet you need based on the contents of the worksheet. Is there a title? A certain date? Maybe the newest worksheet? Something in a certain cell? There must be something that differentiates it from the others.
Hopefully this gives you some ideas towards a non-linear solution. 😉
As in whole, I would recommend ashleedawg's solution, but if you
insisted on maintaining your code structure, your code could look
something like this:
You can distinguish between amount of Sheets a Workbook has using .Count property of the Sheets object (or Worksheets if you do not want to include Charts) and use InputBox to check for the sheet you want to look for.
MyFile = Application.GetOpenFilename()
Workbooks.Open (MyFile)
If ThisWorkbook.Sheets.Count = 1 Then
ThisWorkbook.ActiveSheet.Copy After:=wbook.Sheets(1)
ThisWorkbook.ActiveSheet.Name = "Selected File"
Else
Dim checkfor As String
checkfor = InputBox("What Sheet should I execute the code for?")
Dim i As Integer
For i = 0 To ThisWorkbook.Sheets.Count
If Trim(LCase(checkfor)) = Trim(LCase(Sheets(i).Name))) Then
ThisWorkbook.Sheets(i).Copy After := wbook.Sheets(1)
ThisWorkbook.Sheets(i).Name = "Selected file"
End If
Next i
End If
Workbooks.Open (MyFile)
ActiveWorkbook.Close SaveChanges:=False
Might need some further tweaking, because I was unsure what exactly you wanted to achieve.

Excel "Workbook_BeforeClose" event not firing again after canceled

Update: After more research, I found this duplicate question: Excel 2016 Workbook.BeforeClose event firing every other time bug. It seems I was using the wrong keywords, and that this is a bug, not a problem with my code. However, I cannot seem to download the version mentioned in the solution. I am running Windows 7 and using Microsoft Office 365 Pro Plus, and Office is stating that the most up to date version available is 16.0.6965.2105
I am trying to use the Workbook_BeforeClose event to test whether a checkbox is checked or not. If not, the user is prompted on whether they want to continue closing with out checking the box. If they choose "Yes," the sheet is cleared and the workbook is saved. If they choose, "No," the box is checked and, "Cancel" is set to true.
This works fine the first time the Workbook_BeforeClose event runs. However, the second time the sheet is closed, the standard Excel "Want to save your changes to..." dialogue box comes up and the Workbook_BeforeClose event does not fire. If I click cancel on the dialogue box and close the workbook a third time, the event fires. Something is being reset when, "Cancel," is clicked in the dialogue box, but I can't figure out what it is. My code is below:
Public Closing as Boolean
Sub Workbook_BeforeClose(Cancel As Boolean)
Debug.Print "Workbook_BeforeClose"
If Closing = True Then Exit Sub 'Closing is used as a switch to stop the event from looping on Application.ThisWorkbook.Close below
Closing = True
If Sheets(1).DraftCheckBox = False Then
If MsgBox("This file is not being saved as a draft. This workbook will be cleared if the draft box is not checked." & vbCr & vbCr & "Would you like to continue?", vbYesNo, "Warning") = vbYes Then
'If "Yes" selected
'Stuff happens here
Application.ThisWorkbook.Close savechanges:=True
Else
'If "No" selected
Sheets(1).DraftCheckBox = True
Cancel = True
End If
End If
Closing = False
End Sub
I know for a fact that Application.EnableEvents is set to True. I also know that the event itself is not firing because there is a stop on the very first line at "Debug.Print" This stop is activated after the first close and I can step through the code. It does not activate at all after the second close, either before or after the dialogue box. Is there anything that I should be doing to prevent the dialogue box from coming up and to force the Workbook_BeforeClose event every time the workbook is closed?
You are already closing the workbook you don't want to call close again, use Save instead.
You also don't need the extra variable if you use the code below. One big question remains...how is your user imitating the Close event? If by the red X then the code below should work. If by a button on your form what does that button code look like?
Note: This is untested code as I don't have your workbook.
Sub Workbook_BeforeClose(Cancel As Boolean)
If Sheets(1).DraftCheckBox = False Then
If MsgBox("This file is not being saved as a draft. " + _
"This workbook will be cleared if the draft box is not checked." _
& vbCr & vbCr & "Would you like to continue?", _
vbYesNo, "Warning") = vbYes Then
'If "Yes" selected
'Stuff happens here
Application.ThisWorkbook.Save
Cancel = False 'Just to be sure.
Else
'If "No" selected
Sheets(1).DraftCheckBox = True
Cancel = True
End If
End If
End Sub
HTH

Strange behavior from VBA DataObject. GetText returns what is currently on the clipboard

I posted a question earlier about an error raised by the MSForms DataObject as accessed from Microsoft Office Excel 2013 VBA. As I was writing that post, I discovered other odd behavior that is even more concerning.
Perhaps my perceptions about the DataObject are wrong, but if so, the MS Office documentation is very misleading. My expectations are these:
If I create a DataObject and use the GetFromClipboard method, it should load whatever is on the clipboard into the object. The data stored in the object should NOT change until I perform some other action on the object (such as calling Clear, SetText, etc)
So if I perform the following:
Manually copy some Text onto the windows clipboard.
Create a DataObject and call GetFromClipboard
Perform some VBA operations that change the windows Clipboard (but do NOT access the DataObject)
Call GetText on the DataObject
I would expect that the text I retrieve in step 4 is the same that I placed on it in #2.
However, this IS NOT the case, as my example code below shows.
Instructions for testing:
Copy this code into a standard code Module in an office application.
Copy some text (e.g. from Notepad)
Run the method "TestDataObject"
When prompted, copy some different text.
When prompted a second time, copy some other different text.
(You may need to add a reference to "Microsoft Forms 2.0 Object Library" which you can do quickly by simply adding a UserForm to your VBA project, because this adds the reference automatically)
'Copy some text before running this.
Public Sub TestDataObject()
Dim oData As DataObject
Set oData = New DataObject
'This is BEFORE GetFromClipboard is called, so
' the DataObject currently has NO text in it.
If oData.GetFormat(1) Then
Debug.Print "1) Contents: " & oData.GetText(1)
Else
'This line will be printed.
Debug.Print "1) Contents: (NONE)"
End If
oData.GetFromClipboard
'Now the DataObject has some text, and it will be printed below.
If oData.GetFormat(1) Then Debug.Print "2) Contents: " & oData.GetText(1)
MsgBox "Copy some Text"
'If you copied NEW text, then it will be shown below (instead of the original data)
If oData.GetFormat(1) Then Debug.Print "3) Contents: " & oData.GetText(1)
MsgBox "Copy some different Text"
'If you copied other NEW text, then it will be shown below (instead of the original data)
If oData.GetFormat(1) Then Debug.Print "4) Contents: " & oData.GetText(1)
End Sub
Assuming the text I copied before running the sub was "Hello", I expect this to print out the following, regardless of what I manually copy while the method is running:
1) Contents: (NONE)
2) Contents: Hello
3) Contents: Hello
4) Contents: Hello
But the actual output is this:
1) Contents: (NONE)
2) Contents: Hello
3) Contents: World
4) Contents: Goodbye
(Assuming I copied "World" when prompted the first time and "Goodbye" when prompted the second time.)
Note that the Msgbox is NOT causing this behavior. You could use a DoEvents-Loop for a couple seconds instead if you like. Or perform Copy/Paste operations with a Range object or other Excel objects as shown below:
Public Sub TestDataObject()
Dim oData As DataObject: Set oData = New DataObject
ThisWorkbook.ActiveSheet.Range("A1").Select
Selection.Value = "Hello"
Selection.Copy
If oData.GetFormat(1) Then
Debug.Print "1) Contents: " & oData.GetText(1)
Else
Debug.Print "1) Contents: (NONE)"
End If
oData.GetFromClipboard
If oData.GetFormat(1) Then Debug.Print "2) Contents: " & oData.GetText(1)
Selection.Value = "World"
Selection.Copy
If oData.GetFormat(1) Then Debug.Print "3) Contents: " & oData.GetText(1)
Selection.Value = "Goodbye"
Selection.Copy
If oData.GetFormat(1) Then Debug.Print "4) Contents: " & oData.GetText(1)
End Sub
This is not specific to Excel. The same code work in Word, except that you have to change the Selection/Copy code to this (e.g.):
' Code to copy text in Word
Selection.Text = "World"
Selection.Copy
So my question is: Is this behavior expected or is it a bug? I am using Office 2014 64-bit. Does this also happen in 32-bit Office? Maybe it is just a 64-bit bug.
Thanks!
I can replicate (32-bit Office 2010, Win7)
Sub Tester()
Dim d As New DataObject, d2 As New DataObject
d2.SetText "first"
d2.PutInClipboard
d.GetFromClipboard
Debug.Print d.GetText '--> "first"
d2.SetText "second"
d2.PutInClipboard
Debug.Print d.GetText '--> "second"
d2.SetText "third"
d2.PutInClipboard
Debug.Print d.GetText '--> "third"
End Sub
I'd have to guess that GetFromClipboard only establishes a link by reference to the clipboard, not by value. So whenever you call GetText it actually pulls directly from the clipboard, not from a copied cache held in the DataObject.
If you need a stable copy of the clipboard content which would be unaffected by subsequent copy operations then you'd have to store it in a (e.g.) String variable.
I encountered a similar problem with MS Access VBA:
My routine starts a software which automatically copies text contents from my smartphone's clipboard, then makes a loop until the desired text is available in the pc's clipboard.
The text (special bill format) comes from a QR scanning app on my smartphone.
However, I always got errors, sometimes with DataObj.GetFromClipboard and sometimes with ClipText = DataObj.GetText(1) . When I resumed the procedure with <F5> or <F8> everything ran then without any error.
' …
Dim ClipData As New DataObject 'object to use the clipboard
ClipData.SetText Text:="×××"
ClipData.PutInClipboard 'overwriting old text contents
' …
Application.Echo (False)
On Error Resume Next
For WarteZeit = 1 To 80
DataObj.GetFromClipboard ' get data from the clipboard
If DataObj.GetFormat(1) = True Then ' Text im Clipboard
ClipText = DataObj.GetText(1) ' get clipboard contents
If Left(ClipText, 3) = "SPC" And ((InStr(ClipText, Chr(10)) = 4) _
Or (InStr(ClipText, Chr(13)) = 4)) Then
Exit For
End If
End If
Sleep 750 ' 80 × 750 ms = 1 Minute
Next WarteZeit
On Error GoTo 0
Application.Echo (True)
' …
The clipboard is obviously quite slow and poorly multitasking. While the clipboard is active with loading new data from one software, it still contains the old data or is not accessible at all leading to errors (no Windows error number).
Above code includes my solution to the problem: one just has to be patient and to make sure that the new contents have reached the clipboard before calling them back.
Gruß Gott :-)
I wonder if the phenomena discussed here could be explained slightly differently:
It seems to me that the data object and the Windows Clipboard are somehow linked quite closely, but in a way that maybe, either, no one knows anymore exactly, or, those that do are not telling as it is propriety information . In addition possibly there are some rules , coding, or similar that govern how the data object and Windows Clipboard copes with the spaghetti of different Clipboards ( Office, Windows, Excel etc… ) and different versions of copied data therein. I doubt anyone in the meantime is capable of unravelling that spaghetti to make any clear sense of it.
Part of the monster that is “clipboards” is a true OLE object , the Data object. We have access to that.
Our Data object is perhaps more of a hooked Events thing which monitors the Windows Clipboard. We can set things that may then be used. We can influence with the Data object the behaviour of the clipboards
My experimenting suggest to me that there is some registers not directly accessible to us, and which we can influence and which is somehow strongly related to what is in the Window Clipboard. I think we are only privy to some of its behaviour.
I am thinking that .GetText() returns the last thing added in to a register.
Some examples to help shed some explanation to the strange behaviour:
Sub Copying()
This routine initially fills Office, Windows, and Excel Clipboards via an Excel range copy
.PutInClipboard and .GetText() would fail initially as the registers are not filled to which they refer.
.GetFromClipboard somehow now adds something to a register in the data object; I think that is getting data from the Windows Clipboard.
I clear the Office and Excel Clipboard to demonstrate they are not used further in the code. However I note that I must do the .GetFromClipboard before clearing those: clearing in this case either of the Office or Excel clipboard seams to make the Windows clipboard empty. I am not sure exactly why this should be the case, other than the spaghetti of dependencies in the clipboards somehow coming into play at this initial point ### I do not think that is generally true that we can empty the Windows clipboard by emptying the Office or Excel Clipboards
.GetText() now gives me the value from the copied cell. But I believe this is telling me what I last added to a register in the data object.
Now I use .SetText , and I believe I am adding something again to a register in the data object.
.GetText() now gives me the text that I added, “New Text”
I am proposing that at this point, in this particular code, I still have the value from the cell as coming from a clipboard and this is in a main register. I am not too sure where/ how the “New Text” is at this point. ( There are cases I have seen where this “New Text” would replace the text in the windows clipboard by a .PutInClipboard. This does not happen in this routine, strangely)
If I attempt a paste at this point, it would error. I think this is reasonable: At this point I think I have no data in the Windows Clipboard. ### In this particular example the windows clipboard has been emptied by the code lines emptying the Office or Excel clipboard: This is not usually the case ###
.PutInClipboard code line does now not error.
Nor does the paste a few lines down.
What one might not have expected is that the value pasted is not the “New Text”, but in fact, the original text from the cell
( And note also that this time we could clear the Office and Excel clipboards – with or without these lines the result is the same - Code line ActiveSheet.Paste Destination:=ActiveSheet.Range("C1") is apparently using the Windows Clipboard )
Option Explicit ' https://stackoverflow.com/questions/25091571/strange-behavior-from-vba-dataobject-gettext-returns-what-is-currently-on-the-c
' YOU NEED routine, ClearOffPainBouton() - get here, or just comment out Call s to it : --- https://pastebin.com/5bhqBAVx , http://www.eileenslounge.com/viewtopic.php?f=30&t=31849&start=20#p246838 http://www.excelfox.com/forum/showthread.php/2056-Appendix-Thread-(-Codes-for-other-Threads-HTML-Tables-etc-)?p=11019&viewfull=1#post11019 --- it will be best to copy entire coding here to a seperate code module
Sub Copying()
Range("C1").Clear
Dim DtaObj As Object ' Late Binding equivalent' If you declare a variable as Object, you might be late binding it. http://excelmatters.com/2013/09/23/vba-references-and-early-binding-vs-late-binding/ ... if you can .... http://www.eileenslounge.com/viewtopic.php?f=30&t=31547&start=40#p246602
Set DtaObj = GetObject("New:{1C3B4210-F441-11CE-B9EA-00AA006B1A69}") ' http://excelmatters.com/2013/10/04/late-bound-msforms-dataobject/
Let Range("A1").Value = "CellA1": Range("A1").Copy ' This probably fills the Excel Clipboard, the Window Clipboard and the Office Clipboard
' DtaObj.PutInClipboard ' ' This will fail, DtaObj clear
' MsgBox Prompt:="DtaObj.GetText(1) is " & DtaObj.GetText() ' This will fail, DtaObj clear
DtaObj.GetFromClipboard '
Let Application.CutCopyMode = False ' This clears the Excel Clipboard
Call ClearOffPainBouton ' This clears the Office Clipboard
MsgBox prompt:="DtaObj.GetText() is " & DtaObj.GetText() ' --- "DtaObj.GetText() is CellA1"
DtaObj.SetText Text:="New Text" '
MsgBox prompt:="DtaObj.GetText() is " & DtaObj.GetText() ' --- "DtaObj.GetText() is New Text"
' ActiveSheet.Paste Destination:=ActiveSheet.Range("C1") ' This would error here
DtaObj.PutInClipboard
Let Application.CutCopyMode = False ' This clears the Excel Clipboard
Call ClearOffPainBouton ' This clears the Office Clipboard
ActiveSheet.Paste Destination:=ActiveSheet.Range("C1") ' --- "CellA1" is pasted in cell C1
End Sub
Sub Copying2()
This takes the last Sub further .. here an short walk through the coding
' New bit below
In the next 6 lines I have a feeling that the .PutInClipboard and the .GetFromClipboard are not doing much , if anything at all. Possibly the Excel knows that I have not changed any data, so it ignores attempts to do things that usually would be applied to some new data.
' Manual copy
You are prompted to copy anything. You should do this
Immediately after, .GetText() has not changed, but you get now pasted out the value that you copied. This suggest once again that code line ActiveSheet.Paste Destination:=ActiveSheet.Range("C1") is apparently using the Windows Clipboard
After using .GetFromClipboard , you will now find that .GetText() returns the value that you copied
Attempt to use .SetText to add to windows Clipboard
We Set the Text (do a .SetText) and do a .PutInClipboard. But as we have seen, this does not change the clipboard on this case, and the last thing we manually copied is still there to be pasted out
I try .Clear
The next two lines would error. This makes sense: I have emptied the registers. The third line erroring is less obvious. It suggests that .Clear clears the Windows Clipboard. I am not sure if that would always be the case.
The final code lines are successful in adding, via .SetText , something to the windows clipboard. My explanation is that as all registers are empty, the value given via .SetText is the only thing there, it is added to an empty thing so that is what is in there and available to be Put in the Clipboard.
In this case, now, a second attempt in adding via SetText is also successful. Why it should be in this case is slightly puzzling.
Sub Copying2()
Range("C1").Clear
Dim DtaObj As Object '
Set DtaObj = GetObject("New:{1C3B4210-F441-11CE-B9EA-00AA006B1A69}")
Let Range("A1").Value = "cellA1": Range("A1").Copy ' This fills the Excel Clipboard, the Window Clipboard and the Office Clipboard
' DtaObj.PutInClipboard '
' MsgBox Prompt:="DtaObj.GetText(1) is " & DtaObj.GetText()
DtaObj.GetFromClipboard
Let Application.CutCopyMode = False ' This clears the Excel Clipboard
Call ClearOffPainBouton ' This clears the Office Clipboard
MsgBox prompt:="DtaObj.GetText() is " & DtaObj.GetText()
DtaObj.SetText Text:="New Text"
MsgBox prompt:="DtaObj.GetText() is " & DtaObj.GetText()
' ActiveSheet.Paste Destination:=ActiveSheet.Range("C1") ' This would error here
DtaObj.PutInClipboard
Let Application.CutCopyMode = False ' This clears the Excel Clipboard
Call ClearOffPainBouton ' This clears the Office Clipboard
ActiveSheet.Paste Destination:=ActiveSheet.Range("C1")
' New bit below - first 6 lines are not doing much if at all
Range("C1").Clear
DtaObj.PutInClipboard
MsgBox prompt:="DtaObj.GetText() is " & DtaObj.GetText()
DtaObj.GetFromClipboard
MsgBox prompt:="DtaObj.GetText() is " & DtaObj.GetText()
ActiveSheet.Paste Destination:=ActiveSheet.Range("C1")
' Below we manually copy
MsgBox prompt:="Please copy anything from anywhere , before hitting OK "
MsgBox prompt:="DtaObj.GetText() is " & DtaObj.GetText() ' has not changed
Range("C1").Clear
ActiveSheet.Paste Destination:=ActiveSheet.Range("C1") ' pastes what you copied
DtaObj.GetFromClipboard
MsgBox prompt:="DtaObj.GetText() is " & DtaObj.GetText() ' now shows what you copied
' Attempt to use SetText to add to windows Clipboard
DtaObj.SetText Text:="New Text To Paste"
MsgBox prompt:="DtaObj.GetText() is " & DtaObj.GetText() ' reflects the added text, "New Text To Paste"
DtaObj.PutInClipboard ' This either does nothing or once again puts what you copied there - as it already is, then Excel may know you already did this so does nothing
ActiveSheet.Paste Destination:=ActiveSheet.Range("C1") ' pastes what you copied
DtaObj.Clear
' MsgBox prompt:="DtaObj.GetText() is " & DtaObj.GetText()' This would error - the registers are empty
' DtaObj.PutInClipboard ' This would also error - there is nothing in the registers to fill the clipboard with
' ActiveSheet.Paste Destination:=ActiveSheet.Range("C1") ' pastes what you copied
DtaObj.SetText Text:="New Text To Paste"
MsgBox prompt:="DtaObj.GetText() is " & DtaObj.GetText() ' reflects the added text, "New Text To Paste"
DtaObj.PutInClipboard
ActiveSheet.Paste Destination:=ActiveSheet.Range("C1") ' pastes "New Text To Paste"
DtaObj.SetText Text:="second Text To Paste"
MsgBox prompt:="DtaObj.GetText() is " & DtaObj.GetText() ' reflects the added text, "New Text To Paste"
DtaObj.PutInClipboard
ActiveSheet.Paste Destination:=ActiveSheet.Range("C1") ' pastes "New Text To Paste"
End Sub
_.__________________________________________
Coming back to the original question .. to explain the strange behaviour … just my take…
_ …... Q: Is this behaviour expected or is it a bug?
_ ....A
I think I expect it if my assessment of the situation above is correct: Doing a manual copy, or a copy via coding, makes an entry in a register in the data object. This is what .GetText() appears to get – the last thing entered, be it via a copy or by a .SetText. The .SetText won’t have any effect on a .PutInClipboard unless the register is empty. I guess the reason why it is done like this is somehow related to how you can use the format thing to effectively have multi text.
What is required to empty these registers is not clear always, that is to say , it is not clear if .Clear always is necerssary.
Clearly, the clipboard remains a mystery to everyone I think.
# Tim Williams.
Sub Tester()
I think the results from your Sub Tester() are constant with my take on the situation: If you have nothing in the windows clipboard , you will need your code lines of d2.PutInClipboard and d.GetFromClipboard to get d.GetText to not error the first time. This will have a similar effect to doing d.SetText in terms of initialising a register, that is somehow related to d.
( If you have something in the Windows clipboard, then you do not need d2.PutInClipboard to prevent d.GetText from erroring , but it will return you what is in the windows clipboard. )
You do not ever need the second and third d2.PutInClipboard as they never have any effect on your coding: As long as you have your first d2.PutInClipboard, then you will get your shown results. My take on that is that .GetText is indicating the last thing it knows about that was added. You will find however, that if you copy something to the windows clipboard and delete all your 3 d2.PutInClipboard lines, then d.GetText will always tell you what is in the window clipboard. Somehow in this case it has lost its awareness of what d2 is doing. To confirm this you can try copying something to the Windows Clipboard, then run Sub Testies3() , which has the first d2.PutInClipboard removed, but the second and third included. Your routine will in this case tell you all the time what is in the windows clipboard, at least if you copy from Word… . ( …. if you copy something from inside Excel…. Then the first time you run that last routine, Sub Testies3() , you will get some interesting results. Something is managing to make d aware of what d2 is doing, the first time you run the code after copying something from Excel. It also happens like that if you copy something out of a text datei, or a browser, but not if you copy from Word )
If you try to understand fully what is going on in the spaghetti of interdependencies in the Clipboards then you will go mad…
Sub Tester()
Dim d As New DataObject, d2 As New DataObject
d2.SetText "first": d2.PutInClipboard
d.GetFromClipboard
Debug.Print d.GetText '--> "first"
d2.SetText "second": 'd2.PutInClipboard
Debug.Print d.GetText '--> "second"
d2.SetText "third" 'd2.PutInClipboard
Debug.Print d.GetText '--> "third"
End Sub
Sub Testes2() 'COPY SOMETING before running this
Dim d As New DataObject, d2 As New DataObject
d2.SetText "first": 'd2.PutInClipboard
d.GetFromClipboard
Debug.Print d.GetText '--> "What you copied"
d2.SetText "second": 'd2.PutInClipboard
Debug.Print d.GetText '--> "What you copied"
d2.SetText "third" 'd2.PutInClipboard
Debug.Print d.GetText '--> "What you copied"
End Sub
Sub Testies3() 'COPY SOMETING before running this
Dim d As New DataObject, d2 As New DataObject
d2.SetText "first": 'd2.PutInClipboard
d.GetFromClipboard
Debug.Print d.GetText '--> "What you copied"
d2.SetText "second": d2.PutInClipboard
Debug.Print d.GetText '--> "What you copied"
d2.SetText "third": d2.PutInClipboard
Debug.Print d.GetText '--> "What you copied"
End Sub