Powerpoint Event handling - Prevent New Presentation - vba

I am creating a Powerpoint Add-in. I would like to restrict user from either:
Create New Presentation
Open an existing Presentation
I have used this tutorial/overview to trap the NewPresentation and PresentationOpen events. I would like to close any presentation initialized through these events before the user can interact with it. When I try to close it using .Close method, I receive an error (screenshot below).
In my class module, I have the following to trap the NewPresentation event. This works fine, I receive the message box and Pres is a valid Presentation object that can be passed to the CloseNewPres routine.
Private Sub PPTEvent_NewPresentation(ByVal Pres As Presentation)
MsgBox "You cannot use this Charting tool with multiple presentations.", vbInformation
CloseNewPres Pres
End Sub
In a standard module, I have the CloseNewPres routine, which I expect to close the "New" presentation:
Sub CloseNewPres(Pres As Presentation)
Application.Presentations(Pres.Name).Close
'Pres.Close '<~~ This also fails.'
End Sub
I receive the following error.
Any thoughts on why this is happening? Or what I can do to close these presentations?

In my opinion you need to use another event which is quite similar to one you used:
Private Sub PPApp_AfterNewPresentation(ByVal Pres As Presentation)
If I set Pres.Close within proposed one it really closes new created presentation.

The best explanation I have (and this is my own interpretation) is that it's barking because you're trying to remove an object (the presentation) while the event handler is dealing with it. Kind of asking the event handler to pull the rug out from under its own feet.
By the way, it won't work to call another routine from within the event handler and have IT do the deed because the event handler's still active.
One way around this:
Have the event handler load a form modelessly.
When you do that, the remaining code in the event handler runs to completion.
The form's initialization code can close the presentation if your conditions are met.
The form needn't ever become visible for this to work.
Yep. Seems bizarre. But it works.

In C#.Net you can do by this way,
using pp = Microsoft.Office.Interop.PowerPoint;
pp.Application app = Globals.ThisAddIn.Application;
app.AfterNewPresentation += Event_PresNew;
public void Event_PresNew(pp.Presentation pres)
{
//.....your code here....
}
Also you can see the list of all the events you can use in powerpoint here below,
https://learn.microsoft.com/en-us/office/vba/api/powerpoint.application.newpresentation(even)

Related

Closing any open userform

After a good search and some (over)thinking I came to the conclusion that I have no answer on what seems to be a simple question.
I have an excel document with many (20+) userforms in it. If you press a button (that is not in the userform, but just on the excel sheet) to start over again it should close any userform that's open at that moment.
I tried with unload me but of course I got an error when there wasn't any open userform.
Then I tried to add on error resume next thinking it would skip the line if there was no userform and therefore not giving an error but just continue what I want it to do. (opening a new userform).
It did indeed not give me the error anymore but it doesn't close any open userform as well (when there is one open).
So here I am, hoping someone here can help me as I don't know what to do. I could list up all of the userforms I suppose but it should be possible to go faster and automatically I suppose?
Some more info: It is never possible to have more than one userform open at the same time. // The button I want to create closes all the userforms if there are any and leads the user back to the main menu.
Thanks in advance!
KawaRu
Try calling the following when you want to unload all forms
Sub UnloadAllForms(Optional dummyVariable As Byte)
'Unloads all open user forms
Dim i As Long
For i = VBA.UserForms.Count - 1 To 0 Step -1
Unload VBA.UserForms(i)
Next
End Sub
This is hopefully the worst code that I have written in the last 5 years, but it will close any breathing form in Excel that you may have (and will kill any variables etc) :
Public Sub CloseAll()
End
End Sub
Use with caution!
From the MSDN End Statement:
The End statement stops code execution abruptly, without invoking the Unload, QueryUnload, or Terminate event, or any other Visual Basic code.
Code you have placed in the Unload, QueryUnload, and Terminate events offorms and class modules is not executed.
Objects created from class modules are destroyed, files opened using the Open statement are closed, and memory used by your program is freed.
Object references held by other programs are invalidated.
The End statement provides a way to force your program to halt. For normal termination of a Visual Basic program, you should unload all forms.
Your program closes as soon as there are no other programs holding references to objects created from your public class modules and no code executing.

Track which hyperlink called a slide

A quiz was created using PowerPoint. Each question has multiple-choice answers. For each answer, a hyperlink was created to either send it to a slide indicating a correct answer (only one) or a slide indicating an incorrect answer (all the others). It needs to capture which answer the user clicked. The correct one is easy because only one answer will open the correct slide, but all the incorrect answers go to the same slide.
Is there a way to capture what object/hyperlink ID/Name, etc. that "called" a slide? I want to avoid writing separate Events for each hyperlink. Is there a way to capture it in the Application Event?
I have the following code.
Class Code:
Public WithEvents PPTEvent As Application
Private Sub PPTEvent_SlideShowNextSlide(ByVal Wn As SlideShowWindow)
MsgBox ActivePresentation.Slides.Item(1).SlideNumber
End Sub
PowerPoint supports action settings to run a macro. Define the macro as follows:
Sub ClickMe(Shp As Shape)
MsgBox Shp.Name
End Sub
Now, assign the action setting to run this macro and see how the shape reference is passed along. You can use that to avoid using events altogether. Drive the quiz using code.

Application fail to exit when a runtime created object is used

I'm using vb.net 2013, and I have configured Shutdown mode to "When last form is closed".
On my main form, I have a menu item which has this code to close the application:
Application.Exit
Everything is working fine, except one case:
When I open a specific form, where a Combobox is created on runtime and I've used Addhandler to subscribe to several events.
The combobox is created when pressing a button.
When I open this form and I don't create the combobox, everything is working ok. If the combobox is created, when I close this form and try to close the application using the menu item, nothing happens. The application is not closed and no error message is displayed. (the same situation occurs when I try to close the main form with "x" button)
On the form's (where I have the combobox) close event , I tried to put a line of code:
MyCombobox.dispose()
But the situation is the same.
What can I do? Thank you!
What I do from my little experience is
1.) remove the MyCombobox from its parent control (I'm think this is in your combobox close event).
2.) set the MyCombobox to Nothing
3.) Dispose() it.
MyCombobox = Nothing
MyCombobox.Dispose()
It would be useful to see some part of your code for the close event so we can help you check. More power to you!
Update based on OP's comment:
I have read the following from MSDN: https://msdn.microsoft.com/en-us/library/system.windows.forms.form.closing(v=vs.110).aspx
From this, it is important to note that:
The Form.Closed and Form.Closing events are not raised when the Application.Exit method is called to exit your application. If you have validation code in either of these events that must be executed, you should call the Form.Close method for each open form individually before calling the Exit method.

How do I access some sort of OnLoad event in Word 2007 VBA?

I am trying to have some code fire when the document is first loaded, but there are two problems. First, I don't know which method to call to get something to fire when the document is first opened. Second, if they have macros disabled, how can I be sure that it gets called when they are enabled?
Thanks!
The Document_Open event is sent when your document is first loaded. To make use of it, enter the following in the VBA code for ThisDocument:
Private Sub Document_Open()
'// your code goes here'
End Sub
As for the disabled macros, I'm not aware of a method that will be called as soon as macros are enabled.

Force multi-threaded VB.NET class to display results on a single form

I have a windows form application that uses a Shared class to house all of the common objects for the application. The settings class has a collection of objects that do things periodically, and then there's something of interest, they need to alert the main form and have it update.
I'm currently doing this through Events on the objects, and when each object is created, I add an EventHandler to maps the event back to the form. However, I'm running into some trouble that suggests that these requests aren't always ending up on the main copy of my form. For example, my form has a notification tray icon, but when the form captures and event and attempts to display a bubble, no bubble appears. However, if I modify that code to make the icon visible (though it already is), and then display the bubble, a second icon appears and displays the bubble properly.
Has anybody run into this before? Is there a way that I can force all of my events to be captured by the single instance of the form, or is there a completely different way to handle this? I can post code samples if necessary, but I'm thinking it's a common threading problem.
MORE INFORMATION: I'm currently using Me.InvokeRequired in the event handler on my form, and it always returns FALSE in this case. Also, the second tray icon created when I make it visible from this form doesn't have a context menu on it, whereas the "real" icon does - does that clue anybody in?
I'm going to pull my hair out! This can't be that hard!
SOLUTION: Thanks to nobugz for the clue, and it lead me to the code I'm now using (which works beautifully, though I can't help thinking there's a better way to do this). I added a private boolean variable to the form called "IsPrimary", and added the following code to the form constructor:
Public Sub New()
If My.Application.OpenForms(0).Equals(Me) Then
Me.IsFirstForm = True
End If
End Sub
Once this variable is set and the constructor finishes, it heads right to the event handler, and I deal with it this way (CAVEAT: Since the form I'm looking for is the primary form for the application, My.Application.OpenForms(0) gets what I need. If I was looking for the first instance of a non-startup form, I'd have to iterate through until I found it):
Public Sub EventHandler()
If Not IsFirstForm Then
Dim f As Form1 = My.Application.OpenForms(0)
f.EventHandler()
Me.Close()
ElseIf InvokeRequired Then
Me.Invoke(New HandlerDelegate(AddressOf EventHandler))
Else
' Do your event handling code '
End If
End Sub
First, it checks to see if it's running on the correct form - if it's not, then call the right form. Then it checks to see if the thread is correct, and calls the UI thread if it's not. Then it runs the event code. I don't like that it's potentially three calls, but I can't think of another way to do it. It seems to work well, though it's a little cumbersome. If anybody has a better way to do it, I'd love to hear it!
Again, thanks for all the help - this was going to drive me nuts!
I think it is a threading problem too. Are you using Control.Invoke() in your event handler? .NET usually catches violations when you debug the app but there are cases it can't. NotifyIcon is one of them, there is no window handle to check thread affinity.
Edit after OP changed question:
A classic VB.NET trap is to reference a Form instance by its type name. Like Form1.NotifyIcon1.Something. That doesn't work as expected when you use threading. It will create a new instance of the Form1 class, not use the existing instance. That instance isn't visible (Show() was never called) and is otherwise dead as a doornail since it is running on thread that doesn't pump a message loop. Seeing a second icon appear is a dead give-away. So is getting InvokeRequired = False when you know you are using it from a thread.
You must use a reference to the existing form instance. If that is hard to come by (you usually pass "Me" as an argument to the class constructor), you can use Application.OpenForms:
Dim main As Form1 = CType(Application.OpenForms(0), Form1)
if (main.InvokeRequired)
' etc...
Use Control.InvokeRequired to determine if you're on the proper thread, then use Control.Invoke if you're not.
You should look at the documentation for the Invoke method on the Form. It will allow you to make the code that updates the form run on the thread that owns the form, (which it must do, Windows forms are not thread safe).
Something like
Private Delegate Sub UpdateStatusDelegate(ByVal newStatus as String)
Public sub UpdateStatus(ByVal newStatus as String)
If Me.InvokeRequired Then
Dim d As New UpdateStatusDelegate(AddressOf UpdateStatus)
Me.Invoke(d,new Object() {newStatus})
Else
'Update the form status
End If
If you provide some sample code I would be happy to provide a more tailored example.
Edit after OP said they are using InvokeRequired.
Before calling InvokeRequired, check that the form handle has been created, there is a HandleCreated property I belive. InvokeRequired always returns false if the control doesn't currently have a handle, this would then mean the code is not thread safe even though you have done the right thing to make it so. Update your question when you find out. Some sample code would be helpful too.
in c# it looks like this:
private EventHandler StatusHandler = new EventHandler(eventHandlerCode)
void eventHandlerCode(object sender, EventArgs e)
{
if (this.InvokeRequired)
{
this.Invoke(StatusHandler, sender, e);
}
else
{
//do work
}
}