activate a worksheet by index - vb.net

I have a winform with a forward and a backwards button. What I am trying to do is to allow the user to move back and forth on the workbook by clicking on the button. I thought that the best way to achieve this is by using index. However, it is giving me fits. The IDE is telling me that I have syntax errors on lines:
(WS.Index - 1).Activate()
and
If Err.Number <> 0 Then WS(1).Activate()
Here is my entire code:
Option Explicit On
Option Strict On
'Import Libraries
Imports Microsoft.Office.Tools.Excel
Imports System.Drawing
Imports System
Imports System.IO
Imports System.Drawing.Printing
Imports System.Windows.Forms
Public Class frmNavigation
Dim WB As Excel.Workbook
Dim WS As Excel.Worksheet
Private Sub btnMoveBack_Click(sender As Object, e As EventArgs) Handles btnMoveBack.Click
'This event is triggered when the Previous Sheet button is
'clicked. The sheet moves to the previous sheet. This event
'is only run throughout the clientworksheets as the tabs and other
'standard excel navigation may be disabled.
WB = CType(Globals.ThisWorkbook.Application.ActiveWorkbook, Excel.Workbook)
WS = CType(WB.ActiveSheet, Excel.Worksheet)
WS.Application.ScreenUpdating = False
On Error Resume Next
(WS.Index - 1).Activate()
If Err.Number <> 0 Then WS(1).Activate() 'If error stay in the active sheet
WS.Application.ScreenUpdating = True
End Sub

I think it should be:
WB.WorkSheets(WS.Index -1).Activate()
Instead of just
(WS.Index -1).Activate()

Related

excel vba + how to programmatically add code to button

I have a button in a workdbook (wbShared), clicking on that button a second workbook (wbNewUnshared) opens. I want to add a button to wbNewUnshared with code programmatically.
I already found how to add the button, but I didn't find how to add code to this button.
'create button
'--------------------------------------------------------
Dim objBtn As Object
Dim ws As Worksheet
Dim celLeft As Integer
Dim celTop As Integer
Dim celWidth As Integer
Dim celHeight As Integer
Set ws = wbNewUnshared.Sheets("Sheet1")
celLeft = ws.Range("S3").left
celTop = ws.Range("T2").top
celWidth = ws.Range("S2:T2").width
celHeight = ws.Range("S2:S3").height
Set objBtn = ws.OLEObjects.add(classType:="Forms.CommandButton.1", link:=False, _
displayasicon:=False, left:=celLeft, top:=celTop, width:=celWidth, height:=celHeight)
objBtn.name = "Save"
'buttonn text
ws.OLEObjects(1).Object.Caption = "Save"
I found this online:
'macro text
' Code = "Sub ButtonTest_Click()" & vbCrLf
' Code = Code & "Call Tester" & vbCrLf
' Code = Code & "End Sub"
' 'add macro at the end of the sheet module
' With wbNewUnshared.VBProject.VBComponents(ActiveSheet.name).codeModule
' .InsertLines .CountOfLines + 1, Code
' End With
But this gives an error in the last line. Anybody has a clue?
tx
EDIT:
SOLVED
Ok, the code given works, I had an error 'Programmatic Access To Visual Basic Project Is Not Trusted'. Thanks to the help of S Meaden I solved that via https://support.winshuttle.com/s/article/Error-Programmatic-Access-To-Visual-Basic-Project-Is-Not-Trusted.
after that my code worked. So thanks again.
The first code I provided assumes 1 workbook. The code I'm presenting now does not. The limitation of this is that if the arrBttns is lost, the project is reset, the link between the code and the button is lost and the procedure addCodeToButtons has to be run again.
In the wbNewUnshared, create a class module with the following code
Option Explicit
Public WithEvents cmdButtonSave As MSForms.CommandButton
Public WithEvents cmdButtonDoStuff As MSForms.CommandButton
Private Sub cmdButtonDoStuff_Click()
'Your code to execut on "Do Stuff" button click goes here
MsgBox "You've just clicked the Do Stuff button"
End Sub
Private Sub cmdButtonSave_Click()
'Your code to execut on "Save" button click goes here
MsgBox "You've just clicked the Save button"
End Sub
In the wbNewUnshared add a standard module with the following code
Option Explicit
Dim arrBttns() As New Class1
Public Sub addCodeToButtons()
Dim bttn As OLEObject
Dim ws As Worksheet
Dim i As Long
ReDim arrBttns(0)
'Iterate through worksheets
For Each ws In ThisWorkbook.Worksheets
'Iterate through buttons on worksheet
For Each bttn In ws.OLEObjects
'Expand arrBttns for valid buttons.
If bttn.Name = "Save" Or bttn.Name = "DoStuff" Then
If UBound(arrBttns) = 0 Then
ReDim arrBttns(1 To 1)
Else
ReDim Preserve arrBttns(1 To UBound(arrBttns) + 1)
End If
End If
'Link button to correct code
Select Case bttn.Name
Case "Save"
Set arrBttns(UBound(arrBttns)).cmdButtonSave = bttn.Object
Case "DoStuff"
Set arrBttns(UBound(arrBttns)).cmdButtonDoStuff = bttn.Object
End Select
Next bttn
Next ws
End Sub
In the wbNewUnshared add the following code in the ThisWorkbook module, this is to add the code to the buttons on workbook open.
Option Explicit
Private Sub Workbook_Open()
Call addCodeToButtons
End Sub
In the wbShared add the following line after you're done adding buttons
Application.Run "wbNewUnshared.xlsm!addCodeToButtons"
Original Answer
Add a class module to your project to which you add.
Option Explicit
Public WithEvents cmdButton As MSForms.CommandButton 'cmdButton can be an name you like, if changed be sure to also change the Private Sub below
Private Sub cmdButton_Click()
'Your code on button click goes here
MsgBox "You just clicked me!"
End Sub
To a module you add the code below
Option Explicit
Dim arrBttns() As New Class1 'Change Class1 to the actual name of your classmodule
'The sub which adds a button
Sub addButton()
Dim bttn As OLEObject
Dim ws As Worksheet
Set ws = ThisWorkbook.Worksheets("Sheet1")
Set bttn = ws.OLEObjects.Add(ClassType:="Forms.CommandButton.1")
ReDim arrBttns(0)
If UBound(arrBttns) = 0 Then
ReDim arrBttns(1 To 1)
Else
ReDim Preserve arrBttns(1 To UBound(arrBttns))
End If
Set arrBttns(UBound(arrBttns)).cmdBttn = bttn.Object
End Sub

VBA: Running Workbook_BeforeClose from general module and not ThisWorkbook of Active Workbook

So I have a macro I wrote that I want to run on close of the workbook.
Unfortunately, the only way to apparently do this is to place the macro in the ThisWorkbook module of the actual file as opposed to having it sit in the PERSONAL.XLSB.
This is not desirable for a few reasons:
The macro would have to be put into every workbook it needs to be run on--I have hundreds.
The workbooks would need to be saved as macro enabled which, in my experience, many email servers won't accept emails with macro enabled workbooks attached.
So ideally I would like to be able to run the macro from the PERSONAL.XLSB in just a general module.
Any suggestions about how this might be possible?
EDIT:
Per instructions at:
http://www.cpearson.com/excel/AppEvent.aspx
PERSONAL.XLSB
CExcelEvents class module
Private WithEvents App As Application
Private Sub Class_Initialize()
Set App = Application
End Sub
Private Sub App_WorkbookOpen(ByVal Wb As Workbook)
MsgBox "New Workbook: " & Wb.Name
End Sub
PERSONAL.XLSB
ThisWorkbook
Private XLApp As CExcelEvents
Private Sub Workbook_Open()
Set XLApp = New CExcelEvents
End Sub
Doesn't work if you try to open a different workbook. If you click on PERSONAL.XLSB in recent documents it will trigger the message.
Move this into another ThisWorkbook object for a specific workbook and it still only works on that workbook:
Private XLApp As CExcelEvents
Private Sub Workbook_Open()
Set XLApp = New CExcelEvents
End Sub
So even though the class module is in PERSONAL.XLSB, it appears you still have to put the above into the workbook you want it to run on it, which I think I would still require saving it as an .XLSM and would run into email filter issues.
For some reason this did start working with everything in PERSONAL.XLSB although I didn't change anything. Exciting, but would like to know why.
However, now that I am trying to change the example to actually work how I need it with BeforeClose. So I updated to the following:
PERSONAL.XLSB
CExcelEvents class module
Private WithEvents App As Application
Private Sub Class_Initialize()
Set App = Application
End Sub
Private Sub App_WorkbookBeforeClose(ByVal Wb As Workbook, Cancel As Boolean)
MsgBox "Closing the workbook."
End Sub
PERSONAL.XLSB
ThisWorkbook
Private XLApp As CExcelEvents
Private Sub Workbook_BeforeClose(Cancel As Boolean)
Set XLApp = New CExcelEvents
End Sub
Back to what happened yesterday... will only trigger when you go to close PERSONAL.XLSB. One would think that since the PERSONAL.XLSB opens with all workbooks, it would trigger regardless, but it isn't. Again, saving in the ThisWorkbook object of the target workbook works, but isn't a solution due to having to save as an .XLSM and email filters.
You can look at Application Events, which allow you to hook into events at the Application level, using code which can be in your Personal.xlsb
Here's a good starting point: http://www.cpearson.com/excel/AppEvent.aspx

Workbook_BeforeSave with every active workbook ActiveWorkbook

As I very often have to problem, that the tick vanishes in the settings: Calculate before save. (I don't know the exact term as my office version is in German).
That's why I tried to use VBA to solve the problem. I used the following code in my Excel file:
Option Explicit
Private Sub Workbook_BeforeSave(ByVal SaveAsUI As Boolean, _
Cancel As Boolean)
If Application.CalculateBeforeSave = False Then
If MsgBox("Caution! Should >calculatebeforesave< be activated?", vbYesNo) = vbYes Then
Application.CalculateBeforeSave = True
Else
End If
Else
End If
End Sub
I put this into "Thisworkbook". But I would like this code to be ran in every workbook I work with (at least all these which allow for macros).
My suggestion was to write ActiveWorkbook_BeforeSave... instead of Workbook_BeforeSave and then put the code in a module in the PERSONAL Macro file. But this doesn't work.
I think you need to use the Excel Applications events rather than workbook events to achieve this, such as in this example
In your PERSONAL workbook right click and insert a a Class Module (Class 1)
Add something similar to below to Class 1:
Public WithEvents appevent As Application
Private Sub appevent_WorkbookBeforeClose(ByVal Wb As Workbook, Cancel As Boolean)
'Add what you would like to happen before a workbook closes
End Sub
Next open ThisWorksheet and add code along these lines (I think the PERSONAL workbook opens automatically when Excel starts):
Dim myobject As New Class1
Private Sub Workbook_Open()
Set myobject = Application
End Sub

Share variables between excel and visual basic

I recently started programming with .NET visual basic using visual basic studio. I am also using excel VBA to make some macros. I would be very appreciative if someone could answer a question I have, apologies if the answer is obvious, I'm just getting started:
Basically, if I have set a variable in excel VBA, for example:
dim text as string
text = "hello world"
Would it be possible for me to use that variable when programming in visual basic and have it retain its value from when it was set in the excel VBA macro.
Please comment if you need clarification.
Many thanks.
SOLUTION:
Okay I managed to figure it out with the help of the solutions, the code that works in VB is as follows:
Imports Microsoft.Office.Interop.Excel
Imports Microsoft.Office.Interop
Imports Microsoft.Office.Core
Public Class Form1
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim oxl As excel.application
Dim owb As excel.workbook
Dim osheet As Excel.worksheet
Dim orng As excel.Range
Dim strtext As String
oxl = CreateObject("Excel.application")
owb = oxl.Workbooks.Open(Filename:="C:\Users\USERNAME\Documents\Variable Passing Test.xlsm")
oxl.Run("dosomethingtostrtext")
strtext = oxl.Run("getstrtext")
MsgBox(strtext)
End Sub
End Class
One way to access variables in another workbook's VBA code is to create functions that return those values to you. Then call those functions from your other app.
For example, let this be code in a module in your Excel file with the macro:
Option Explicit
Private strText As String
' Say you have a routine that manipulates strText (or not, even!)
Public Sub doSomethingToSTRTEXT()
strText = "Hello World!"
End Sub
' This is the function to call to retrieve strText
Public Function getSTRTEXT() As String
getSTRTEXT = strText
End Function
And this is code in a VBA project elsewhere (not running .Net on this machine, just microsoft office sad) where you have this:
Option Explicit
Sub Test()
' Declare
Dim WBK As Workbook
' Open the workbook in question
Set WBK = Workbooks.Open(Filename:="C:\Path\To\File\With\VBA.xls")
' This code's own variable
Dim strText As String
' Call the routine (or not) that does something to that workbook's vba's strText
Application.Run (WBK.Name & "!doSomethingToSTRTEXT")
' Now let's retrieve that value via function!
strText = Application.Run(WBK.Name & "!getSTRTEXT")
' Show it to me
MsgBox strText
End Sub
I would like to leave the conversion of the code right above ^ into VB.Net to you (or do a search here on SO for .Net code to handle Excel objects) and try it out

Open a workbook (mode automatic) by VBA command without recalculating it

In my VBA addin.xlam, I use workbooks.Open("C:\f.xlsm") to open workbook f.xlsm. The workbook calculation mode of f.xlsm is Automatic, thus I realize that everything in f.xlsm is recalculated automatically, after calling workbooks.Open("C:\f.xlsm"). But this is not what I want.
Is it possible to open a workbook by VBA command without refreshing it, even though the mode of the workbook is Automatic?
Edit 1:
I tried the idea #Ripster suggested:
1) I created a class model CExcelEvents in addin.xlam:
Private WithEvents App As Application
Private Sub App_WorkbookOpen(ByVal Wb As Workbook)
Application.Calculation = xlCalculationManual
End Sub
Private Sub Class_Initialize()
Set App = Application
End Sub
2) I linked CExcelEvents to the code in addin.xlam which opens f.xlsm:
Private XLApp As CExcelEvents
Sub try()
Set XLApp = New CExcelEvents
workbooks.Open ("C:\f.xlsm")
End Sub
Then, what try() does turns out to be first opening f.xlsm (which triggers automatic recalculation), then changing its calculation mode to manual. The workbook has been already re-calculated before changing the mode --- it is too late! Does anyone have any idea?
Try setting XLCalculation to Manual immediately before you open the workbook:
Sub try()
Application.Calculation = xlCalculationManual
workbooks.Open ("C:\f.xlsm")
End Sub