declaring New Microsoft.Office.Interop.Excel.Application outside of Sub makes the VSTO COM addin to freeze during loading - vsto

I have created an Excel COM add in which opens a windows form from a ribbon button. I want to populate the combobox using data from an external excel spreadsheet as well as populate the textboxes based on the selected name.
The part of the form where this applies
when placing the block of code:
Dim ExcelApp = New Microsoft.Office.Interop.Excel.Application ' this is causing the issue
Dim ClientListBook = ExcelApp.Workbooks.Open("C:\Users\SoftwareDev\Desktop\ASSETS\MasterFile.xlsx")
Dim ClientListSheet = ClientListBook.Sheets(1)
below the Public Class Form1 as seen below:
Imports Microsoft.Office.Interop.Excel
Public Class Form1
Dim ExcelApp = New Microsoft.Office.Interop.Excel.Application ' this is causing the issue
Dim ClientListBook = ExcelApp.Workbooks.Open("C:\Users\SoftwareDev\Desktop\ASSETS\MasterFile.xlsx")
Dim ClientListSheet = ClientListBook.Sheets(1)
Private Sub ComboBox1_SelectedIndexChanged(sender As Object, e As EventArgs) Handles cmbClientName.SelectedIndexChanged
End Sub
it causes the Add in to freeze
But when Placing the same block of code into the Sub Form1_Load and Sub ComboBox1 as seen below, the add in works although it is very slow and i suspect it is an incorrect implementation.
THIS IS THE WORKING FULL CODE FOR THE FORM:
Imports Microsoft.Office.Interop.Excel
Public Class Form1
Private Sub ComboBox1_SelectedIndexChanged(sender As Object, e As EventArgs) Handles cmbClientName.SelectedIndexChanged
Dim ExcelApp = New Microsoft.Office.Interop.Excel.Application ' this is causing the issue
Dim ClientListBook = ExcelApp.Workbooks.Open("C:\Users\SoftwareDev\Desktop\ASSETS\MasterFile.xlsx")
Dim ClientListSheet = ClientListBook.Sheets(1)
Dim iStartedRow As Integer
Dim iTotalRows As Integer
' count the number of rows in the worksheet
iTotalRows = ExcelApp.ActiveWorkbook.Sheets(1).Range("a1").CurrentRegion.Rows.Count
' populates the textboxes using clientList data
For iStartedRow = 2 To iTotalRows
If Me.cmbClientName.Text = ClientListSheet.Cells(iStartedRow, 1).text Then
Me.txtSal.Text = ClientListSheet.Cells(iStartedRow, 4).text
Me.txtInvMan.Text = ClientListSheet.Cells(iStartedRow, 5).text
End If
Next
End Sub
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Dim ExcelApp = New Microsoft.Office.Interop.Excel.Application ' this is causing the issue
Dim ClientListBook = ExcelApp.Workbooks.Open("C:\Users\SoftwareDev\Desktop\ASSETS\MasterFile.xlsx")
Dim ClientListSheet = ClientListBook.Sheets(1)
Dim StartedRow As Integer
Dim TotalRows As Integer
' clear any existing data
Me.cmbClientName.Items.Clear()
' count the number of rows in the worksheet
TotalRows = ExcelApp.ActiveWorkbook.Sheets(1).Range("a1").CurrentRegion.Rows.Count
' create a loop to add data into combobox
For StartedRow = 2 To TotalRows
Me.cmbClientName.Items.Add(ClientListSheet.Cells(StartedRow, 1).Text)
Next
End Sub
End Class
Please would you let me know how i can correctly implement this and if it would be better and faster to use Microsoft Access database instead. I am new to VSTO COM add ins and VB.Net, this is my first project so i understand that my implementation may be suboptimal and will appreciate all feedback.
Thank you

If you declare and initialize these variables on the class level, they are created when the constructor runs.
There is absolutely no reason to initialize them immediately - you can declare them on the class level, but create them only when needed
Moreover, if your code is running in a VSTO addin, there is absolutely no reason to create an instance of the Excel.Application object - it is already available to you.
Hardcoding the file path is obviously also a problem...
Public Class Form1
Dim ExcelAppe
Dim ClientListBook
Dim ClientListSheet
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
ExcelApp = Globals.ThisAddin.Application
ClientListBook = ExcelApp.Workbooks.Open("C:\Users\SoftwareDev\Desktop\ASSETS\MasterFile.xlsx")
ClientListSheet = ClientListBook.Sheets(1)

Related

function that runs when dynamically generated control is interacted with

I've been Having trouble with making a sub that runs when my datagridview cell is doubleclicked. It is caused because the datagridview is programmatically created, rather than created by the designer. I have found a help website i will include that appears to be related to the issue.
Public Class seattemplatecreator
Dim alphabet() As Char = "ABCDEFGHIJKLMNOPQRSTUVWXYZ".ToCharArray()
Private WithEvents dgv_flightTemplate As DataGridView
'help from https://it.toolbox.com/question/event-for-dynamically-created-command-button-043008
Public Sub init(ByVal dgv01 As DataGridView)
dgv_flightTemplate = dgv01
End Sub
Private Sub dgv_flightTemplate_CellMouseDoubleClick(ByVal sender As Object, ByVal e As System.Windows.Forms.DataGridViewCellMouseEventArgs) Handles dgv_flightTemplate.CellMouseDoubleClick
MsgBox("workwd")
End Sub
Private Sub btn_createflight_Click(sender As Object, e As EventArgs) Handles btn_createflight.Click
'used https://social.msdn.microsoft.com/Forums/vstudio/en-US/e222f438-f060-4e61-ab28-523d02db91b2/how-to-programmatically-create-datagridview-with-empty-columns-and-rows?forum=vbgeneral
'to help with this part for automatically generating the datagridview
MsgBox(alphabet(0))
Dim dgv_flightTemplate As New DataGridView
Dim c As Integer = txb_columns.Text
Dim r As Integer = txb_rows.Text
For colcount As Integer = 0 To c - 1
Dim nc As New DataGridViewTextBoxColumn
nc.Name = "Seating Column"
dgv_flightTemplate.Columns.Add(nc)
Next
dgv_flightTemplate.Rows.Add(r)
For x = 0 To r - 1
dgv_flightTemplate.Rows(x).HeaderCell.Value = alphabet(x).ToString
Next
Me.Controls.Add(dgv_flightTemplate)
dgv_flightTemplate.Location = New Point(400, 400)
dgv_flightTemplate.AllowUserToAddRows = False
dgv_flightTemplate.AllowUserToDeleteRows = False
dgv_flightTemplate.RowHeadersWidthSizeMode = DataGridViewRowHeadersWidthSizeMode.AutoSizeToAllHeaders
dgv_flightTemplate.AutoResizeRows()
dgv_flightTemplate.AutoSize = True
End Sub
End Class
https://it.toolbox.com/question/event-for-dynamically-created-command-button-043008
Edit: Olivier Jacot-Descombes response was perfect all that was needed was run the "Init" sub.
Public Class seattemplatecreator
Dim alphabet() As Char = "ABCDEFGHIJKLMNOPQRSTUVWXYZ".ToCharArray()
Private WithEvents dgv_flightTemplate As DataGridView
'help from https://it.toolbox.com/question/event-for-dynamically-created-command-button-043008
Public Sub init(ByVal dgv01 As DataGridView)
dgv_flightTemplate = dgv01
End Sub
Private Sub dgv_flightTemplate_CellMouseDoubleClick(ByVal sender As Object, ByVal e As System.Windows.Forms.DataGridViewCellMouseEventArgs) Handles dgv_flightTemplate.CellMouseDoubleClick
MsgBox("workwd")
End Sub
Private Sub btn_createflight_Click(sender As Object, e As EventArgs) Handles btn_createflight.Click
'used https://social.msdn.microsoft.com/Forums/vstudio/en-US/e222f438-f060-4e61-ab28-523d02db91b2/how-to-programmatically-create-datagridview-with-empty-columns-and-rows?forum=vbgeneral
'to help with this part for automatically generating the datagridview
MsgBox(alphabet(0))
Dim dgv_flightTemplate As New DataGridView
Dim c As Integer = txb_columns.Text
Dim r As Integer = txb_rows.Text
For colcount As Integer = 0 To c - 1
Dim nc As New DataGridViewTextBoxColumn
nc.Name = "Seating Column"
dgv_flightTemplate.Columns.Add(nc)
Next
dgv_flightTemplate.Rows.Add(r)
For x = 0 To r - 1
dgv_flightTemplate.Rows(x).HeaderCell.Value = alphabet(x).ToString
Next
Me.Controls.Add(dgv_flightTemplate)
dgv_flightTemplate.Location = New Point(400, 400)
dgv_flightTemplate.AllowUserToAddRows = False
dgv_flightTemplate.AllowUserToDeleteRows = False
dgv_flightTemplate.RowHeadersWidthSizeMode = DataGridViewRowHeadersWidthSizeMode.AutoSizeToAllHeaders
dgv_flightTemplate.AutoResizeRows()
dgv_flightTemplate.AutoSize = True
init(dgv_flightTemplate)
End Sub
End Class
Any help would be greatly appreciated
Thanks, Taine
You are not calling Init. But instead, you could as well directly assign to the field
dgv_flightTemplate = New DataGridView 'Note: No Dim here.
But even if you are creating the columns dynamically, you could add a grid with the designer. Just call
dgv_flightTemplate.Columns.Clear()
before adding columns.

Enumerate all forms in VB.NET project, then show them by Name or Fullname?

How do I create a reference to a form, simply by its Name or .Fullname..? It seems so simple, but nothing I've tried will work.
Given the following code, the part at the end is where I'm stuck.
Thanks.
Public Class frmLauncher
Private Sub FormPicker_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Dim myAssembly As System.Reflection.Assembly = System.Reflection.Assembly.GetExecutingAssembly()
Dim myTypes() As Type = Nothing
' Both of the following lines seem to do the same thing.
' Is one way better or safer than the other?
myTypes = myAssembly.DefinedTypes
myTypes = myAssembly.GetTypes
For Each t In myTypes
If t.BaseType.FullName.ToString.ToUpper = "System.Windows.Forms.Form".ToUpper Then
ListBox1.Items.Add(t.Name)
End If
Next
End Sub
Private Sub ListBox1_DoubleClick(sender As Object, e As EventArgs) Handles ListBox1.DoubleClick
Dim frmName As String = ListBox1.Text
' the next line is where I'm totally stuck.
Dim frm As Form = GetSomethingUnknown(frmName)
frm.Show()
End Sub
End Class
To create an instance of your forms first add to your listbox the FullName property, this includes also the namespace of your own application and it is required to find the form classes via reflection
For Each t In myTypes
If t.BaseType.FullName.ToString.ToUpper = "System.Windows.Forms.Form".ToUpper Then
ListBox1.Items.Add(t.FullName)
End If
Next
Now the code required to create the instance is the following
Private Sub ListBox1_DoubleClick(sender As Object, e As EventArgs) Handles ListBox1.DoubleClick
Dim frmName As String = ListBox1.Text
Dim myAssembly As System.Reflection.Assembly = System.Reflection.Assembly.GetExecutingAssembly()
Dim obj = myAssembly.GetType(frmName).InvokeMember(Nothing, Reflection.BindingFlags.CreateInstance, Nothing, Nothing, Nothing)
Dim frm As Form = CType(obj, System.Windows.Forms.Form)
frm.Show()
End Sub
As you can see, the crucial point here is the call to InvokeMember method from the type identified by your frmName variable. This is a complex method that you should study carefully if you want really work with reflection code.

how to export all items in listbox to excel in vb

i'm using vb windows form.
I'm trying to export all items in a listbox1 to excel file using a button, but the problem that it export only the first item
i want to export all the listbox1 items
here is my code
Imports Microsoft.Office.Interop
Public Class Form1
Dim MsExcel As Excel.Application
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
MsExcel = CreateObject("Excel.Application")
MsExcel.Workbooks.Add()
MsExcel.Range("A1").Value = ListBox1.Items
MsExcel.Visible = True
End Sub
End Class
You'll need to loop through the item and increment the row you print in :
Imports Microsoft.Office.Interop
Public Class Form1
Dim oItem As Object
Dim OffS As Integer
Dim MsExcel As Excel.Application
Dim Wb As Excel.Workbook
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
MsExcel = CreateObject("Excel.Application")
Set Wb = MsExcel.Workbooks.Open("Path_of_File")
OffS = 0
For Each oItem In ListBox1.Items
Wb.Sheets(1).Range("A1").Offset(OffS, 0).Value = oItem
OffS = OffS + 1
Next oItem
Wb.SaveAs
DoEvents
Wb.Close
MsExcel.Visible = True
End Sub
End Class

Excel 2010 Add-In having issues with saving picture clipboard to file

I'm trying to make an add in in vs2010 for Excel 2010 that copies an image to clipboard and then saves it.
The add in has a button which runs the following code:
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As Microsoft.Office.Tools.Ribbon.RibbonControlEventArgs) Handles Button1.Click
Dim app As Excel.Application = Globals.ThisAddIn.Application
Dim desktopFolder As String = Environment.GetFolderPath(Environment.SpecialFolder.Desktop)
Dim fileName As String = "D:\testaddin\test.jpg"
Dim activeWorksheet As Excel.Worksheet = Globals.ThisAddIn.Application.ActiveWorkbook.ActiveSheet
'Dim currentCell As Excel.Range = Globals.ThisAddIn.Application.Selection
Dim pic As Excel.Shape = activeWorksheet.Shapes.Item("Picture 1")
pic.CopyPicture()
If Not System.Windows.Forms.Clipboard.GetDataObject() Is Nothing Then
Dim oDataObj As IDataObject = System.Windows.Forms.Clipboard.GetDataObject()
If oDataObj.GetDataPresent(System.Windows.Forms.DataFormats.Bitmap) Then
Dim oImgObj As System.Drawing.Image = oDataObj.GetData(DataFormats.Bitmap, True)
oImgObj.Save("d:\Test.jpeg", System.Drawing.Imaging.ImageFormat.Jpeg)
End If
End If
End Sub
The code compiles just fine but when I press the button nothing happens.
One thing i know for sure is that the code DOES copy the picture to the clipboard as i checked that in mspaint.
How can I make the clipboard to be saved to jpeg?

Excel Automation in WebBrowser Control

I am using the following code to display an Excel Spreadsheet in a WebBrowser Control in VB.net 2010 Express.
Option Explicit On
Imports Microsoft.Office.Interop
Imports Microsoft.Office.Interop.Excel
Imports Microsoft.Office.Core
Imports System.Data
Public Class CustomerService
Dim oDocument As Object
Dim oXL As Excel.Application
Dim oWB As Excel.Workbook
Dim oSheet As Excel.Worksheet
Dim oRng As Excel.Range
Private Sub CustomerService_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
WebBrowser1.Navigate(quoteFile)
End Sub
Private Sub WebBrowser1_NavigateComplete2(ByVal sender As Object, ByVal e As AxSHDocVw.DWebBrowserEvents2_NavigateComplete2Event) Handles WebBrowser1.NavigateComplete2
On Error Resume Next
Dim oXL As New Excel.Application
oDocument = e.pDisp.Document
With oDocument.Application.CommandBars("Standard")
.Position = 4 '[msoBarFloating]
.Visible = True
End With
MsgBox("File opened by: " & oDocument.Application.Name)
oDocument.Range("A1") = "HEllo"
End Sub
End Class
My question is, how can I reference the opened document in the WebBrowser1_NavigateComplete2 method so I can automate excel to say display text in a specific cell.
Tried:
Option Explicit On
Imports Microsoft.Office.Interop
Imports Microsoft.Office.Interop.Excel
Imports Microsoft.Office.Core
Imports System.Data
Public Class CustomerService
Dim oDocument As Object
Dim oXL As Excel.Application
Dim oWB As Excel.Workbook
Dim oSheet As Excel.Worksheet
Dim oRng As Excel.Range
Private Sub CustomerService_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
WebBrowser1.Navigate(quoteFile)
End Sub
Private Sub WebBrowser1_NavigateComplete2(ByVal sender As Object, ByVal e As AxSHDocVw.DWebBrowserEvents2_NavigateComplete2Event) Handles WebBrowser1.NavigateComplete2
On Error Resume Next
Dim oXL As New Excel.Application
oDocument = e.pDisp.Document
objApp = oDocument
objBooks = objApp.Workbooks
objBook = objBooks.Add
objSheets = objBook.Worksheets
objSheet = objSheets(1)
oXL = oDocument(.Document) 'with or without .Document
With oDocument.Application.CommandBars("Standard")
.Position = 4 '[msoBarFloating]
.Visible = True
End With
MsgBox("File opened by: " & oDocument.Application.Name)
objSheet.Range("A1") = "HEllo"
End Sub
End Class
With no luck. If you even have an inkling of a way to take this, I am all ears.
Thanks
Does changing the last line to include the sheet solve the problem?
oDocument.worksheets(1).Range("A1") = "HEllo"
Are you getting an error or is it just not doing anything? Do you get your msgbox?