Table of Contents
Introduction
When you work on slides for public talks it may happen that you would like to have two similar talks sharing some slides. For instance, one of my talks explains internals of async in C# so I have plenty slides with decompiled code, documentation etc. But my other talk, covering cons of async, has similar part as it also needs to explain some machinery before moving forward. However, I’d like to avoid just copying slides here and there as updating them is much harder. So I’d like to be able to extract common parts and reuse them.
I don’t want to use TeX to do so. I made presentations in TeX and I find it very tedious and time consuming, not to mention that ad-hoc adjustments for events (like theme or first slide) are much harder without proper software in place (or even with it, it’s still TeX). PowerPoint is simple and powerful enough for my needs so I wanted to use it. So I implemented couple of VBA functions to do the job.
So I prepared a PowerPoint deck with macro which merges all building blocks together and I can easily modify the macro to add, remove, reorder, or change slides on the go. Simple enough.
Presentation content
All my talks have similar blocks and use the same theme. I use:
- Section name
- Some content
- About me
- Agenda
- Presentation title, Q&A, Thanks
So there is not many building blocks to be used. Let’s go.
Beginnings
Press ALT+F11 and add new module to the presentation, save the file as macro-enabled deck. Now, you can run macros easily with ALT+F8.
First function we need is one to remove all slides and sections from the deck:
1 2 3 4 5 6 7 8 9 |
Private Sub Cleanup() While ActivePresentation.slides.Count > 0 ActivePresentation.slides(1).Delete Wend While ActivePresentation.SectionProperties.Count > 0 ActivePresentation.SectionProperties.Delete sectionIndex:=1, deleteSlides:=False Wend End Sub |
We call this method once at the beginning.
At the end we need to set the footer with slide numbers, date, and a title:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
Private Sub SetFooter(footerText As String) footerText = footerText & " - Adam Furmanek" With ActivePresentation.SlideMaster.HeadersFooters .Footer.Visible = True .Footer.text = footerText .DateAndTime.Visible = True .DateAndTime.Format = ppDateTimeFigureOut .DateAndTime.UseFormat = True .SlideNumber.Visible = True .DisplayOnTitleSlide = False End With For Each slide In ActivePresentation.slides With slide.HeadersFooters .Footer.Visible = True .Footer.text = footerText .DateAndTime.Visible = True .DateAndTime.Format = ppDateTimeFigureOut .DateAndTime.UseFormat = True .SlideNumber.Visible = True End With Next slide End Sub |
And we need to set title in metadata:
1 2 3 |
Private Sub SetMetadataTitle(title As String) Application.ActivePresentation.BuiltInDocumentProperties.Item("title").Value = title End Sub |
Section name
The simplest slide with just a section name and a subtitle:
1 2 3 4 5 6 7 |
Private Sub Section(title As String, subtitle As String) ActivePresentation.slides.Add Index:=ActivePresentation.slides.Count + 1, Layout:=ppLayoutSectionHeader With ActivePresentation.slides(ActivePresentation.slides.Count) .Shapes(1).TextFrame.TextRange = title .Shapes(2).TextFrame.TextRange = subtitle End With End Sub |
We add slide with specific layout and then update labels.
Some content
Here comes the reusability. We prepare separate deck with any slides we like (the actual “content” of the talk) and then copy them over here:
1 2 3 4 5 6 |
Private Sub Fragment(path As String) Dim slideId As Integer slideId = ActivePresentation.slides.Count ActivePresentation.slides.InsertFromFile (ActivePresentation.path & "\Fragments\" & path & ".pptx"), slideId ActivePresentation.SectionProperties.AddBeforeSlide SlideIndex:=slideId + 1, sectionName:=path End Sub |
You can see how I take the current presentation path, go to Fragments
directory and the get specific file. Slides are added at the end. Also, section is created before added slides so it is easy to identify where they come from.
About me
This is basically a single slide just like fragment, however, it is probably exactly the same in all talks so I just store it once and reuse:
1 2 3 |
Private Sub AboutMe() Fragment ("AboutMe") End Sub |
Agenda
Now we want to generate a slide with bulleted list. Since agenda will change for each talk (depending on the content), we’d like to generate it automatically:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
Private Sub Agenda(points As Variant) ActivePresentation.slides.Add Index:=ActivePresentation.slides.Count + 1, Layout:=ppLayoutText Dim slide As slide Set slide = ActivePresentation.slides(ActivePresentation.slides.Count) slide.Shapes(1).TextFrame.TextRange = "Agenda" Dim text As String text = "" For i = 0 To UBound(points) text = text & LTrim(points(i)) & vbCr Next slide.Shapes(2).TextFrame.TextRange = text For i = 0 To UBound(points) If InStr(points(i), " ") = 1 Then slide.Shapes(2).TextFrame.TextRange.paragraphs(i + 1).IndentLevel = 2 End If Next End Sub |
I accept a list of labels and indent them respectively. I call this method like this:
1 2 3 4 5 6 7 8 9 10 11 12 |
Agenda Array( _ "Exception mechanisms overview", _ "Implementation details:", _ " How to rethrow, catch everything, and what cannot be handled?", _ " When are exceptions thrown and how to stop it?", _ " How to handle corrupted state?", _ "Even more implemenation details:", _ " SEH and its frame.", _ " .NET and double pass.", _ " Thread.Abort implementation.", _ " AccessViolation internals.", _ " VEH and machine code generation to catch StackOverflowException") |
You can see that I insert 3 spaces to indent the label. Obviously, you can extend this as you wish.
Presentation title, Q&A, Thanks
These are almost regular section name slides but they have QR code on them which is added like this:
1 2 3 4 5 6 7 8 9 10 11 12 |
Private Sub InsertQR(qrCodePath As String, left As Integer, top As Integer, width As Integer, height As Integer) ActivePresentation.slides(ActivePresentation.slides.Count).Select ActivePresentation.slides(ActivePresentation.slides.Count).Shapes.AddPicture( _ FileName:=ActivePresentation.path & "\Fragments\" & qrCodePath, _ LinkToFile:=msoFalse, _ SaveWithDocument:=msoTrue, _ left:=left, _ top:=top, _ width:=width, _ height:=height _ ).Select End Sub |
It copies the picture and puts it directly into the slide.
Now it gets super simple. Q&A:
1 2 3 4 |
Private Sub QA(qrCodePath As String) Section "Q&A", "" InsertQR qrCodePath, 500, 30, 400, 400 End Sub |
Presentation title:
1 2 3 4 5 6 7 |
Private Sub PresentationTitle(title As String, qrCodePath As String) Fragment ("PresentationTitle") With ActivePresentation.slides(ActivePresentation.slides.Count) .Shapes(1).TextFrame.TextRange = title End With InsertQR qrCodePath, 700, 30, 200, 200 End Sub |
Thanks:
1 2 3 4 |
Private Sub Thanks(qrCodePath As String) Fragment ("Thanks") InsertQR qrCodePath, 500, 30, 400, 400 End Sub |
Merging things together
All helper methods are private. Now we need to actually define one macro which will compose everything together:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 |
Sub combineExisting() Dim slides As Variant Dim fileTitle As String Dim qrCode As String Cleanup ' Settings ======================================== fileTitle = "Internals of Exceptions" qrCode = "Exceptions\InternalsOfExceptionsQR.png" ' Content ======================================== Fragment "Exceptions\Quote_ExceptionalSituations" PresentationTitle "Internals of Exceptions", qrCode AboutMe Agenda Array( _ "Exception mechanisms overview", _ "Implementation details:", _ " How to rethrow, catch everything, and what cannot be handled?", _ " When are exceptions thrown and how to stop it?", _ " How to handle corrupted state?", _ "Even more implemenation details:", _ " SEH and its frame.", _ " .NET and double pass.", _ " Thread.Abort implementation.", _ " AccessViolation internals.", _ " VEH and machine code generation to catch StackOverflowException") Section "Exception mechanisms overview", "" Fragment "Exceptions\CppCsharpTryCatchFinallySyntax" Fragment "Exceptions\SehVehSyntax" Fragment "Exceptions\ExceptionQuestionsOneMightAsk" Section "Implementation details", "" Fragment "Exceptions\Stacktraces" Fragment "Exceptions\CatchingEverythingAndUncatchable" Fragment "Exceptions\FixingUsing" Fragment "Exceptions\FinalizerDuringExit" Fragment "Exceptions\OutOfBandExceptions" Fragment "Exceptions\ConstrainedExecutedRegion" Section "Even more implementation details", "" Fragment "Exceptions\HowIsExceptionThrown" Fragment "Exceptions\ExceptionsInWinDBG" Fragment "Exceptions\SehVehInternals" Fragment "Exceptions\IlExceptionsMetadata" Fragment "Exceptions\TryPerformanceInCsharp" Fragment "Exceptions\LockNopBug" Fragment "Exceptions\TwoPassExceptionSystem" Fragment "Exceptions\ThreadAbortInternals" Fragment "Exceptions\AccessViolationAndStackOverflowException" Section "How to catch SOE?", "" Fragment "Exceptions\CatchingSOE" Fragment "Exceptions\Summary" QA qrCode Fragment "ReferencesBooks" Fragment "Exceptions\ReferencesMyBlog" Fragment "Exceptions\ReferencesExternalLinks" Thanks qrCode ' End ============================================= SetFooter fileTitle SetMetadataTitle fileTitle End Sub |
I start with defining variables which I’ll use later. I remove all slides. Next, I set settings like common talk name and path to the QR code.
Then goes the table of content. You can see I start with building blocks used together.
Finally, I set the footer on each slide, and update the metadata.
If I now want to add, remove, reorder, or change slides — I simply modify them here in this method. Obviously, actual content needs to be changed in the files stored as fragments. Good thing is it is stored in exactly one place. Recreating slide deck takes around 5 seconds for this table of content so it is good enough for my purposes.