PPT VBA Send trigger from ppt shape to Chart data - vba

have formatted the code as below. Error comes as data member not found at ".Sheets(1).Range("A7").Value"
Sub DATA()
temp = 0
ActivePresentation.Slides(4).Shapes("Temp").TextFrame.TextRa‌​nge.Text = ActivePresentation.Slides(4).Shapes("Temp").TextFrame.TextRa‌​nge.Text +1 'counter to add +1
With ActivePresentation.Slides(4).Shapes("Bar1").Chart.ChartData
.Activate
.Sheets(1).Range("A7").Value = ActivePresentation.Slides(4).Shapes("Temp").TextFrame.TextRa‌​nge.Text
.Workbook.Close
End With
End Sub

The ChartData object refers to the entire Workbook, so you're probably getting an error like 438: Object does not support this property or method, because Cell is not a child of Workbook object.
NB also, Cell is not a child of Worksheet object, either, it's Cells or Range that you need, and you'll need to qualify that to a worksheet, like Sheets(1) (modify as needed).
So, try:
Dim val$
val = ActivePresentation.Slides(4).Shapes("temp").textrange.text
With ActivePresentation.Slides(4).Shapes("Bar1").Chart.ChartData
'.Activate
' something like this:
.Sheets(1).Range("A1").value = val
.Workbook.Close
End With
In theory you're supposed to be able to use a With block to manage the ChartData object. In practice, I have always had to actually Activate it and subsequently close it. YMMV.

Related

VBA Word: Change Data of charts

I want to change the data of a chart in a Word Document, but I can't find the right way to address my charts. I tried several techniques, but nothing worked. (I´d love to open a ExcelSheet in which I can just change the Data)
So to put it all together: I want to change the data (not the source), of a MS Word chart, which looks like that:
Edit(13.8.):
After request, I try to give you some "reference Code" to work with.
Sub ChangeChart()
Dim aktDocument As Document
Dim chrt As Chart
Dim SourceSheet As Excel.Worksheet
Set aktDocument = ActiveDocument
Set SourceSheet = aktDocument.Shapes(1).Chart.OpenSourceData 'I know it´s not that easy
SourceSheet.Range("B5") = newStuff
aktDocument.Shapes(1).Chart.SetSourceData = SourceSheet
End Sub
I know this may sounds utopic and ridiculous, but I just don´t know, how to address the chart in the right way, or to even work with it properly.
Edit(15.08):
Even after recreating the old charts, the following code is not able to find a shape which has a chart. And therefore it stops when the index is out of range.
Sub Test()
i = 0
Do While i < 100
i = i + 1
If ActiveDocument.Shapes(i).HasChart Then
MsgBox "found one!"
End If
Loop
End Sub
Solution(30.08.):
The answer from #Cindy Meister was the solution to my problem. After further working with it, I came to the problem, that the ChartData always opens on the screen, while running the code.
Just for reference this question was my workaround.
All Office applications use the Excel engine to create and manage charts. In Word, charts can be formatted in-line with the text or with text wrap formatting. In the former case, a chart object needs to be addressed via the InlineShapes collection, in the latter via the Shapes collection.
Since your sample code uses Shapes(1) I've used that in the code snippet below. If it's not certain that the first Shape in the document is the chart, but you've assigned the Shape a name, you can use that as the index value (for example Shapes("MyChart"). Or you can loop the Shapes collection and check HasChart.
HasChart returns True if the Shape (or InlineShape) is a Chart. It's then possible to set Shape.Chart to an object variable. The chart's data can be accessed using Chart.ChartData.Activate - if you don't use Activate it's not possible to access the data when the chart's worksheet is stored in the Word document. Only then can Chart.ChartData.Workbook return a workbook object, and through that the worksheet can be accessed using ActiveSheet. From that point on, it's like working with the Excel object model.
Sub ChangeChart()
Dim aktDocument As Document
Dim shp As Word.Shape
Dim chrt As Word.Chart
Dim wb As Excel.Workbook, SourceSheet As Excel.Worksheet
Set aktDocument = ActiveDocument
Set shp = aktDocument.Shapes(1)
If shp.HasChart Then
Set chrt = shp.Chart
chrt.ChartData.Activate
Set wb = chrt.ChartData.Workbook
Set SourceSheet = wb.ActiveSheet
SourceSheet.Range("B5").Value2 = newData
End If
End Sub

VBA - Reference an object by using a variable

Not sure how to reference the worksheet object with a variable that changes each time a sheet is activated.
The point is to reference a cell value based on the last worksheet that was activated (this code affects Sheet1 which does not set the variable when activated)
--Module1
Public MyWS as String
--Sheet3 (Deactivation)
MyWS = Sheet3.Codename
--Sheet2 (Deactivation)
MyWS = Sheet2.Codename
--Sheet1
Sheet1.Range("A3").Value = MyWS.Range("A3").Value
Updated:
Thanks for all the guidance but your instructions are not working for my project at least.
Sheet5.Range("C4").Value = Worksheets(MyWS).Range("A2").Value
Subscript out of range error when the above code is executed on Sheet5 deactivate.
MyWS is declared as a public string.
MyWS is assigned the Sheet5.CodeName string when Sheet5 is activated. Sheet5 exists and that is the unmodified codename of the sheet. I can not use the user defined name of the sheet because that can change.
Public MyWS As String declares a String variable, not an object.
CodeName
The CodeName property returns a String that contains an identifier that VBA uses to generate a project-scoped object variable for a Worksheet; in the properties toolwindow (F4), that's the (Name) property.
This is how such code is legal:
Sheet1.Range("A3").Value = 42
Because Sheet1 has a code name string that returns Sheet1. Note that this identifier isn't necessarily the sheet's name (it is by default though), which the user can change at any time without accessing the Visual Basic Editor.
So if you rename the "Sheet1" tab/sheet to "Summary", but don't change its code name, then it will still be Sheet1 in code - so these two instructions do exactly the same thing:
Sheet1.Range("A3").Value = 42
ThisWorkbook.Worksheets("Summary").Range("A3").Value = 42
Now, if you want an object variable holding a reference to a worksheet that exists at compile-time, you already have one - Sheet1 is exactly that.
If you added a worksheet a run-time (doesn't exist at compile-time), then there's no such project-scope object variable for that sheet; that's when you need to declare your own, and assign it with the Set keyword:
Dim someSheet As Worksheet
Set someSheet = ThisWorkbook.Worksheets.Add
ActiveSheet
The Excel object model also has the ActiveSheet object, which returns whatever sheet is currently active.
Sheet1.Range("A3").Value = ActiveSheet.Range("A3").Value
Notice the explicit qualifiers. If it's written in a standard module (.bas), this code is equivalent:
Sheet1.Range("A3").Value = Range("A3").Value
If it's written in the code-behind of a specific worksheet module, then the above code will instead be doing this:
Sheet1.Range("A3").Value = Me.Range("A3").Value
Where Me is whatever the specific worksheet module you're in is, so if you're writing that code in a worksheet module, you will want to explicitly qualify the Range member call with the ActiveSheet object.
Worksheet Events
If you need to execute code when a worksheet is activated, you can handle the SheetActivate event in the ThisWorkbook module:
Private Sub Workbook_SheetActivate(ByVal Sh As Object)
Dim sheet As Worksheet
If TypeOf Sh Is Worksheet Then
Set sheet = Sh
Else
'Sh is not a worksheet. could be a chart sheet, or something else.
Exit Sub
End If
Debug.Print sheet.Name & " activated!"
End Sub
If you need to handle the Activated event of a specific worksheet that exists at compile-time, you need an event handler for it in that worksheet's code-behind:
Private Sub Worksheet_Activate()
Debug.Print Me.Name & " activated!"
End Sub
If you need to handle that event for a worksheet that is created at run-time, you need a WithEvents object variable in a class module (.cls):
Private WithEvents MySheet As Worksheet
And then you can write a handler for MySheet_Activate in that module, but that's more advanced stuff and I'm barely scratching the surface here, but that should get you going :)
With ActiveSheet as mentioned in the comments is really the best solution.
However, if you want to do it "your way", write these Activate events in every worksheet:
Private Sub Worksheet_Activate()
lastWS = Me.Name
End Sub
Then lastWs would be the name of the ActiveSheet. And you would be able to refer to it like this Worksheets(lastWs). Thus:
Sheet1.Range("A3").Value = Worksheets(lastWs).Range("A3").Value

Workbooks.Add not adding a new workbook

I have an Excel function that populates a dictionary with information from a SQL pull. To help visualize the answer set, I had it currently dumping into a new workbook - and while I don't need to visualize it anymore, I still find it helpful to populate.
The answer set doesn't change unless I myself have done something in the database populating it, so I don't need the function to perform the query every time. Therefore, once the dictionary is populated, I am bypassing the query unless I force it to initialize the dictionary with a refresh parameter.
The module is structured as follows:
Option Explicit
Option Compare Text
Private dProducts As Scripting.Dictionary
------
Function ProdLookup(sValue As Variant, sReturn As Variant, sLookupType As
Variant, _Optional iVendor As Integer, Optional bRefresh As Boolean) As
Variant
If sValue = "" Then
ProdLookup = ""
Exit Function
End If
If sLookupType = "SKU" Then
If (dProducts Is Nothing) Or (bRefresh = True) Then
Call Create_dProdsBySKU
End If
ProdLookup = dProducts(CStr(sValue.Value))(CStr(sReturn.Value))
Exit Function
End If
End Function
------
Sub Create_dProdsBySKU()
Dim newBook As Workbook
Set newBook = Workbooks.Add
'Rest of code to create query, run it, retrieve results, dump onto
'newBook, and populate into dProducts
newBook.Close SaveChanges:=False
End Sub
If I simply run Create_dProdsBySKU from within the Editor, the dictionary populates onto a new workbook, and closes. If I use the ProdLookup function within Excel, however, it never creates a new workbook - and if I put a watch on newBook, it shows it's got a value of ThisWorkbook.
Attempting to see the properties of newBook in the Watch window hangs Excel and I need to End Task from the Task Manager.
What am I missing?
If I use the ProdLookup function within Excel
If you are using the function as a UDF, it will not be permitted to create a new workbook. UDFs are limited to only returning a value to the cell containing the function call.

Why does assigning a reference in my spreadsheet sometimes work and sometimes not?

I have a few cells in my excel workbook which are available for a client to put his own values. I wanted the workbook to initialize those cells with default values. In order to do so I have a worksheet "Arkusz do makr", where I store the values.
In a module "GM" I declare a variable to reference my worksheet easier like this:
Public M As Worksheet
Then I initialize this variable and set my default values like this (in ThisWorkbook):
Private Sub Workbook_Open()
Set M = Worksheets("Arkusz do makr")
Worksheets("Values").Range("Value1") = M.Range("Value1")
Worksheets("Values").Range("Value2") = M.Range("Value2")
Worksheets("Values").Range("Value3") = M.Range("Value3") `etc
End Sub
Now sometimes this works like a charm, and sometimes, when I open the workbook I get a
Run-time error '91': Object variable or With block variable not set.
Could someone please explain this behaviour to me? Additionally I would like to ask if my approach makes sense, since I have a hard time grasping the order of events in excel as well as the range of its objects.
EDIT: Additionally I should mention that the Debug function highlights the first Worksheets... line in my code. In specific worksheets I reference the M object as well, though I thought it changes anything here...
Try to change the code of this Sub like below.
I have added a simple error handling - if there is no worksheet "Arkusze do makr" or "Values" in your workbook, warning message is displayed and default values are not copied.
You can find more comments in code.
Private Sub Workbook_Open()
Dim macrosSheet As Excel.Worksheet
Dim valuesSheet As Excel.Worksheet
'------------------------------------------------------------------
With ThisWorkbook
'This command is added to prevent VBA from throwing
'error if worksheet is not found. In such case variable
'will have Nothing as its value. Later on, we check
'the values assigned to those variables and only if both
'of them are different than Nothing the code will continue.
On Error Resume Next
Set macrosSheet = .Worksheets("Arkusz do makr")
Set valuesSheet = .Worksheets("Values")
On Error GoTo 0 'Restore default error behaviour.
End With
'Check if sheets [Values] and [Arkusz do makr] have been found.
'If any of them has not been found, a proper error message is shown.
'In such case default values are not set.
If valuesSheet Is Nothing Then
Call VBA.MsgBox("Sheet [Values] not found")
ElseIf macrosSheet Is Nothing Then
Call VBA.MsgBox("Sheet [Arkusz do makr] not found")
Else
'If both sheets are found, default values are copied
'from [Arkusz do makr] to [Values].
'Note that if there is no Range named "Value1" (or "Value2" etc.)
'in any of this worksheet, another error will be thrown.
'You can add error-handling for this case, similarly as above.
With valuesSheet
.Range("Value1") = macrosSheet.Range("Value1")
.Range("Value2") = macrosSheet.Range("Value2")
.Range("Value3") = macrosSheet.Range("Value3")
End With
End If
End Sub

Store COPY of a worksheet in worksheet variable

What I want to achieve:
I want to assign copy of a worksheet to variable, for later use.
What I tried and results
First : The code below works fine. Something like this I would like to achieve, but using worksheet.copy.
Sub DuplicateSheetRenameFirst()
Dim wsDuplicate As Worksheet
Set wsDuplicate = Worksheets.Add
wsDuplicate.Name = "Duplicate"
End Sub
Second : Using the copy method, creates a worksheet in current workbook, but generates a Runtime error 424 - Object required.
Sub DuplicateSheetRenameSecond()
Dim wsDuplicate As Worksheet
Set wsDuplicate = Worksheets("Sheet1").Copy(after:=Worksheets(Worksheets.Count))
'above line : runtime error 424 object required, but the sheet is created
wsDuplicate.Name = "Duplicate"
End Sub
Third : Creates a worksheet in new workbook (so creates book, then sheet), but still generates the same Runtime error 424 - Object required.
Sub DuplicateSheetRenameThird()
Dim wsDuplicate As Worksheet
Set wsDuplicate = Worksheets("Sheet1").Copy
'above line : runtime error 424 object required, but the sheet is created in new workbook
wsDuplicate.Name = "Duplicate"
End Sub
Workaround : I can modify any of the second or third way to at first copy the sheet and then set the variable to activesheet, but I was wandering if there is a one step way of doing this. I'm not sure if this would work all the time, since the activesheet may not be the one just copied, maybe.
The Question:
Is there a simple (one step) way to store the copy of a worksheet in a variable? Preferably without errors or without filtering the error with error handler.
This is maybe ok?
Sub copySheet()
Dim ws As Excel.Worksheet
Excel.ThisWorkbook.Sheets("Sheet1").Copy After:=Sheets(1)
Set ws = Excel.ThisWorkbook.ActiveSheet
End Sub
It is unfortunate that in this case you need to use an Active... object. Generally it is good practice to avoid Active... objects.
You cannot do this though as the method .copy is not returning an object of the worksheet class:
Sub copySheet()
Dim ws As Excel.Worksheet
Set ws = Excel.ThisWorkbook.Sheets("Sheet1").Copy(After:=Sheets(1))
End Sub
Some further explanation is in this previous post:
Why does Worksheet.Copy not return a reference to the new workbook created
In MSDN it is not altogether obvious that the method returns nothing:
https://msdn.microsoft.com/EN-US/library/office/ff837784.aspx
...but in your friend Excel's Object Explorer it is more obvious. If it returned a worksheet object then by the arrow would read:
Sub Copy([Before], [After]) as Worksheet