Adding shapes/pages to visio on shape drop - vba

Hello I am fairly new to VBA in visio, and I am trying to add functionality to a visio template so that a page will be added to the active document whenever a specific shape is dropped onto a page. I looked through MSDN and found an example using Application.ShapeAdded function but the active document I am working in doesn't seem to be responding to my modified code.
Private Sub Document_ShapeAdded(ByVal vsoShape As Visio.IVShape)
Dim vsoMaster As Visio.Master
'Get the Master property of the shape.
Set vsoMaster = vsoShape.Master
'If Visio shape added is named "SC" add a new page
If vsoMaster.Name = "SC" Then
NewPage
End If
End Sub
I drop the shape master "SC", which I confirmed is the name of the shape master, and nothing happens. The MSDN verbage describes Application.ShapeAdded as an event listener to the open application. Am I missing something or is there possibly a better way to do this I am not thinking of?
Here is the MSDN description: https://msdn.microsoft.com/en-us/library/office/ff766392.aspx

The Document_ShapeAdded event will only apply to the document in which the VBA code is located.
You'd have to declare an application object withevents and have it watch for that event.
Example (in an object or ThisDocument module):
Private WithEvents App as Visio.Application
Private Sub App_ShapeAdded(ByVal Shape As IVShape)
Call ActiveDocument.Pages.Add() ' etc..
End Sub
Alternatively, if it's something simple, you could just add a shapesheet CALLTHIS function on the master shape in question, which just fires a VBA routine to add a new page or whatever you have to do.

Related

CommandBars("Text").Controls not deleted when exiting the document - VBA word add-in

I'm trying to create a add in for MS Word with VBA.
It has a "AutoExec" procedure that creates a new item in the CommandBar("Text") collection (right click menu) and a "AutoExit" that deletes this created item.
As an example, I tried the code below that create an item "How Many Pages?", which executes a macro displaying the number of pages in the active document.
This is the AutoExec Code:
Public Sub AutoExec()
Dim objcommandbutton As CommandBarButton
Call MsgBox("AutoExec")
Set objcommandbutton = Application.CommandBars("Text").Controls.Add _
(Type:=msoControlButton, Before:=1)
objcommandbutton.Caption = "How Many Pages?"
objcommandbutton.OnAction = "HowManyPages"
End Sub
This is the AutoExit Code:
Public Sub AutoExit()
Dim objcommandbutton As CommandBarControl
Call MsgBox("AutoExit")
For Each objcommandbutton In Application.CommandBars("Text").Controls
If objcommandbutton.Caption = "How Many Pages?" Then
objcommandbutton.Delete
End If
Next objcommandbutton
End Sub
This is the Main Macro Code:
Public Sub HowManyPages()
If Documents.Count > 0 Then
Call MsgBox(ActiveDocument.BuiltInDocumentProperties("Number of Pages"))
Else
Call MsgBox("No document is currently active.")
End If
End Sub
When exiting the document, the Button previously added in CommandBars("Text") collection is not deleted. I see this when I open a new blank Word document and the button remains in the right click menu.
I know that the routine is performed correctly because there is a MsgBox instruction to verify it.
This only happen with the AutoExit subroutine of a add-in, that is, loaded as a add-in: running the code in a macro with vba module works fine.
Any help?
When working with the CommandBars object model in Word it's necessary to always specify the Application.CustomizationContext.
Word can save keyboard layouts and CommandBar customizations in various places: the Normal.dotm template, the current template or the current document. The default when you create a CommandBar addition may not be the3 same as the default when trying to delete something.
Since this is an add-in, I assume you want the change for the entire Word environment (any open document). In that case, use the context NormalTemplate. Use this before any calls to CommandBar:
Application.CustomizationContext = NormalTemplate
Note: for saving a customization in the current document: = ActiveDocument; for saving in the template attached to the current document: = ActiveDocument.AttachedTemplate.
I solved my problem with a workaround:
I was trying to "add" a template (.dotm) as a add-in (in the "Templates and Add-ins" window) to use my VBA project in a new document. That's why I was using the AutoExec() and AutoExit() procedures.
But only now I figure out that just "attaching" the .dotm template to the active document (in the same "Templates and Add-ins" window, as show in the figure below) makes the functions Private Sub Document_Open() and Private Sub Document_Close() to run normally. Which solves my problem.
Even so, I think there is a certain "issue" with the AutoExit() procedure when trying to change the CommandBars itens. But that's ok for now.
enter image description here

How to add handwritten signature using Excel's Ink Tools?

I want to add a handwritten digital signature to some of my company's forms.
The goal is to, select a document, add a signature (through the use of a drawing pad, which can be done with Excel's Ink Tools) and store the file in the server as PDF. This would cut out the necessity of printing and then scanning the form back in to obtain a signature.
I'm using Excel for the main interface for file manipulation and search. I've not found any references/libraries for the use of Excel - Ink Tools through VBA.
How do I start Ink Tools Objects in VBA? Would I have to use a different software to get the signature?
Update:
After #Macro Man pointed me in the right direction I found some material that helped get the eSignature up and running.
I've found some material on MSDN Digital Ink Signatures - Concepts and Technologies and InkPicture Class that talk about the Ink collection on VB.net and C# through a PictureBox/Userform , this coupled with the InkEdit Control in another Stackoverflow response in which I realised that VBAs tool box had a InkPicture Control additional control that could be utilized to collect the handwritten eSignature through User form.
Please find below step by step:
In VBAs toolbox on additional control under Tools > Additional Controls there is the InkPicture Control which allows you to create a signature Userform.
Once Added InkPicture can be used as any other control on the toolbox.
Then its a case of initialising the UserForm for the Signature request. I'm using a drawing pad, but other hardware should work as well.
And storing or utilising the resultant image at need, in my case saving a temp version in the server to then resize and add to a Word document.
Edit:
After answering a similar question in here, on how to use Userform InkPicture to input image signature into a worksheet/specific cell, I thought I'd edit this answer for those interested.
The below code will allow you to, open the userform so the user can sign the ink field, save the image temperately, add the InkPicture to your worksheet and kill the temp image.
Set up your UserForm (mine is set up like this, with a couple extra options) the UserForm is named Signature_pad, the essential option you need is Private Sub Use_Click().
This is the code inside the Userform:
Private Sub Use_Click()
'dim object type and byte array to save from binary
Dim objInk As MSINKAUTLib.InkPicture
Dim bytArr() As Byte
Dim File1 As String
'get temp file path as $user\Temp\[file name]
FilePath = Environ$("temp") & "\" & "Signature.png"
' set objInk as image/strokes of InkPicture control form object
Set objInk = Me.SignPicture
'if object is not empty
If objInk.Ink.Strokes.Count > 0 Then
'get bytes from object save
bytArr = objInk.Ink.Save(2)
'create file for output
Open FilePath For Binary As #1
'output/write bytArr into #1/created (empty)file
Put #1, , bytArr
Close #1
End If
'set public File as file path to be used later on main sub
Signature.File = FilePath
Unload Me
End Sub
Private Sub Cancel_Click()
End
End Sub
Private Sub ClearPad_Click()
'delete strokes/lines of signature
Me.SignPicture.Ink.DeleteStrokes
'refresh form
Me.Repaint
End Sub
Below is the Main sub (Module called Signature) to call the userform and handle the signature, you can call this Sub with a button or form another Sub.
'public temp file path
Public File
Sub collect_signature()
'Dim and call userform
Dim myUserForm As Signature_pad
Set myUserForm = New Signature_pad
myUserForm.Show
Set myUserForm = Nothing
'insert image/signature from temp file into application active sheet
Set SignatureImage = Application.ActiveSheet.Shapes.AddPicture(File, False, True, 1, 1, 1, 1)
'scale image/signature
SignatureImage.ScaleHeight 1, True
SignatureImage.ScaleWidth 1, True
'image/signature position
SignatureImage.Top = Range("A1").Top
SignatureImage.Left = Range("A1").Left
'delete temp file
Kill File
End Sub
Be sure to rename either the Userform Name and Buttons Name Or the code to match the names of you buttons.
Is it the InkEdit Control you're after?
This is one of the standard libraries that you can find in Tools->References

Modify Chart properties in Access report via VBA (error 2771)

I am building an Access report (2010 version), and would like to be able to customize it based on the user's selections on a form. When I run it, I get error 2771: The bound or unbound object frame you tried to edit does not contain an OLE object.
This is the code to pass the parameter:
Private Sub Command120_Click()
DoCmd.OpenReport ReportName:="rpt_EODGraph", View:=acViewPreview, _
OpenArgs:=Me!Text0.Value
End Sub
This is the code to open the report.
Private Sub Report_Open(Cancel As Integer)
Dim ch As Chart
Set ch = Me.Graph3.Object.Application.Chart 'This line generates the error
ch.ChartTitle.text = OpenArgs
End Sub
I've found at least one person saying that this is not actually possible to do on a report. (http://www.access-programmers.co.uk/forums/showthread.php?t=177778&page=2 Note that this is page 2 of a 2 page forum discussion...) Can anyone corroborate or refute?
Apparently the Report has to have some kind of focus before OLE objects are accessible.
It is enough if you click on it or set the focus to something:
Private Sub Report_Open(Cancel As Integer)
Dim ch As Object
Me.RandomButton.SetFocus
Set ch = Me.Diagramm11.Object
ch.ChartTitle.Text = "Hello"
End Sub
This works. I just set a button on the report that gets the focus. Perhaps you find something more elegant ;)

Excel spreadsheet events - how to create generic MSForms.Control event handler for all ActiveX controls?

Background/Introduction
In an effort to tame wild ActiveX objects, I am implementing custom event handlers which I can assign to each ActiveX control.
I currently have implemented one such as the class, WSCheckboxEventHandler:
Option Explicit
Private WithEvents m_ole As MSForms.CheckBox
Private m_ws As Worksheet
Public Sub init(p_SourceOLE As MSForms.CheckBox, p_ws As Worksheet)
Set m_ole = p_SourceOLE
Set m_ws = p_ws
End Sub
Private Sub m_ole_Click()
Debug.Print "Checkbox click for " + m_ole.name
m_ole.Left = m_ole.Left
m_ole.Width = m_ole.Width
m_ole.Height = m_ole.Height
m_ole.Top = m_ole.Top
m_ws.Shapes(m_ole.name).ScaleHeight 1.25, msoFalse, msoScaleFromTopLeft
m_ws.Shapes(m_ole.name).ScaleHeight 0.8, msoFalse, msoScaleFromTopLeft
End Sub
In a worksheet module, with m_WSObjectEventHandler as a private variable, the following sets the handler up perfectly:
Set m_WSObjectEventHandler = New WSCheckboxEventHandler
m_WSObjectEventHandler.init Sheet1.chk_DraftMode, Sheet1
Basically this is a hack work around for the objects resizing visually - by calling these commands I force them to remain sized correctly. The linked question above details this problem.
However, this requires me to create a separate event handler for each type of control. I have about 7 now, so I've created a separate class which basically serves as a pseudo factory for these, passing in the worksheet, then iterating through all the ActiveX objects for it and creating the appropriate handler via an ugly select statement:
For Each mOLE In m_ws.OLEObjects
Select Case TypeName(mOLE.Object)
Case "CheckBox"
Set mCheckBoxHndlr = New WSCheckboxEventHandler
mCheckBoxHndlr.init mOLE.Object, m_ws
m_CheckBoxes.Add mCheckBoxHndlr
'etc... there are a lot of these!
Case Default
Debug.Print "Default"
End Select
Next mOLE
This lets me however have a single worksheet variable contain all the event handlers as member collections. Ugly? Yes, but it will allow better code reuse.
Question
I want to be able to implement a single event handler for all ActiveX object types (there are many, the factory type class above is going to have a huge ugly switch statement). Basically changing MSForms.CheckBox to MSForms.Control in the above event handler. It'd be great to not have to copy the same code into 5+ event handlers and maintain that. Not to mention avoiding the ugly select statement.
How can I refer to the control as a valid MSForms.Control object and consequentially setup the event handler? I basically want to typecast the MSForms.CheckBox into a MSForms.Control object.
Alternatively, is it possible to get the MSForms.Control object somehow? It doesn't seem to be part of the OLEObject.Object at all (I get type errors doing this).

Can I turn a Microsoft.Office.Interop.Excel.Chart object into a Microsoft.Office.Tools.Excel.Chart object?

Basically I've coded an Excel 2007 project in VB.NET 2010 that allows you to create charts with a fair amount of interactivity. I want the user to be able to save and reopen this workbook and still have that interactivity in any already-created charts, so they don't have to re-create them.
When I create the charts, I use Sheet1.Controls.AddChart(...), which returns a Microsoft.Office.Tools.Excel.Chart with which I can handle events and such. However, when I reopen the file and look through the Sheet1.Controls collection, there are no Chart objects. Accessing the charts through Sheet1.ChartObjects.Chart gives me Interop Charts, when I need the Tools Charts.
Is there a better way to do this? Should I just be using Interop charts from the get-go?
According to the MSDN article, you can use the HasVstoObject and GetVstoObject methods to convert a native object into a host control. The docs don't specifically mention the chart object, though.
Paul, I apologize in advance if I'm not addressing your issue. Up until now I didn't realize there were two types of charts and I'd be interested in understanding the issues with them. At any rate, after some fooling around I was able to create a chart, name it, have it persist and have it respond to events. It does rely on interop charts though:
Public Class ThisWorkbook
Dim clsChart As cChart
Private Sub ThisWorkbook_Startup() Handles Me.Startup
Dim coChartObj As Microsoft.Office.Interop.Excel.ChartObject
Dim chtMyChart As Microsoft.Office.Interop.Excel.Chart
Dim i As Int32
With Globals.Sheet1
For i = 1 To .ChartObjects.count
If .ChartObjects(i).Name = "MyChart" Then
chtMyChart = .ChartObjects(i).chart
Exit For
End If
Next i
If chtMyChart Is Nothing Then
chtMyChart = .Shapes.AddChart.Chart
chtMyChart.SetSourceData(.Range("A1:B2"))
coChartObj = chtMyChart.Parent
coChartObj.Name = "MyChart"
End If
clsChart = New cChart
clsChart.chtChart = chtMyChart
End With
End Sub
End Class
Public Class cChart
Public WithEvents chtChart As Microsoft.Office.Interop.Excel.Chart
Private Sub chtChart_Resize() Handles chtChart.Resize
MessageBox.Show("resize")
End Sub
End Class