Collection storing more than it's intended to causing problems for a Union statement - vba

For some reason every column with data is being stored into columnsToCopy and unionVariable. At the top levels in Locals, I can see that it recognizes the column I actually want, but when I go deeper into say Cells -> WorkSheet -> UsedRange -> Value2 it will now show that all columns in my workbook are stored. This is the piece of code that I have assigning columnsToCopy, all the way to assigning unionVariable and then Copying it:
checkOne = iq_Array(0)
hasIQs = Left(checkOne, 3) = "iq_"
Dim columnsToCopy As Collection
Set columnsToCopy = New Collection
If hasIQs Then
' paste inital column into temporary worksheet
columnsToCopy.Add ShRef.Columns(1)
End If
' loop for each iq_ in the array
For arrayLoop = LBound(iq_Array) To UBound(iq_Array)
' Take copy of potential ref and adjust to standard if required
checkStr = iq_Array(arrayLoop)
If hasIQs And Left(checkStr, 3) <> "iq_" Then checkStr = "iq_" & checkStr
' Look for existence of corresponding column in local copy array
pCol = 0
For iCol = 2 To colNumb
If checkStr = IQRef(iCol) Then
pCol = iCol
Exit For
End If
Next iCol
If pCol > 0 Then
' Paste the corresponding column into the forming table
columnsToCopy.Add ShRef.Columns(pCol)
End If
Next arrayLoop
If columnsToCopy.Count > 1 Then 'data was added
' Copy table
Dim unionVariable As Range
Set unionVariable = columnsToCopy(1)
For k = 2 To columnsToCopy.Count
Set unionVariable = xlApp.Union(unionVariable, columnsToCopy(k))
Next k
unionVariable.Copy ' all the data added to ShWork
The reason I'm looking into this, is because when I Union(unionVariable, columnToCopy(k)) I'm not getting something that would be equivalent to Range("A:A","D:D","Z:Z") , instead I'm getting Range("A:Z").
Any help is appreciated
My full code:
Option Explicit
Private Sub averageScoreRelay()
' 1. Run from PPT and open an Excel file
' 2. Start at slide 1 and find a box that contains the words "iq_", if it has those words then it will have numbers after it like so "iq_43" or "iq_43, iq_56,iq_72".
' 3. find those words and numbers in the opened Excel file after splitting and re-formating string.
' 3. Copy column into a new sheets and repeat for all "iq_'s" until sheets 2 has a table.
' 4. Copy table from xl Paste Table into ppt
' 5. Do this for every slide
'Timer start
Dim StartTime As Double
Dim SecondsElapsed As Double
StartTime = Timer
'Create variables
Dim xlApp As Excel.Application
Dim xlWB As Excel.Workbook
Dim ShRef As Excel.Worksheet
Dim pptPres As Object
Dim colNumb As Long
Dim rowNumb As Long
' Create new excel instance and open relevant workbook
Set xlApp = New Excel.Application
'xlApp.Visible = True 'Make Excel visible
Set xlWB = xlApp.Workbooks.Open("C:\Users\Andre Kunz\Desktop\Gate\Macros\averageScores\pptxlpratice\dummy2.xlsx", True, False, , , , True, Notify:=False) 'Open relevant workbook
If xlWB Is Nothing Then ' may not need this if statement. check later.
MsgBox ("Error retrieving Average Score Report, Check file path")
Exit Sub
End If
xlApp.DisplayAlerts = False
'Find # of iq's in workbook
Set ShRef = xlWB.Worksheets("Sheet1")
colNumb = ShRef.Cells(1, ShRef.Columns.Count).End(xlToLeft).Column
rowNumb = ShRef.Cells(ShRef.Rows.Count, 1).End(xlUp).Row
Dim IQRef() As String
Dim iCol As Long
Dim IQRngRef() As Range
ReDim IQRef(colNumb)
ReDim IQRngRef(colNumb)
' capture IQ refs locally
For iCol = 2 To colNumb
Set IQRngRef(iCol) = ShRef.Range(ShRef.Cells(1, iCol), ShRef.Cells(rowNumb, iCol))
IQRef(iCol) = ShRef.Cells(1, iCol).Value
Next iCol
'Make pptPres the ppt active
Set pptPres = PowerPoint.ActivePresentation
'Create variables for the slide loop
Dim pptSlide As Slide
Dim Shpe As Shape
Dim pptText As String
Dim iq_Array As Variant
Dim arrayLoop As Long
Dim myShape As Object
Dim i As Long
Dim lRows As Long
Dim lCols As Long
Dim k As Long
'Loop through each pptSlide and check for IQ text box, grab avgScore values and create pptTable
For Each pptSlide In pptPres.Slides
i = 0
pptSlide.Select
'searches through shapes in the slide
For Each Shpe In pptSlide.Shapes
If Not Shpe.HasTextFrame Then GoTo nextShpe 'boom, one less nested If statement
If Not Shpe.TextFrame.HasText Then GoTo nextShpe ' boom, another nested If statement bites the dust
'Set pptText as the Text in the box, then make it lowercase and trim Spaces and Enters
pptText = Shpe.TextFrame.TextRange
pptText = LCase(Replace(pptText, " ", vbNullString))
pptText = Replace(Replace(Replace(pptText, vbCrLf, vbNullString), vbCr, vbNullString), vbLf, vbNullString)
'Identify if within text there is "iq_"
If InStr(1, pptText, "iq_") <= 0 Then GoTo nextShpe
'set iq_Array as an array of the split iq's
iq_Array = Split(pptText, ",")
Dim hasIQs As Boolean
Dim checkStr As String
Dim pCol As Long
Dim checkOne
checkOne = iq_Array(0)
hasIQs = Left(checkOne, 3) = "iq_"
Dim columnsToCopy As Collection
Set columnsToCopy = New Collection
If hasIQs Then
' paste inital column into temporary worksheet
columnsToCopy.Add ShRef.Columns(1)
End If
' loop for each iq_ in the array
For arrayLoop = LBound(iq_Array) To UBound(iq_Array)
' Take copy of potential ref and adjust to standard if required
checkStr = iq_Array(arrayLoop)
If hasIQs And Left(checkStr, 3) <> "iq_" Then checkStr = "iq_" & checkStr
' Look for existence of corresponding column in local copy array
pCol = 0
For iCol = 2 To colNumb
If checkStr = IQRef(iCol) Then
pCol = iCol
Exit For
End If
Next iCol
If pCol > 0 Then
' Paste the corresponding column into the forming table
columnsToCopy.Add ShRef.Columns(pCol)
End If
Next arrayLoop
If columnsToCopy.Count > 1 Then 'data was added
' Copy table
Dim unionVariable As Range
Set unionVariable = columnsToCopy(1)
For k = 2 To columnsToCopy.Count
Debug.Print k & " : " & unionVariable.Address & " + " & columnsToCopy(k).Address
Set unionVariable = xlApp.Union(unionVariable, columnsToCopy(k))
Debug.Print " --> " & unionVariable.Address
Next k
unionVariable.Copy ' all the data added to ShWork
tryAgain:
ActiveWindow.ViewType = ppViewNormal
ActiveWindow.Panes(2).Activate
Set myShape = pptSlide.Shapes.PasteSpecial(DataType:=ppPasteHTML, Link:=msoFalse)
On Error GoTo tryAgain
'Set position:
myShape.Left = -200
myShape.Top = 150 + i
i = i + 150
End If
nextShpe:
Next Shpe
nextSlide:
Next pptSlide
xlWB.Close
xlApp.Quit
xlApp.DisplayAlerts = True
'End Timer
SecondsElapsed = Round(Timer - StartTime, 2)
MsgBox "This code ran successfully in " & SecondsElapsed & " seconds", vbInformation
End Sub
Output from Debugger:
2 : $A:$A + $B:$B
--> $A:$B
3 : $A:$B + $AF:$AF
--> $A:$B,$AF:$AF
2 : $A:$A + $C:$C
--> $A:$A,$C:$C
2 : $A:$A + $D:$D
--> $A:$A,$D:$D
3 : $A:$A,$D:$D + $L:$L
--> $A:$A,$D:$D,$L:$L

Here is another option doesn't have the additional overhead of creating a temporary workbook/worksheet.
Note: It may not be perfect -- in my testing it does not preserve cell background color but it does preserve text/font formats, and this appears consistent with the PasteSpecial(ppPasteHtml) method.
Note also: this assumes you can use a Table in PowerPoint to store the pasted data, and that all columns in your union range have the same number of rows. If you're just dumping the data in to a textbox or whatever sort of shape, this won't work.
But the idea is that once we have our "union", we can iterate over the Areas, and the Columns in each area, performing the Copy and Paste operation against each individual column.
Here is my data in Excel, I will create a union of the highlighted cells:
Here is the output in PowerPoint where I removed the borders from the table, note the text formatting preserved as well as cell alignment:
Option Explicit
Sub foo()
Dim ppt As PowerPoint.Application
Dim pres As PowerPoint.Presentation
Dim sld As PowerPoint.Slide
Dim shp As PowerPoint.Shape
Dim tbl As PowerPoint.Shape
Dim unionRange As Range
Dim ar As Range, c As Long, i As Long
Set unionRange = Union([A1:B2], [D1:D2], [F1:F2])
Set ppt = CreateObject("PowerPoint.Application")
ppt.Visible = True
Set pres = ppt.ActivePresentation
Set sld = pres.Slides(1)
' Create initial table with only 1 column
With unionRange
Set tbl = sld.Shapes.AddTable(.Rows.Count, 1)
End With
For Each ar In unionRange.Areas()
For c = 1 To ar.Columns.Count
i = i + 1
With tbl.Table
' Add columns as you iterate the columns in your unionRange
If .Columns.Count < i Then .Columns.Add
.Columns(i).Cells.Borders(ppBorderBottom).Transparency = 1
.Columns(i).Cells.Borders(ppBorderTop).Transparency = 1
.Columns(i).Select
ar.Columns(c).Copy '// Copy the column from Excel
ppt.CommandBars.ExecuteMso ("Paste") '// Paste the values to PowerPoint
End With
Next
Next
End Sub
Maybe more efficient to handle the Areas like so:
For Each ar In unionRange.Areas()
c = ar.Columns.Count
Dim tCol
tCol = .Columns.Count
With tbl.Table
' Add columns as you iterate the columns in your unionRange
While .Columns.Count < (tCol + c)
.Columns.Add
Wend
.Columns(tCol).Cells.Borders(ppBorderBottom).Transparency = 1
.Columns(tCol).Cells.Borders(ppBorderTop).Transparency = 1
.Columns(tCol).Select
ar.Copy '// Copy the columns in THIS Area object from Excel
ppt.CommandBars.ExecuteMso ("Paste") '// Paste the values to PowerPoint
End With
Next
But I still think performance on large data set will suffer vs the other answer.

The issue seems to be caused by the pasting of the non-contiguous range into PowerPoint.
I don't know enough PowerPoint VBA to know whether it has some other paste method you could use, but a work-around would be to create a new Excel worksheet containing just the info you want to copy, and then to copy that worksheet to PowerPoint:
'...
Next k
unionVariable.Copy ' all the data added to ShWork
'Create a temporary sheet (the workbook is being closed without saving
'so the temporary worksheet will be "lost" after we finish)
xlWB.Worksheets.Add Before:=xlWB.Worksheets(1)
'Paste the data into the temporary sheet
xlWB.Worksheets(1).Range("A1").PasteSpecial xlPasteValues
'Copy the temporary sheet
xlWB.Worksheets(1).UsedRange.Copy
tryAgain:
ActiveWindow.ViewType = ppViewNormal
'...

Related

Why vba loop fails after the first round?

My code is suppose to create sheets, then charts on sheets with name "Sheet..." and then create a powerpoint. then it deletes all sheets and starts all over again. The charts creation part works, but when it comes to creating a powerpoint, the code hops over the IF-statement. It works the first round but after that it jumps over IF.
Dim ppt As PowerPoint.Application
Set ppt = New PowerPoint.Application
ppt.Visible = msoTrue
'Change template source if necessary
ppt.Presentations.Open "C:\Desktop\Template\PowerPoint_Template.potx", _
Untitled:=msoTrue
ppt.Activate
Dim ppt_pres As PowerPoint.Presentation
Set ppt_pres = ppt.ActivePresentation
Dim ppt_layout As CustomLayout
Set ppt_layout = ppt_pres.Slides(2).CustomLayout
Dim ppt_slide As PowerPoint.Slide
Set ppt_slide = ppt_pres.Slides.AddSlide(2, ppt_layout)
Dim ppt_shape As PowerPoint.Shape
Set ppt_shape = ppt_slide.Shapes(1)
Dim ppw As Object
Set ppw = ppt_pres.Windows(ppt_pres.Windows.Count)
Dim wsPIA As Worksheet
Set wsPIA = Sheet4
'Naming title slide
ppt_pres.Slides(1).Shapes(3).TextFrame.TextRange.Text = wsPIA.Range("C2")
ppt_pres.Slides(1).Shapes(2).TextFrame.TextRange.Text = wsPIA.Range("I2")
'Identifying number of sheets that contain newly created charts
Dim j As Integer, vNames() As Variant, ws As Worksheet, picture As Shape
j = 0
For Each ws In ThisWorkbook.Worksheets
If Left(ws.Name, 5) = "Sheet" Then
j = j + 1
ReDim Preserve vNames(j)
vNames(j) = ws.Name
End If
Next ws
Application.DisplayAlerts = False
'Looping copypaste of charts from excel to powerpoint
For Each ws In Worksheets
If Left(ws.Name, 5) = "Sheet" Then
ws.Select
For Each picture In ActiveSheet.Shapes
picture.Copy
ppw.View.GotoSlide ppt_pres.Slides.Count - 1
ppt_slide.Shapes.PasteSpecial ppPasteEnhancedMetafile
ppt_slide.Shapes(7).Height = 390
ppt.ActiveWindow.Selection.ShapeRange.Align msoAlignCenters, msoCTrue
ppt.ActiveWindow.Selection.ShapeRange.Align msoAlignMiddles, msoTrue
ppt_slide.Shapes(7).Top = ppt_slide.Shapes(7).Top + 14
ppt_slide.Shapes.Title.TextFrame.TextRange.Text = wsPIA.Range("C2")
ppt_slide.Shapes(4).TextFrame.TextRange.Text =wsPIA.Range("I2")
ppw.View.GotoSlide ppt_pres.Slides.Count - 1
Set ppt_slide = ppt_pres.Slides.AddSlide(ppt_pres.Slides.Count - 1, ppt_layout)
Next
End If
Next
'Deleting blank slides from the end
ppt_pres.Slides.item(ppt_pres.Slides.Count - 1).Delete
ppt_pres.Slides.item(ppt_pres.Slides.Count - 1).Delete
With ppt_pres
.SaveAs ("C:\Desktop\Presentations\Pres1.pptx")
.Close
End With
The first round, line:
If Left(ws.Name, 5) = "Sheet" Then ws. Select
is true but after the first iteration, it skips that and goes straight to
'Deleting blank slides from the end

Return the Worksheet that an Excel Chart is referencing using VBA

I need to be able to identify the worksheet that an excel chart (on a worksheet) is getting it's data from. I only need the data sheet which series 1 is referencing. I've started trying to extract the sheet name from .SeriesCollection(1).Formula but it gets realy complex. here's what I've got so far:
Sub GetChartDataSheet()
Dim DataSheetName As String
Dim DataSheet As Worksheet
DataSheetName = ActiveChart.SeriesCollection(1).Formula
DataSheetName = Left(DataSheetName, InStr(1, DataSheetName, "!$") - 1)
DataSheetName = WorksheetFunction.Replace(DataSheetName, 1, Len("=series("), "")
If Left(DataSheetName, 1) = "'" And Right(DataSheetName, 1) = "'" Then DataSheetName = Mid(DataSheetName, 2, Len(DataSheetName) - 2)
DataSheetName = Replace(DataSheetName, "''", "'")
Set DataSheet = Sheets(DataSheetName)
End Sub
this works in a lot of cases, but if my users have a strange worksheet name (eg Sh'e e$,,t!3!$) it fails. the same goes if series 1 has been named (eg .SeriesCollection(1).Formula = "=SERIES(**"Hell,o !"**,'Sh''e e$,,t!3!$'!$B$2:$B$18,'Sh''e e$,,t!3!$'!$C$2:$C$18,1)".
Is there a simple way to solve this?
I thought this is an easy one, turns out it's not. One of the cases where Excel has the information but will not give it away for free. I ended up with a function like this - maybe this helps:
Function getSheetNameOfSeries(s As Series) As String
Dim f As String, i As Integer
Dim withQuotes As Boolean
' Skip leading comma if not all parts of series is filled. Check if sheetname is in single quotes
For i = 9 To Len(s.Formula)
If Mid(s.Formula, i, 1) <> "," Then
If Mid(s.Formula, i, 1) = "'" Then
withQuotes = True
f = Mid(s.Formula, i + 1)
Else
withQuotes = False
f = Mid(s.Formula, i)
End If
Exit For
End If
Next i
' "f" now contains a part of the formula with the sheetname as start
' now we search to the end of the sheet name.
' If name is in quotes, we are looking for the "closing" quote
' If not in quotes, we are looking for "!"
i = 1
Do While True
If withQuotes Then
' Sheet name is in quotes, found closes quote --> we're done
' (but if next char is also a quote, we have the case the the sheet names contains a quote, so we have to continue working)
If Mid(f, i, 1) = "'" Then
If Mid(f, i + 1, 1) <> "'" Then
getSheetNameOfSeries = Mid(f, 1, i - 1)
Exit Do
Else
i = i + 1 ' Skip 2nd quote
End If
End If
Else
' Sheet name is quite normal, so "!" will indicate the end of sheetname
If Mid(f, i, 1) = "!" Then
getSheetNameOfSeries = Mid(f, 1, i - 1)
Exit Do
End If
End If
i = i + 1
Loop
getSheetNameOfSeries = Replace(getSheetNameOfSeries, "''", "'")
End Function
You can use the Find function to look for the values of SeriesCollection(1).
In the worksheet that hold the data of SeriesCollection(1), you will be able to find all the values in that array.
More explanations inside the code below.
Code
Option Explicit
Sub GetChartDataSheet()
Dim DataSheetName As String
Dim DataSheet As Worksheet
Dim ws As Worksheet
Dim ValuesArr As Variant, Val As Variant
Dim FindRng As Range
Dim ShtMatch As Boolean
Dim ChtObj As ChartObject
Dim Ser As Series
' if you want to use ActiveChart
Set ChtObj = ActiveChart.Parent
Set Ser = ChtObj.Chart.SeriesCollection(1)
ValuesArr = Ser.Values ' get the values of the Series Collection inside an array
' use Find to get the Sheet's origin
For Each ws In ThisWorkbook.Sheets
With ws
ShtMatch = True
For Each Val In ValuesArr ' loop through all values in array
Set FindRng = .Cells.Find(what:=Val) ' you need to find each value in the worksheet that SeriesCollection data is tied to
If FindRng Is Nothing Then
ShtMatch = False
Exit For
End If
Set FindRng = Nothing ' reset
Next Val
If ShtMatch = True Then
Set DataSheet = ws
Exit For
End If
End With
Next ws
DataSheetName = DataSheet.Name
End Sub

How could I make this code run faster and smoother?

Three issues.
This code is running in 4-5 minutes for me with the database that I currently have. Normally it will be a database with 100~ columns. I want to make this faster.
Another issue I have is that I keep getting two different pop-ups:
"File now Available for Editing"
"User is currently editing workbook, would you like to run in read-only mode?"
Very annoying, but nothing I can't live with.
Lastly, I also sometimes get an error on this line:
pptSlide.Shapes.PasteSpecial DataType:=ppPasteHTML, Link:=msoFalse But all I have to do is re-run the program and it'll go away.
I'm looking for any suggestions to make this code run a little faster and smoother, any recommendations are welcome.
Thanks!
Public Sub averageScoreRelay()
' 1. Run from PPT and open an Excel file
' 2. Start at slide 1 and find a box that contains the words "iq_", if it has those words then it will have numbers after it like so "iq_43" or "iq_43, iq_56,iq_72".
' 3. find those words and numbers in the opened Excel file after splitting and re-formating string.
' 3. Copy column into a new sheets and repeat for all "iq_'s" until sheets 2 has a table.
' 4. Copy table from xl Paste Table into ppt
' 5. Do this for every slide
'Timer start
Dim StartTime As Double
Dim SecondsElapsed As Double
StartTime = Timer
'Create variables
Dim xlApp As Excel.Application
Dim xlWB As Excel.Workbook
Dim pptSlide As Slide
Dim Shpe As Shape
Dim pptText As String
Dim pptPres As Object
Dim iq_Array As Variant
Dim arrayLoop As Integer
Dim i As Integer
Dim myShape As Object
Dim colNumb As Integer
Dim size As Integer
Dim k As Integer
Dim lRows As Long
Dim lCols As Long
' Create new excel instance and open relevant workbook
Set xlApp = New Excel.Application
'xlApp.Visible = True 'Make Excel visible
Set xlWB = xlApp.Workbooks.Open("file.xlsx", True, False, , , , True, Notify:=False) 'Open relevant workbook
If xlWB Is Nothing Then ' may not need this if statement. check later.
MsgBox ("Error retrieving Average Score Report, Check file path")
Exit Sub
End If
xlApp.DisplayAlerts = False
With xlWB.Worksheets("Sheet1")
colNumb = .Cells(1, .Columns.Count).End(xlToLeft).Column
End With
'Create a new blank Sheet in excel, should be "Sheet2"
xlWB.Worksheets.Add After:=xlWB.ActiveSheet
'Make pptPres the ppt active
Set pptPres = PowerPoint.ActivePresentation
'Loop through each pptSlide and check for IQ text box, grab avgScore values and create pptTable
For Each pptSlide In pptPres.Slides
pptSlide.Select
'searches through shapes in the slide
For Each Shpe In pptSlide.Shapes
k = 1
'Identify if there is text frame
If Shpe.HasTextFrame Then
'Identify if there's text in text frame
If Shpe.TextFrame.HasText Then
'Set pptText as the Text in the box, then make it lowercase and trim Spaces and Enters
pptText = Shpe.TextFrame.TextRange
pptText = LCase(Replace(pptText, " ", vbNullString))
pptText = Replace(Replace(Replace(pptText, vbCrLf, vbNullString), vbCr, vbNullString), vbLf, vbNullString)
'Identify if within text there is "iq_"
If InStr(1, pptText, "iq_") > 0 Then
'set iq_Array as an array of the split iq's
iq_Array = Split(pptText, ",")
'Find size of the array
size = UBound(iq_Array) - LBound(iq_Array)
'loop for each iq_ in the array'
For arrayLoop = 0 To size
'Statement that will take iq_'s in the form "iq_9" or "iq_99" or "iq_999"
If iq_Array(arrayLoop) Like "iq_#" Or iq_Array(arrayLoop) Like "iq_##" Or iq_Array(arrayLoop) Like "iq_###" Then
'loops for checking each column
For i = 1 To colNumb
'Copies the first column (role column) for every slide that needs it
If i = 1 And arrayLoop = 0 Then
'copy column
xlWB.Worksheets("Sheet1").Columns(1).Copy
'paste column in Sheet2 which was newly created
xlWB.Worksheets("Sheet2").Paste Destination:=xlWB.Worksheets("Sheet2").Columns(1)
'If this is not the role column, then check to see if the iq_'s match from ppt to xl
ElseIf xlWB.Worksheets("Sheet1").Cells(1, i) = iq_Array(arrayLoop) And i <> 1 Then
'Serves to paste in the next column of Sheet2 so that we end up with a table
k = k + 1
'same as above
xlWB.Worksheets("Sheet1").Columns(i).Copy
xlWB.Worksheets("Sheet2").Paste Destination:=xlWB.Worksheets("Sheet2").Columns(k)
'Go to next array
GoTo Line2
End If
Next i
'Same as above, just this one is for iq_'s with form "iq_45,46,47" instead of "iq_45,iq_46,iq_47"
ElseIf (iq_Array(0) Like "iq_#" Or iq_Array(0) Like "iq_##" Or iq_Array(0) Like "iq_###") And (IsNumeric(iq_Array(arrayLoop)) And Len(iq_Array(arrayLoop)) <= 3) Then
For i = 1 To colNumb
If i = 1 And arrayLoop = 0 Then
xlWB.Worksheets("Sheet1").Columns(1).Copy
xlWB.Worksheets("Sheet2").Paste Destination:=xlWB.Worksheets("Sheet2").Columns(1)
ElseIf xlWB.Worksheets("Sheet1").Cells(1, i) = ("iq_" & iq_Array(arrayLoop)) And i <> 1 Then 'if iq in ppt = iq in xl and if not the first cell then execute
k = k + 1
xlWB.Worksheets("Sheet1").Columns(i).Copy
xlWB.Worksheets("Sheet2").Paste Destination:=xlWB.Worksheets("Sheet2").Columns(k)
GoTo Line2
End If
Next i
End If
Line2:
Next arrayLoop
End If
End If
End If
Next Shpe
'calculate last row and last column on sheet2. aka. find Table size
With xlWB.Worksheets("Sheet2")
lRows = .Cells(.Rows.Count, 1).End(xlUp).Row
lCols = .Cells(1, .Columns.Count).End(xlToLeft).Column
'If only one column then go to next slide
If lRows = .Cells(1, 1).End(xlUp).Row And lCols = .Cells(1, 1).End(xlToLeft).Column Then
GoTo Line1
End If
'Copy table
.Range(.Cells(1, 1), .Cells(lRows, lCols)).Copy
End With
'Paste Table into ppt
pptSlide.Shapes.PasteSpecial DataType:=ppPasteHTML, Link:=msoFalse
'Recently pasted shape is the last shape on slide, so it will be the same as count of shapes on slide
Set myShape = pptSlide.Shapes(pptSlide.Shapes.Count)
'Set position:
myShape.Left = -200
myShape.Top = 200
'Clear Sheet2 for next slide
xlWB.Worksheets("Sheet2").Range("A1:P10").Clear
Line1:
Next pptSlide
xlWB.Worksheets("Sheet2").Delete
xlWB.Close
xlApp.Quit
xlApp.DisplayAlerts = True
'End Timer
SecondsElapsed = Round(Timer - StartTime, 2)
MsgBox "This code ran successfully in " & SecondsElapsed & " seconds", vbInformation
End Sub

Not able to get the cell value in foreach loop using excel vba

Hi,
I have enclosed the sheet image.
My requirement is:
I want to get all the "G" column values for the organization matching to a specific organization name (Ex:360 evaluations).
I am getting null value after first loop for the G Column
Sub UsageWeekTrend()
Dim customerName As String
Dim sheetName As String
Dim dataFound As Boolean
Dim selectedCell As Range
Dim rowNumber As Integer
Dim weekMinutes As Double
Dim trendsFile As Workbook
Dim trendsSheet As Worksheet
On Error GoTo errorHandling
sheetName = ActiveSheet.Name
customerName = ActiveSheet.Range("A" & (ActiveCell.row)).Value
dataFound = False
For Each selectedCell In ActiveSheet.Range("A1:A1000")
If UCase(selectedCell.Value) = UCase(customerName) Then
weekMinutes = ActiveSheet.Range("G" & selectedCell.row).Value
Debug.Print weekMinutes
Debug.Print "G" & selectedCell.row
If dataFound = False Then
If trendsFile Is Nothing Then
Set trendsFile = Workbooks.Add()
trendsFile.Activate
Set trendsSheet = trendsFile.ActiveSheet
Else
' add a new sheet to the trends workbook
trendsFile.Activate
Set trendsSheet = Sheets.Add
End If
dataFound = True
rowNumber = 1
trendsSheet.Name = Left(customerName, 10) + " " + Format(Date, "MMDD")
trendsSheet.Cells(rowNumber, 1) = "Users"
trendsSheet.Cells(rowNumber, 2) = "Minutes"
rowNumber = rowNumber + 1
End If
' if a sheet has been created, then we have at least one non-zero value so add data
If dataFound = True Then
trendsSheet.Cells(rowNumber, 1) = customerName
trendsSheet.Cells(rowNumber, 2) = weekMinutes
rowNumber = rowNumber + 1
End If
End If
Next selectedCell
' if we have data, create the chart
If dataFound = True Then
' make sure the trends sheet is active for chart insertion
trendsSheet.Activate
Dim chtChart As ChartObject
Dim chartName As String
Dim endRange As String
' define the end of the range for the chart
endRange = "C" & CStr(rowNumber - 1)
' add chart to current sheet
Set chtChart = ActiveSheet.ChartObjects.Add(Left:=200, Top:=200, Width:=900, Height:=400)
chtChart.Activate
ActiveChart.ChartType = xlLineStacked
ActiveChart.SetSourceData Source:=trendsSheet.Range("A2", endRange)
ActiveChart.HasTitle = True
ActiveChart.ChartTitle.Text = customerName
ActiveChart.ApplyLayout (5)
Else
MsgBox ("No usage data found for customer " + customerName)
End If
Exit Sub
errorHandling:
MsgBox (Err.Description)
End Sub
When you run this line:
trendsFile.Activate
You change the Activesheet, so the 2nd time on the loop you again look at the activesheet
weekMinutes = ActiveSheet.Range("G" & selectedCell.row).Value
but the activesheet has changed. I would change those Activesheet calls to a worksheet object that you assign at the top.
This is always a good read for those new to VBA programming: How to avoid using Select in Excel VBA macros
The issue is that you're using ActiveSheet, and the active sheet is being changed in your code.
As soon as trendsFile.Activate is executed, these two references will have new meanings ActiveSheet.Range("A1:A1000") and ActiveSheet.Range("G" & selectedCell.row).Value.
You've created workbook & worksheet variables for your Trends file, and use those, you also need to create a worksheet variable for your "source" worksheet (not sure how you'd refer to it).
Also, I'd be a bit concerned about this section of code:
If trendsFile Is Nothing Then
Set trendsFile = Workbooks.Add()
trendsFile.Activate
Set trendsSheet = trendsFile.ActiveSheet
Else
' add a new sheet to the trends workbook
trendsFile.Activate
Set trendsSheet = Sheets.Add
End If
I believe you'll be adding a new sheet every time through the loop.
Try something like this:
Sub UsageWeekTrend()
Dim customerName As String
Dim sheetName As String
Dim dataFound As Boolean
Dim selectedCell As Range
Dim rowNumber As Integer
Dim weekMinutes As Double
Dim trendsFile As Workbook
Dim trendsSheet As Worksheet
Dim SourceSheet as worksheet 'this is the place where you start, call it what you will
On Error GoTo errorHandling
set SourceSheet = activesheet 'this will now always be THIS sheet, and won't change
sheetName = SourceSheet.Name
customerName = SourceSheet.Range("A" & (ActiveCell.row)).Value
dataFound = False
For Each selectedCell In SourceSheet.Range("A1:A1000")
If UCase(selectedCell.Value) = UCase(customerName) Then
weekMinutes = SourceSheet.Range("G" & selectedCell.row).Value
Debug.Print weekMinutes
Debug.Print "G" & selectedCell.row
If dataFound = False Then
If trendsFile Is Nothing Then
Set trendsFile = Workbooks.Add()
'trendsFile.Activate - never needed
Set trendsSheet = trendsFile.Sheets("Sheet1") 'use the first sheet, since you just created a brand new workbook
Else
' add a new sheet to the trends workbook
'trendsFile.Activate -- you never need this when you're working with an object instead of "Active"
'you'll find that this line will add a new sheet every time you execute the loop
'once you've created your "trendsFile" workbook. you'll need to do some tweaking here
'to prevent you getting one loop worth of data on each sheet
Set trendsSheet = Sheets.Add
End If
dataFound = True
rowNumber = 1
trendsSheet.Name = Left(customerName, 10) + " " + Format(Date, "MMDD")
trendsSheet.Cells(rowNumber, 1) = "Users"
trendsSheet.Cells(rowNumber, 2) = "Minutes"
rowNumber = rowNumber + 1
End If
' if a sheet has been created, then we have at least one non-zero value so add data
If dataFound = True Then
trendsSheet.Cells(rowNumber, 1) = customerName
trendsSheet.Cells(rowNumber, 2) = weekMinutes
rowNumber = rowNumber + 1
End If
End If
Next selectedCell
'The rest of your routine here...
End Sub

Read Cell properties in Visio using vb

I am trying to create a VB macro in Visio that can read the data and properties of the shape. So say I have a Rectangle Shpae in Visio with Cells Name, Description, Type, Size.... and so on.
When I try to read the cells and their values I am only getting the first cell and its value.
Here is my code . I would appreciate some help here.
Sub Testing()
Dim excelObj As Object
Dim excelFile As String
Dim sheetName As String
' Dim excelBook As Excel.Workbook
' Set excelFile = "C:\Users\hbhasin\Documents\test.xls"
'Set sheetName = "New Sheet name"
Set excelObj = CreateObject("Excel.Application")
excelObj.Workbooks.Add
Dim pagObj As Visio.Page
Dim shpsObj As Visio.shapes
Dim shapes As Visio.shapes
Dim shpObj As Visio.Shape
Dim CellObj As Visio.Cell
Dim Storage() As String
Dim iShapeCount As Integer
Dim i As Integer
Dim j As Integer
Set pagObj = ActivePage
Set shpsObj = pagObj.shapes
iShapeCount = shpsObj.Count
Debug.Print iShapeCount
ReDim Storage(8, iShapeCount - 1)
For i = 1 To iShapeCount - 1
Set shpObj = shpsObj(i)
Storage(1, i - 1) = shpObj.Name
If shpObj.CellExists("Prop.Name", visExistsLocally) Then
Set CellObj = shpObj.CellsU("Prop.Name")
Storage(2, i - 1) = CellObj.ResultStr("")
End If
If shpObj.CellExists("Prop.Description", visExistsLocally) Then
Debug.Print "Test the IF statement"
Set CellObj = shpObj.CellsU("Prop.Description")
Storage(3, i - 1) = CellObj.ResultStr("")
End If
Next
For i = 0 To iShapeCount - 1
Debug.Print "Name- " & Storage(0, i)
Debug.Print "Description-" & Storage(1, i)
Next
End Sub
In fact, I have put a debug statement within the second if clause and that does not execute which tells me the compiler is not even seeing the second cell or any cell after.
If you're not getting the Description Shape Data it maybe that it's not local, but inherited from its master. Here's a slight modification of your code (with the Excel part removed as I don't think it's relevant here):
Sub Testing()
Dim shpsObj As Visio.shapes
Set shpsObj = ActivePage.shapes
Dim iShapeCount As Integer
iShapeCount = shpsObj.Count
'Assumes you want an array of shape data
Dim Storage() As String
ReDim Storage(iShapeCount - 1, 2)
'Visio shapes are 1 based so use full count
Dim i As Integer
Dim shpObj As Visio.Shape
For i = 1 To iShapeCount
Set shpObj = shpsObj(i)
Storage(i - 1, 0) = shpObj.Name 'Do you want NameU?
'Assumes you don't care whether the cell is local or inherited
If shpObj.CellExistsU("Prop.Name", visExistsAnywhere) Then
Storage(i - 1, 1) = shpObj.CellsU("Prop.Name").ResultStr("")
End If
If shpObj.CellExistsU("Prop.Description", visExistsAnywhere) Then
Storage(i - 1, 2) = shpObj.CellsU("Prop.Description").ResultStr("")
End If
Next
Dim j As Long
For j = LBound(Storage, 1) To UBound(Storage, 1)
Debug.Print "Shape Name- " & Storage(j, 0)
Debug.Print " Prop.Name- " & Storage(j, 1)
Debug.Print " Prop.Description- " & Storage(j, 2)
Next j
End Sub
If you're just running through all the shapes on the page, then you might want to look at For Each shp In shapes as an alternative. Check out this page for more details:
http://visualsignals.typepad.co.uk/vislog/2007/11/looping-through.html
Also, you might want to try look at the CreateSelection page method to narrow down your target shapes if you're dealing with a large number