Copy VBA code from one Worksheet to another using VBA code - vba

Ok here is what I want to accomplish: I am trying to copy all the VBA code from "Sheet2" to "Sheet 3" code pane. I'm NOT referring to copying a Module from one to another but the excel sheet object code.
I already added a Reference to MS VB for Applications Extensibility 5.3
I'm not sure where to start but this is what I have started with and its not going anywhere and probably all wrong. Please Help - Simply want to programmatically copy sheet vba code to another sheet vba pane.
Dim CodeCopy As VBIDE.CodePane
Set CodeCopy = ActiveWorkbook.VBProject.VBComponents("Sheet2").VBE
ActiveWorkbook.VBProject.VBComponenets("Sheet3").CodeModule = CodeCopy

Use the CodeModule object instead of the CodePane, then you can create a second variable to represent the destination module (where you will "paste" the code).
Sub test()
Dim CodeCopy As VBIDE.CodeModule
Dim CodePaste As VBIDE.CodeModule
Dim numLines As Integer
Set CodeCopy = ActiveWorkbook.VBProject.VBComponents("Sheet2").CodeModule
Set CodePaste = ActiveWorkbook.VBProject.VBComponents("Sheet3").CodeModule
numLines = CodeCopy.CountOfLines
'Use this line to erase all code that might already be in sheet3:
'If CodePaste.CountOfLines > 1 Then CodePaste.DeleteLines 1, CodePaste.CountOfLines
CodePaste.AddFromString CodeCopy.Lines(1, numLines)
End Sub
In addition to adding a reference to "Reference to MS VB for Applications Extensibility 5.3"
You'll also need to enable programmatic access to the VBA Project.
In Excel 2007+, click the Developer item on the main Ribbon and then
click the Macro Security item in the Code panel. In that dialog,
choose Macro Settings and check the Trust access to the VBA project
object model.

Thank you all! After testing multiple suggestions above, where "b" is the Worksheet name, you must use .CodeName, NOT .Name
Set CodePaste = ActiveWorkbook.VBProject.VBComponents(WorkShe‌ets(b).CodeName).Cod‌​eModule
If you have set your target worksheet as an object:
Dim T As Worksheet
Set T = Worksheets("Test")
Then you simply need:
Set CodePaste = ActiveWorkbook.VBProject.VBComponents(Worksheets(T.Name).CodeName).Cod‌​eModule

Related

Programmatically add workbook event and save it

I have a list of excel files in a spreadsheet. I'd like to loop through them and add a worksheet event to each. Save it, close it and move on to the next. The problem is that when I reopen (manually) the workbook the code is gone.
Inside the for each loop:
Set xl = Workbooks.Open(filepath)
addCode xl 'subroutine to add code
xl.Save
xl.Close SaveChanges:=False
The addCode subroutine is:
Sub addCode(book As Excel.Workbook)
acsh = book.ActiveSheet.CodeName
startline = book.VBProject.VBComponents(acsh).CodeModule.CreateEventProc("SelectionChange", "Worksheet") + 1
book.VBProject.VBComponents(acsh).CodeModule.InsertLines startline, codetoadd
End Sub
If I comment out xl.Close the code is in the workbook and works. I can manually save and close the file and the code remains. I've added a break point between xl.save and xl.close and made a copy of the file. After the code is done neither has the changes. I've tried using xl.saveas and xl.close SaveChanges:=True. All have identical results.
I'm using Excel 2013, I've told excel to trust access to the VBA object model. I've tried using XLS files and XLSM files. Obviously XLSX won't work.
Here is some sample code which is working for me on Excel 2010. The changes I made to your example code are:
use a .xlsm for the target workbook - I know you said you already did that.
reference a specific worksheet in the AddCode sub rather than pick up the sheet name from ActiveSheet.
set the workbook dirty status per Ralph's comment
Don't set the SaveChanges flag when closing the target workbook
Other than that, my version is pretty similar to yours. I think it is the wb.Saved = False line that does the trick i.e. the dirty flag. I tried to use the SaveAs method on the VBProject itself thinking it would be the same as hitting the save button when you are in the VBA Editor itself. However, this just gives unhelpful errors.
Here's the sample code:
Option Explicit
Sub Test()
Dim wbTarget As Workbook
Dim strCode As String
' get target workbook
Set wbTarget = Workbooks.Open("\\server\path\Book3.xlsm")
' test setting code to worksheet change
strCode = "Debug.Print ""Sheet selection changed to: "" & Target.Address"
AddWorksheetChangeCode wbTarget, "Sheet1", strCode
' test saving the target workbook
With wbTarget
' set book to dirty to force the save
.Saved = False
.Save
.Close
End With
End Sub
Sub AddWorksheetChangeCode(ByRef wb As Workbook, strWorksheetName As String, strCode As String)
Dim intInsertLine As Integer
' create stub for event and get line to insert
intInsertLine = wb.VBProject.VBComponents(strWorksheetName).CodeModule.CreateEventProc("SelectionChange", "Worksheet") + 1
' add event logic
wb.VBProject.VBComponents(strWorksheetName).CodeModule.InsertLines intInsertLine, strCode
End Sub

Defining a variable as a range - VB.NET

I think I'm losing my mind - how do you declare a variable as a string and then set it equal to a range in an Excel workbook in VB.NET? In VBA this was easy:
Dim SQL as string
SQL = ActiveWorkbook.Sheets("MySheet").Range("SQL")
If I try do something like this in VB.NET (in Visual Studio 2015), first I can't find Activeworkbook. Second, if I try Excel.Range("SQL"), I get an error saying that 'Range' is an interface type and cannot be used as an expression. Also, it doesn't look like the Range data type exists either. Surely this functionality exists in VB.NET, right?
Thanks for the help!
To work on Excel since VB.NET, first you must add the reference to your Project :
Microsoft.Office.Interop
To Add a Reference :
In Solution Explorer, right-click on the References node and choose Add Reference.
Import the Reference in your code :
Imports Microsoft.Office.Interop
Try to use this code :
Dim AppExcel As New Excel.Application 'Create a new Excel Application
Dim workbook As Excel.Workbook = AppExcel.Workbooks.Add() 'Create a new workbook
Dim sheet As Excel.Worksheet = workbook.Sheets("Sheet1") ' Create variable a Sheet, Sheet1 must be in WorkBook
'Work with range
Dim cellRange1 As Excel.Range = sheet.Range("A1") 'Range with text address
cellRange1.Value = "Text in Cell A1"
Dim cellRange2 As Excel.Range = sheet.Cells(2, 2) 'Range("B2:B2") with index; Cells(N°Row,N°Col)
cellRange2.Value = "Text in Cell B2"
Dim tableRange3 As Excel.Range = sheet.Range("A1:F4") 'Range with text address
Dim tableRange4 As Excel.Range = sheet.Range(sheet.Cells(1, 1), sheet.Cells(4, 6)) 'Range("A1:F4") with index; Cells(N°Row,N°Col)
AppExcel.Visible = True 'To display the workbook
Code without variable sheet
Dim AppExcel as New Excel.Application
Dim workbook As Excel.Workbook = AppExcel.Workbooks.Add()
'Range
Dim cellrange1 as Excel.Range = AppExcel.ActiveWorkbook.Sheets("Feuil1").Range("A1")
You would need to start from your application object. Suppose that's AppExcel:
Dim AppExcel As New Excel.Application
From there, you could do:
Dim cellrange1 as Excel.Range = AppExcel.ActiveWorkbook.Sheets("MySheet").Range("SQL")
Because you've declared cellrange1 as a Range it can't be set to Range("SQL").Value.
Value returns an object which is the value contained in that Range.
That's so wordy. To put it (maybe) more clearly, Range("SQL") returns a Range. Range("SQL").Value returns an object.
If you want to get the value, that would be cellrange1.Value, or perhaps cellrange1.Text. Assuming that the range contains some sort of SQL, I'd go with Text.
An unfortunate aspect of Excel interop programming is that many properties return objects rather than strongly-typed values. For example, the object returned by Range.Text is always going to be a string, but the property still returns an object. That means that Visual Studio intellisense will often not tell you what type a property returns. You'll need to look up properties and functions in the documentation to really know what they return.

Save Excel Sheet text only to text file VBA

I am trying to copy the values of one column in a sheet to a text file. The code I currently have causes runtime error 434.
Sheets("Output to fcf.1").Columns("A").SaveToText "P:\4_Calcs\02. Flag Mapping\test_.txt"
If I try and save the whole sheet
Sheets("Output to fcf.2").SaveToText "P:\Clear Project Drive\CLE10276 AWS SMP Model Assessmnts\4_Calcs\02. Flag Mapping\test2_.txt"
I get the entire sheet converted into text rather than just the text in the sheet. Is there a simple way to do this?
Thanks in advance!
Not sure which Excel version you have but I don't see a method for SaveToText.
But this procedure should work, or at least get you started...
Sub SaveColumn(sheetName As String, columnName As String, fileName As String)
Dim cell
Dim fso
Dim file
Set fso = CreateObject("Scripting.FileSystemObject")
Set file = fso.CreateTextFile(fileName, True)
For Each cell In Sheets(sheetName).Columns(columnName).Cells
If cell.Value <> "" Then
file.WriteLine cell.Value
End If
Next
file.Close
Set file = Nothing
Set fso = Nothing
End Sub
To call it...
SaveColumn "Output to fcf.1", "A", "P:\4_Calcs\02. Flag Mapping\test_.txt"
This is designed to be used as a macro.
Step by step guide:
1) From excel, hit Alt+F11 on your keyboard.
2) From the menu bar, click Insert, then Module
3) Copy and paste the code provided below into the new module that opens.
NOTE: DocPath = "C:\docs\data.txt" should be wherever you want the output file saved, including the file's actual name. Remember, the folder you want the output file to be located in should ALREADY exist. This does not create the folder if it can't be found.
4) From the menu bar, click Tools, then References. Make sure both "Microsoft Office 14.0 Object Library" as well as "Microsoft Word 14.0 Object Library" are checked, and hit okay (See screenshot for details)
5) Save the document as an .xlsm file (This file type supports Macros)
6) Close the VBA editor. Back in Excel, on the ribbon click View and then Macros. Your new macro should be in the list as ExportToTXT
7) Select it and hit run.
Sub ExportToTXT()
Dim DocPath As String
Dim MsgBoxCompleted
Columns("A").Select
Dim AppWord As Word.Application
Set AppWord = CreateObject("Word.Application")
AppWord.Visible = False
Selection.Copy
DocPath = "C:\docs\data.txt"
'Create and save txt file
AppWord.Documents.Add
AppWord.Selection.Paste
AppWord.ActiveDocument.SaveAs2 Filename:=DocPath, FileFormat:=wdFormatText
Application.CutCopyMode = False
AppWord.Quit (wdDoNotSaveChanges)
Set AppWord = Nothing
MsgBoxCompleted = MsgBox("Process complete.", vbOKOnly, "Process complete")
End Sub
Good luck, and if you have any questions, don't hesitate to ask.
NOTE: These directions might seem overly simplified for your skill level, but I wrote the answer like this to potentially help others in the future.
EDIT
Change
DocPath = "C:\docs\data.txt"
to
DocPath = "C:\docs\data.fcf"
And change
AppWord.ActiveDocument.SaveAs2 Filename:=DocPath, FileFormat:=wdFormatText
to
AppWord.ActiveDocument.SaveAs2 Filename:=DocPath
The output file will be .fcf format. Whether or not it will open properly is something I'm not sure of. You'd have to test in the program you're using.

Call Macro from Hyperlink using multiple workbooks without re-opening Macro Sheet

I have 2 Workbooks:
Workbook With Macros
Workbook to write to.
I have 2 subs. One is to insert a hyperlink in the 'workbook to write to' and the other Sub will execute when the hyperlink is clicked.
However, my sub to create the Hyperlink is causing me some confusion. I need to reference the worksheet in the Macro Enabled Workbook, but doing that requires a Workbook.Open command. Obviously the Macro Enabled Workbook will already be open so this closes and re-opens it. I've gotten very muddled with this, can someone point me in the right direction?
So Macro Sheet will have the below Sub, the link is to another Sub in the same sheet. "CreateHyperlinks" is called from another method which writes to an external spreadsheet.
Obviously "ActiveSheet" below is wrong. I want to write to a different spreadsheet, so I will need to open it also (I assume)?
Or, can I pass the worksheet that is being written to from it's write method which is calling "CreateHyperlinks" or am I coupling everything too much as it is?
' This is called elsewhere
Sub CreateHpyerlinks(rangeValue, textValue)
Dim fileName As String
Dim wb As Workbook
Dim TheSheet As Worksheet
fileName = "c:\blah\blah.xlsm"
' ** This is the part: How do i reference "TheSheet" without opening the XL?
Set wb = Workbooks.Open(fileName:=fileName)
Set TheSheet = wb.Sheets("MasterCard")
TheSheet.UsedRange.Select
ActiveSheet.Hyperlinks.Add Anchor:=rangeValue, Address:=TheSheet!THISISMYMACROHERE(textValue), SubAddress:="", ScreenTip:="Go to Word Documebnt", TextToDisplay:=textValue
End Sub
UPDATED:
I have updated the Sub, and am hitting Object does not support his method or property
Sub CreateHpyerlinks(rangeValue, textValue)
Dim fileName As String
Dim wb As Workbook
Dim wbWrite As Workbook
Dim TheSheetWithMacros As Worksheet
Dim TheSheetToWriteTo As Worksheet
fileName = "c:\WorkbookToWriteTo.xlsx"
Set wb = Application.Workbooks(ThisWorkbook.Name)
Set TheSheetWithMacros = wb.Worksheets("Sheet1")
Set wbWrite = Workbooks.Open(fileName:=fileName)
Set TheSheetToWriteTo = wbWrite.Worksheets("Sheet1")
' This Line Errors:
TheSheetToWriteTo.Hyperlinks.Add Anchor:=rangeValue, Address:="", SubAddress:=TheSheetWithMacros!Goto80BytePopulationGuide(textValue), ScreenTip:="Call Macro", TextToDisplay:=textValue
wbWrite.Save
wbWrite.Close
End Sub
The line TheSheetToWriteTo.Hyperlinks.Add Anchor:=rangeValue, Address:="", SubAddress:=TheSheetWithMacros!Goto80BytePopulationGuide(textValue), ScreenTip:="Call Macro", TextToDisplay:=textValue is at fault, clearly TheSheetWithMacors!CallMacro doesn't work like I had hoped.
You may be able to copy the Module's code from one workbook to the other workbook. I assume for now that the Worksheet module only contains the macro and nothing else (or that it is OK to copy everything in that module).
So the reason I propose this, is that if you can copy it, then you can point the hyperlink to a local (within the same file) macro -- because we're copying the module to the new file -- which I think you have already been able to do.
This requires you to check the trust center setting to allow access to the VBProject. A prompt will ask you to do that before it can proceed.
Sub writeit()
'copies a specified code module from one workbook, to
' a code module in another workbook
'
'ASSUMPTIONS:
'-the specified destination must already exist
'-the specified destination does not already contain
' any procedures that would conflict naming with the
' copied module procedures
MsgBox "Please make sure to enable access to ""Trust access to the VBA Project Object Model""", vbInformation
Application.CommandBars.ExecuteMso("MacroSecurity")
Dim macrobook As Workbook
Dim otherbook As Workbook
Dim lines As String
Dim destModule As Object 'VBComponent
Dim copyModule As Object 'VBComponent
Set macrobook = ThisWorkbook 'Modify as needed
Set copyModule = macrobook.VBProject.VBComponents("Sheet1") 'Name of your module to copy
Set otherbook = Workbooks(2) 'Modify as needed
Set destModule = otherbook.VBProject.VBComponents("Sheet1") 'name of where it will go
'Appends the lines from CopyModule to DestModule
With destModule.CodeModule
.InsertLines .CountOfLines + 1, copyModule.CodeModule.lines(1, copyModule.CodeModule.CountOfLines)
End With
End Sub

Worksheet.CodeName empty

I am trying to reference newly added Worksheet by it's CodeName property. The problem is that CodeName returns empty string unless run from debugger.
Set tableSheet = Worksheets.Add(After:=Worksheets(Worksheets.Count))
MsgBox tableSheet.CodeName
Even this simple example doesn't work unless I put a break point on MsgBox line.
What is the problem with this?
I was able to duplicate your issue. Some googling revealed this answer:
Sub test()
Dim tablesheet As Excel.Worksheet
Set tablesheet = Worksheets.Add(After:=Worksheets(Worksheets.Count))
MsgBox ThisWorkbook.VBProject.VBComponents(tablesheet.Name).Properties("Codename")
End Sub
I think you have to check Microsoft Visual Basic for Applications Extensibility 5.3 in Tools>References.
I also needed to read codename for new sheet. This solution worked for me:
Go to Trust Center, under Macro settings check "Trust access to VBA project model".
Now just put this three lines before the line where you need code name. It won't work without this. It is a VBA quirk.
On Error Resume Next
Debug.Print ActiveWorkbook.VBProject.VBComponents(Worksheets(ActiveSheet.Name).CodeName).Properties("Codename")
On Error GoTo 0
Now use your code name like this:
strActiveSheetCodeName = ActiveWorkbook.VBProject.VBComponents(Worksheets(ActiveSheet.Name).CodeName).Properties("Codename")
I can confirm this behavior. I have never used CodeName before, I use sometimes Name to reference a sheet.
Sub Test()
Dim tableSheet As New Worksheet
Set tableSheet = Worksheets.Add(After:=Worksheets(Worksheets.Count))
MsgBox tableSheet.Name
End Sub
This gives the name of the sheet in the MsgBox and it is not only readable, you can change the name of the sheet if you want.
I have a similar problem for a new sheet that created by macro (it would have a blank codename unless you open the Macro Editor).
For my case, since I need the code name to insert some macro to the new sheet. So I use the following code, and it works. It seems the codeName would have value, due to my code access Name attribute of 'VBComponents.item', which is codeName attribute for sheet.
Note: I am not sure why, below code would open the VBA Editor automatically.
Dim VBProj As VBIDE.VBProject
Dim VBComp As VBIDE.VBComponent
Set VBProj = ActiveWorkbook.VBProject
Dim i
For i = 1 To VBProj.VBComponents.Count
If VBProj.VBComponents.Item(i).Name = ActiveSheet.CodeName Then
Set VBComp = VBProj.VBComponents.Item(i)
End If
Next