I'm searching for nearly two hours to find a solution on the following. In Excel I have two columns (One Column for Master Records and one Column for Slave Records). Basically, in Combobox1 I want to populate all the Master Records. If a selection is made for MasterRecord A, I want Combobox2 to only show me the SlaveRecords belonging to A and not the other records belonging to other Master Records.
I have the Interop Assembly added and Excel opened (there is a connection already). Your help is much appreciated!
Private Sub Combobox2_Populate()
'Start Excel Script to populate ComboBox2
Dim excel As Application = New Application
Dim w As Workbook = excel.Workbooks.Open(Filename:=databasestatus, [ReadOnly]:=True)
Dim sheet As Worksheet = w.Sheets("AIR_NL_1")
Dim StartRow As Integer
Dim TotalRows As Integer
ComboBox2.Items.Clear()
sheet.UsedRange.AutoFilter(Field:=9, Criteria1:=ComboBox1.SelectedItem, Operator:=XlAutoFilterOperator.xlFilterValues)
TotalRows = sheet.Range("A1").CurrentRegion.Rows.Count
For StartRow = 3 To TotalRows
If XlCellType.xlCellTypeVisible = True Then
ComboBox2.Items.Add(sheet.Range("H:H").Cells(StartRow, 1).Text)
End If
Next
w.Close(SaveChanges:=False)
End Sub
This might help you, or at least give you a basic idea:
Private Function ExcelToDataTable(ByVal fileExcel As String, _
Optional ByVal columnToExtract As String = "*", _
) As System.Data.DataTable
Dim dt As New System.Data.DataTable
Try
Dim MyConnection As System.Data.OleDb.OleDbConnection
Dim MyCommand As OleDbDataAdapter
Dim fileExcelType As String
'Chose the right provider
If IO.Path.GetExtension(fileExcel.ToUpper) = ".XLS" Then
fileExcelType = "Excel 8.0"
MyConnection = _
New System.Data.OleDb.OleDbConnection _
("provider=Microsoft.Jet.OLEDB.4.0;Data Source='" & fileExcel & "';Extended Properties=" & fileExcelType & ";")
Else
fileExcelType = "Excel 12.0"
MyConnection = _
New System.Data.OleDb.OleDbConnection _
("provider=Microsoft.ACE.OLEDB.12.0;Data Source='" & fileExcel & "';Extended Properties=" & fileExcelType & ";")
End If
'Open excel connection
MyConnection.Open()
'Populate DataTable
Dim myTableName = MyConnection.GetSchema("Tables").Rows(0)("TABLE_NAME")
MyCommand = New OleDbDataAdapter(String.Format("SELECT " & columnToExtract & " FROM [{0}] ", myTableName), MyConnection)
MyCommand.TableMappings.Add("Table", columnToExtract)
MyCommand.Fill(dt)
MyConnection.Close()
Catch ex As Exception
Err.Clear()
End Try
Return dt
End Function
As you can see, we have an optional parameter called myWhereStatement.
What does it mean?
You can specify its value when you call the function, otherwise its value will be an empty string
After that we can call ExcelToDataTable inside our Sub in order to populate the ComboBox as shown below:
Private Sub Combobox_Populate()
Dim filePath As String = "your_file_path"
ComboBox1.DataSource = ExcelToDataTable(filePath, "MasterRecord")
End Sub
Now you have your ComboBox1 filled with data, but the ComboBox2 is still empty.
We are going to handle the event of ComboBox1_SelectedValueChanged that means that every time you select an Item from the ComboBox1 it will programmatically fill the ComboBox2 with the propper items as shown below.
Private Sub ComboBox1_SelectedValueChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles ComboBox1.SelectedValueChanged
Dim slaveRecords As System.Data.DataTable = ExcelToDataTable(filePath)
Dim dt As New DataTable
dt.Columns.Add("SlaveRecords")
For i As Integer = 0 To slaveRecords.Rows.Count
If ComboBox1.SelectedItem Is slaveRecords.Rows(i).Item(0) Then
dt.Rows.Add(slaveRecords.Rows(i).Item(1))
End If
Next
ComboBox2.DataSource = dt
End Sub
Remarks
As you can see the first call of ExcelToDataTable has only 2 parameters while the second one has 3 parameters. That's the optional parameter feature!
N.B.
As you can see I'm using alot of _ because of better code formatting. It means that a single statement will continue across multiple lines
If something isn't 100% clear ot you have any dubt feel free to ask in the comments below.
Related
I am trying to read an excel file that has multiple sheets 1,2,3,4,5,6,7,8,9,10
I need to read several columns of what is in those sheets for example from the range a1: a20 and c1: c20
The result is listing it in a listview, I am trying with several suggestions that appear in the forum, but it only allows me to read one sheet and I need to read several at the same time. Anyway I put the code that I am using.
Thanks in advance
Public Class Frm_ImportarLibro
Public Function Obtenerdatos(ByVal ruta As String, ByVal hoja As String, ByVal rango As String) As DataTable
Dim cadenaConexion As String = "Provider=Microsoft.Jet.OLEDB.4.0;Extended Properties='Excel 8.0;HDR=NO';" &
"Data Source=" & ruta
Using cnn As New OleDbConnection(cadenaConexion)
Dim cmd As OleDbCommand = cnn.CreateCommand()
cmd.CommandText = String.Format("SELECT * FROM [{0}${1}]", hoja, rango)
Dim da As New OleDbDataAdapter(cmd)
Dim dtTemp As New DataTable("Prueba")
da.Fill(dtTemp)
Dim dt As DataTable = dtTemp.Clone()
Dim rows As DataRow() = dtTemp.Select()
For index As Integer = 0 To rows.Count - 1
Dim row As DataRow = rows(index)
If (row.Item(0) Is DBNull.Value) Then
Exit For
End If
dt.ImportRow(row)
Next
Return dt
End Using
End Function
I believe this would be a matter of looping through the sheets and loading a DataTable for each sheet which is then added to a DataSet. My code assumes that the same range is used for each sheet. I made the range contiguous because it would require a separate command for each non-contiguous range. I believe it would be easier to ignore the extraneous data in the resulting DataTable.
Private cadenaConexion As String = "Provider=Microsoft.Jet.OLEDB.4.0;Extended Properties='Excel 8.0;HDR=NO';Data Source="
Private rango As String = "A1:C20"
Private Function GetXLSheetNames(path As String) As List(Of String)
Dim SheetNames As New List(Of String)
Dim dt As DataTable
Using cn As New OleDbConnection(cadenaConexion & path)
cn.Open()
dt = cn.GetOleDbSchemaTable(OleDbSchemaGuid.Tables, Nothing)
End Using
For Each row As DataRow In dt.Rows
Dim s = row("Table_Name").ToString
SheetNames.Add(s)
Next
Return SheetNames
End Function
Public Function Obtenerdatos(ByVal ruta As String, ByVal hoja As String, ByVal rango As String) As DataSet
Dim ds As New DataSet
Dim lst = GetXLSheetNames(ruta)
Using cnn As New OleDbConnection(cadenaConexion & ruta),
cmd As New OleDbCommand()
cmd.Connection = cnn
cnn.Open()
For Each s In lst
Dim dt As New DataTable(s)
cmd.CommandText = $"SELECT * FROM [{s}{rango}]"
Using reader = cmd.ExecuteReader
dt.Load(reader)
End Using
ds.Tables.Add(dt)
Next
End Using
Return ds
End Function
I have a data connection in my xlsm file, which is called "DATA".
I created my combo box and input the value from a range.
Now I need to return a result set based on the value from the combo box (drop down list). e.g. if the value in the dropdown list is "CompanyXYZ", then my query from "DATA" needs to be returned but only the data for CompanyXYZ.
The sql equivalent is:
"SELECT * FROM [query] where [column] = combobox
Issue #1
Below is my sheet("DATA"). It has a table returned by the SQL query. One of the columns is Debtor_Name. It has more than 8500 rows but only 90 are unique.
In my other sheet, I have an ActiveX ComboBox that needs to return all the unique values from DATA.Debtor_name column (the 90 unique values).
Sample VBA for issue #1:
Sub Populate_Combobox_Worksheet()
'The Excel workbook and worksheets that contain the data, as well as the range placed on that data
Dim wbBook As Workbook
Dim wsSheet As Worksheet
Dim rnData As Range
'Variant to contain the data to be placed in the combo box.
Dim vaData As Variant
'Initialize the Excel objects
Set wbBook = ThisWorkbook
Set wsSheet = wbBook.Worksheets("DATA")
'Set the range equal to the data, and then (temporarily) copy the unique values of that data to the L column.
With wsSheet
Set rnData = .Range(.Range("D1"), .Range("D10000").End(xlUp))
rnData.AdvancedFilter Action:=xlFilterCopy, _
CopyToRange:=.Range("X1"), _
Unique:=True
'store the unique values in vaData
vaData = .Range(.Range("X2"), .Range("X10000").End(xlUp)).Value
'clean up the contents of the temporary data storage
.Range(.Range("X1"), .Range("X10000").End(xlUp)).ClearContents
End With
'display the unique values in vaData in the combo box already in existence on the worksheet.
With wsSheet.OLEObjects("ComboBox1").Object
.Clear
.List = vaData
.ListIndex = -1
End With
End Sub
Issue #2.
Now the end user will need to select a debtor_name from the combo box, then click on refresh data. This DATA REFRESH will need to only pull the data from SQL where debtor_name = [selected value in combo box]
I asked about for issue #2 because I did not know I had an issue with my combo box (issue #1); however, I can handle that somehow; only need help with issue #2 now.
You can use SQL to populate the ComboBox with unique values.
Option Explicit
Sub Populate_Combobox_Worksheet()
Dim con As ADODB.Connection, rs As ADODB.Recordset, SQL As String
Set con = GetConnection
' query
SQL = " SELECT DISTINCT [Debtor_name] FROM [DATA$]" & _
" WHERE [Debtor_name] IS NOT NULL" & _
" ORDER BY [Debtor_Name]"
Set rs = con.Execute(SQL)
With Sheet2.ComboBox1
.Clear
.List = Application.Transpose(rs.GetRows)
.ListIndex = -1
End With
con.Close
End Sub
Sub RefreshData()
Dim con As ADODB.Connection, rs As ADODB.Recordset, SQL As String
Set con = GetConnection
' query
SQL = " SELECT * FROM [DATA$]" & _
" WHERE [Debtor_name] = '" & Sheet2.ComboBox1.Value & "'"
Set rs = con.Execute(SQL)
Sheet2.Range("A1").CopyFromRecordset rs
con.Close
End Sub
Function GetConnection() As ADODB.Connection
Dim wb As Workbook, sCon As String
Set wb = ThisWorkbook
sCon = "Data Source=" & wb.FullName & "; " & _
"Extended Properties=""Excel 12.0;HDR=Yes;"";"
' connect
Set GetConnection = New ADODB.Connection
With GetConnection
.Provider = "Microsoft.ACE.OLEDB.12.0;"
.ConnectionString = sCon
.Open
End With
End Function
I have been looking for a solution to inserting data into excel using vb6 code and access database. There are many cases where I need to write to an excel spreadsheet multiple times with different records of data. I have an existing workbook that I am trying to open and "save as" when I am complete. I have been able to open the excel workbook, access the sheet I am writing to, and the cells I am writing to, however I can only write to the workbook once and when I leave the scope of the open workbook the connection is closed.
I have a sub routine that creates the workbook object, opens the existing workbook and work sheet, writes to a specified cell number to insert the new data. I have looked at official support pages and it doesn't seem to have what I am looking for at this time.
Am I making this too complicated or is there a solution for this? Please help.
My current code:
Row Arrays
Private oldDataRowArray(3 To 21) As Integer
Private updatedDataRowArray(5 To 2) As Integer
Loop logic
Dim i As Integer
Dim n As Integer
i = 3
n = 5
Do While i <= UBound(oldDataRowArray) And n <= UBound(updatedDataRowArray)
EditExcelSheet txtWorkbookFileName.Text, i, n //sub routine
i = i + 3 //skip number of cells
n = n + 3 //skip number of cells
Loop
Sub Routine to Insert data into Excel
Private Sub EditStakingSheet(ByVal workbook As String, ByVal oldDataRowIndex As Integer, ByVal newDataRowIndex As Integer)
Dim objExcel As Object
Dim objWorkBook As Object
Dim objSheet As Object
Set objExcel = New Excel.Application
Set objWorkBook = objExcel.Workbooks.Open(workbook)
Set objSheet = objWorkBook.Worksheets(1)
objExcel.Visible = True
//insert old value
objSheet.Cells(oldDataRowIndex , 26).Value = "old Value"
//insert new value
objSheet.Cells(newDataRowIndex , 26).Value = "new Value"
End Sub
You could use adodb objects.
This video is a good tutorial for this.
Here is an example how you can use adodb. You need to install the activeX Data Objects Libary for this.
For .source= you can use any sql-query.
Public Function get_Value(table As String, order_by As String) As Variant
Set db_data = New ADODB.Recordset
Set db1 = New ADODB.Connection
pDB_path = "#enter db-path here"
db1.ConnectionString = _
"Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" & pDB_path & ";Persist Security Info=False;"
db1.Open
With db_data
.ActiveConnection = db1
.Source = "SELECT * FROM " & table & " ORDER BY " & order_by & " ASC"
.LockType = adLockReadOnly 'read only access to db
.CursorType = adOpenStatic 'how to update the database
.Open
End With
get_Value = TransposeArray(db_data.GetRows)
db_data.Close
End Function
I have 78 excel columns and I have 5 datagridviews.
How do I make connection?
I understand what you want to achieve, but to get the maximum out of any answer, it would be better if you add some code or some further explanation.
For example how should anyone know which excel data should be displayed in DataGridView one, two etc...
Anyway, i would recommend that you divide the task into two steps:
ReadExcel and DisplayData. In my opinion reading data from excel file via OLEDB is a good way to start. Therefore i recommend reading the following article: http://www.codeproject.com/Tips/705470/Read-and-Write-Excel-Documents-Using-OLEDB
For displaying the data in a DataGridView you need to bind a dataset to it. Maybe you´ll find the following post helpful:
How to bind Dataset to DataGridView in windows application
Its both c# code, but i think getting things running for vb.net is an easy task.
Edit: I found some older vb.net of mine you can use. It´s not that good piece of code but it should get you started. It imports the whole data of an excel sheet. But please don´t just copy and run :)
Public Shared Function ImportExcelSheetData(ByVal ExcelFilePath As String, _
ByVal SourceExcelSheetName As String, _
ByRef pDestDataTable As DataTable, _
ByRef ErrMsg As String, _
Optional ByVal WithHeader As Boolean = False) As Integer
Dim ConnectionString As String = ""
Dim WithHeaderString As String = ""
Dim nOutputRow As Integer = 0
Dim oleExcelCommand As OleDbCommand
Dim oleExcelConnection As OleDbConnection
ImportExcelSheetData = -1 ' Error by default
If System.IO.File.Exists(ExcelFilePath) <> True Then
ErrMsg = "Error: File does not exist." + vbCrLf + "Filepath: " + ExcelFilePath
Exit Function
End If
If WithHeader = True Then
WithHeaderString = "Yes"
Else
WithHeaderString = "No"
End If
ConnectionString = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" + ExcelFilePath + ";Extended Properties=""Excel 12.0;HDR=" + WithHeaderString + ";IMEX=1"""
oleExcelConnection = New OleDbConnection(ConnectionString)
oleExcelConnection.Open()
If IsNothing(pDestDataTable) = True Then
pDestDataTable = New DataTable
End If
' if SourceExcelSheetName is not set, use first sheet!
If SourceExcelSheetName.Trim = "" Then
Dim tmpDataTable As DataTable = oleExcelConnection.GetOleDbSchemaTable(OleDbSchemaGuid.Tables, Nothing)
if IsNothing(tmpDataTable) OR tmpDataTable.Rows.Count < 1 Then
throw new Exception("Error: Could not determine the name of the first worksheet.")
End If
Dim firstSheetName As String = tmpDataTable.Rows(0)("TABLE_NAME").ToString()
If firstSheetName.Trim() <> "" then
SourceExcelSheetName = firstSheetName
End If
End If
If SourceExcelSheetName <> "" Then
Try
Dim oleAdapter As New OleDbDataAdapter()
oleExcelCommand = oleExcelConnection.CreateCommand()
If SourceExcelSheetName.EndsWith ("$") = True Then
oleExcelCommand.CommandText = "Select * From [" & SourceExcelSheetName & "]"
Else
oleExcelCommand.CommandText = "Select * From [" & SourceExcelSheetName & "$]"
End If
oleExcelCommand.CommandType = CommandType.Text
oleAdapter.SelectCommand = oleExcelCommand
oleAdapter.Fill(pDestDataTable)
oleExcelConnection.Close()
Catch ex As Exception
ErrMsg = Err.Description
Exit Function
End Try
End If
ImportExcelSheetData = 0 ' Ok
End Function
I've built a userform that allows modification of a macro-generated string before it becomes part of a new spreadsheet. As written, I have one worry about how resilient it will be.
The form has a single textbox called CourseDescription into which a string value strBundleDescription is dumped:
frmDescriptionReview.CourseDescription = strBundleDescription
frmDescriptionReview.CourseDescription.MultiLine = True
frmDescriptionReview.CourseDescription.WordWrap = True
frmDescriptionReview.Show
The user can then edit the text as needed and press OK to pass the text to the spreadsheet being created.
On clicking OK, the modified string is placed in Range("B7") of the spreadsheet:
Private Sub cmdOK_Click()
Dim strValue As String
strValue = CourseDescription.Value
If strValue <> "" Then
Range("B7").Value = strValue
End If
Unload Me
End Sub
This works so far in practice, but I've had unexplained focus issues before. I am concerned that the focus might in some (unknown) circumstance shift to another open worksheet and the text will be pasted where it does not belong.
My question: Am I right to want a more defined location, or will a simple range definition like the one above be adequate? And if a more defined location is advised, is there a way to pass information like the wkbSaba and shtCourse values without making public variables?
All potential solutions I found involved some form of public variable, but on principle (rightly or wrongly) I'm trying to avoid public variables when information will only be used in one function (as in this case).
Full Code, as requested: This is the the full macro code as it stands. The call for frmDescriptionReview is about 3/4 of the way down under the comment tag "'enter base information for Bundle Description".
I'm going to try the Property call as you suggest, which is something I did not know about, and had not seen when web searching for ways to pass data to a userform. So much to learn! It certainly looks like the variables could be passed that way.
Option Explicit
Sub TransferData()
'***************************************
' TO USE THIS MACRO:
' 1. Make sure that all information for the bundle is included
' on the 'km notification plan' and 'bundle details (kbar)' tabs
' of the Reporting_KMFramework.xlsx
' 2. Select the bundle name on the 'km notification plan' tab.
' 3. Start the macro and it should create the basis of the Saba
' form
' 4. Read through the entire form, especially the bundle
' description, to be sure it is complete and accurate.
'***************************************
'establish variables
Dim iRow As Integer
Dim sTxt As String
Dim sTxt2 As String
Dim sBundleName As String
Dim sNumber As String
Dim aSplit() As String
Dim aSplit2() As String
Dim aBundleSplit() As String
Dim aNumberSplit() As String
Dim wkbFramework As Workbook
Dim wkbSaba As Workbook
Dim shtPlan As Worksheet
Dim shtCourse As Worksheet
Dim vData As Variant
Dim vBundleName As Variant
Dim lLoop As Long
'set initial values for variables
'find current row number
iRow = ActiveCell.Row
'remember locations of current data
Set wkbFramework = ActiveWorkbook
Set shtPlan = ActiveSheet
'Set rngSelect = Range("B" & iRow)
'select bundle name
vBundleName = shtPlan.Range("B" & iRow).Value
vData = vBundleName
sBundleName = shtPlan.Range("B" & iRow).Value
'find and save course names for the bundle
Sheets(2).Select
sTxt = Find_Range(vBundleName, Columns("B"), xlValues).Offset(0, 1).Value 'course names from Detail tab
sTxt2 = Find_Range(vBundleName, Columns("B"), xlValues).Offset(0, 2).Value 'course numbers from Detail tab
'open new Saba Form
Workbooks.Add Template:= _
"C:\Documents and Settings\rookek\Application Data\Microsoft\Templates\Bundle_SabaEntryForm_KM.xltm"
'remember locations of Saba form
Set wkbSaba = ActiveWorkbook
Set shtCourse = ActiveSheet
'move data into new Saba form
'paste bundle name
wkbSaba.Sheets(shtCourse.Name).Range("B5").Value = vData
'Transfer bundle number
vData = wkbFramework.Sheets(shtPlan.Name).Range("E" & iRow).Value
sNumber = vData
Dim aNumber() As String
aNumber = Split(sNumber, "-")
wkbSaba.Sheets(shtCourse.Name).Range("B6").Value = vData
'create names to use in the bundle description and (later) in naming the file
'Establish additional variables
Dim strDate As String
Dim strName1 As String
Dim strName2 As String
Dim strName3 As String
Dim strName4 As String
Dim strName5 As String
Dim aTechSplit() As String
Dim aCourse() As String
Dim iTech As Integer
'Dim iBundle As Integer
Dim iCourse As Integer
vData = wkbFramework.Sheets(shtPlan.Name).Range("L" & iRow).Value
aCourse = Split(sTxt, Chr(10))
iCourse = UBound(aCourse)
aTechSplit = Split(vData, " ")
iTech = UBound(aTechSplit)
aBundleSplit = Split(sBundleName, " ")
aNumberSplit = Split(sNumber, "-")
strName1 = aBundleSplit(0)
strName2 = aBundleSplit(1)
If UBound(aNumberSplit) > 1 Then
strName3 = aNumberSplit(UBound(aNumberSplit) - 1) & aNumberSplit(UBound(aNumberSplit))
End If
strName3 = Right(strName3, Len(strName3) - 1)
strName4 = aTechSplit(0) & " "
strName5 = aCourse(0)
For lLoop = 1 To iTech - 1
strName4 = strName4 & aTechSplit(lLoop) & " "
Next lLoop
If iCourse > 1 Then
For lLoop = 1 To iCourse - 1
strName5 = strName5 & ", " & aCourse(lLoop)
Next lLoop
strName5 = strName5 & ", and " & aCourse(iCourse)
End If
If iCourse = 1 Then
strName5 = strName5 & ", and " & aCourse(iCourse)
End If
strName5 = Replace(strName5, " Technical Differences", "")
strName5 = Replace(strName5, " Overview", "")
strName5 = Replace(strName5, " Technical Presales for ATCs", "")
strName5 = Replace(strName5, " Technical Presales for STCs", "")
strName5 = Replace(strName5, " Technical Presales", "")
'enter base information for Bundle Description
Dim strBundleDescription As String
strBundleDescription = "This Knowledge Maintenance bundle covers recent technology changes that may affect " & strName4 & "environments. Topics covered by this bundle include the enhancements and features introduced with " & strName5 & "."
'wkbSaba.Sheets(shtCourse.Name).Range("B7").Value = strBundleDescription
frmDescriptionReview.CourseDescription = strBundleDescription
frmDescriptionReview.CourseDescription.MultiLine = True
frmDescriptionReview.CourseDescription.WordWrap = True
frmDescriptionReview.Show
'transfer tech and track
wkbSaba.Sheets(shtCourse.Name).Range("B8").Value = vData
'transfer product GA date
vData = wkbFramework.Sheets(shtPlan.Name).Range("G" & iRow).Value
wkbSaba.Sheets(shtCourse.Name).Range("B9").Value = vData
'transfer bundle notification date
vData = wkbFramework.Sheets(shtPlan.Name).Range("D" & iRow).Value
wkbSaba.Sheets(shtCourse.Name).Range("B10").Value = vData
'set audience type
If aNumber(UBound(aNumber)) = "SA" Then
wkbSaba.Sheets(shtCourse.Name).Range("B11").Value = "Internal, Partner, Customer"
Else
wkbSaba.Sheets(shtCourse.Name).Range("B11").Value = "Internal, Partner"
End If
'set Education Manager
frmEducationManagerEntry.EducationManagers.MultiLine = True
frmEducationManagerEntry.EducationManagers.WordWrap = True
frmEducationManagerEntry.Show
'set EPG
wkbSaba.Sheets(shtCourse.Name).Range("B13").Value = "N/A (KM course reuse)"
'set Test information to N/A
wkbSaba.Sheets(shtCourse.Name).Range("A22:B22").Value = "N/A"
'enter course names
aSplit = Split(sTxt, Chr(10)) 'if there is more than one course, this establishes a number and location for each
If UBound(aSplit) > 4 Then
'add rows equal to the difference between ubound and 5
wkbSaba.Sheets(shtCourse.Name).Range("A21", "B" & 21 + (UBound(aSplit) - 5)).Select
Selection.EntireRow.Insert
End If
For lLoop = 0 To UBound(aSplit)
wkbSaba.Sheets(shtCourse.Name).Range("B" & 17 + lLoop).Value = aSplit(lLoop)
Next lLoop
'enter course numbers
aSplit2 = Split(sTxt2, Chr(10)) 'if there is more than one course, this establishes a number and location for each
For lLoop = 0 To UBound(aSplit2)
wkbSaba.Sheets(shtCourse.Name).Range("A" & 17 + lLoop).Value = Trim(aSplit2(lLoop))
Next lLoop
'save and close Saba form
With wkbSaba.Sheets(shtCourse.Name)
Dim SaveAsDialog As FileDialog
strDate = Date
strDate = Replace(strDate, "/", ".")
Set SaveAsDialog = Application.FileDialog(msoFileDialogSaveAs)
With SaveAsDialog
.Title = "Choose a file location and file name for your new Saba form"
.AllowMultiSelect = False
.InitialFileName = strName1 & strName2 & "_SabaEntryForm_" & strName3 & ".xlsx"
'.InitialFileName = sSavelocation & "\" & strName3 & "\" & aBundleSplit(0) & aBundleSplit(1) & "_" & strName3 & "_SabaEntryForm" & ".xlsx"
.Show
.Execute
End With
wkbSaba.Sheets(shtCourse.Name).PrintOut
wkbSaba.Close
End With
' Return focus to Plan sheet
shtPlan.Activate
End Sub
Addition of Property code fails
I tried adding code based on the property link shared in the comments, but running the code results in a Compile error: Method or data member not found. The complete userform code looks like this:
Option Explicit
Private wkbLocation As Workbook
Private shtLocation As Worksheet
Private Sub cmdCancel_Click()
Unload Me
End
End Sub
Private Sub cmdOK_Click()
Dim strValue As String
strValue = CourseDescription.Value
If strValue <> "" Then
wkbLocation.Sheets(shtLocation).Range("B7").Value = strValue
End If
Unload Me
End Sub
Property Let MyProp(wkbSaba As Workbook, shtCourse As Worksheet)
wkbLocation = wkbSaba
shtLocation = shtCourse
End Property
And the call for the userform now looks like this:
'enter base information for Bundle Description
Dim strBundleDescription As String
strBundleDescription = "This Knowledge Maintenance bundle covers recent technology changes that may affect " & strName4 & "environments. Topics covered by this bundle include the enhancements and features introduced with " & strName5 & "."
'wkbSaba.Sheets(shtCourse.Name).Range("B7").Value = strBundleDescription
Dim frmDescriptionReview As UserForm3
Set frmDescriptionReview = New UserForm3
frmDescriptionReview.MyProp = "Pass to form"
frmDescriptionReview.CourseDescription = strBundleDescription
frmDescriptionReview.CourseDescription.MultiLine = True
frmDescriptionReview.CourseDescription.WordWrap = True
frmDescriptionReview.Show
When I run the code, I get a Compile error: Method or data member not found, highlighting .MyProp. Help says this error means I misspelled the object or member name, or specified a collection index that is out of range. I checked the spelling, and MyProp is exactly how I spelled it in both locations. I don't think I'm specifying a collection am I? None are explicitly defined. What am i doing wrong?
I am concerned that the focus might in some (unknown) circumstance
shift to another open worksheet and the text will be pasted where it
does not belong.
Not really sure what you are asking. But you can further define your range variable by using:
Workbooks("Book1.xlsm").Worksheets("Sheet1").Range("B7").Value = strValue
or
Workbooks(wkbSaba).Worksheets(shtCourse).Range("B7").Value = strValue
That will ensure it goes to the right workbook and worksheet. I'm not sure why you think you need public variables?
EDIT:
UserForm Code:
Private wsSheet As Worksheet
Property Let SetWorksheet(wsSheetPass As Worksheet)
Set wsSheet = wsSheetPass
End Property
Private Sub cmdOK_Click()
Dim strValue As String
strValue = CourseDescription.Value
If strValue <> "" Then
wsSheet.Range("B7").Value = strValue
End If
Unload Me
End Sub
Calling Module:
Dim wsSheetToPass As Worksheet
Set wsSheetToPass = Workbooks(wkbSaba).Worksheets(shtCourse)
frmDescriptionReview.SetWorksheet = wsSheetToPass
As Reafidy states, creating a Property for the Userform and passing information to it would clearly be the right answer for passing variables to and from a userform.
Ideally what I want is to have the form very losely coupled with the module, and not touch the spreadsheet at all (so when appropriate I can pass information to the form from other modules, get the information returned, and place it where appropriate for the current module (which could be on an entirely different spreadsheet or in a completely different cell).
I found additional information on passing data with properties on the PeltierTech web site (http://peltiertech.com/Excel/PropertyProcedures.html) that helped me understand what Reafidy was doing so I couls start loosening the coupling between my code and my forms even more (which was my original intent for this question.
Adding the Get property allows the loose coupling I'm looking for, allowing me to both give and receive information without having to pass the spreadsheet data at all. So my call in the module now looks like this:
'review and revise Description Text
Dim DescriptionReview As New frmDescriptionReview
With DescriptionReview
.Description = strBundleDescription
.Show
strBundleDescription = .Description
End With
Unload DescriptionReview
'transfer description text
wkbSaba.Sheets(shtCourse.Name).Range("B7").Value = strBundleDescription
and the code for the UserForm itself becomes much simpler, like this:
Option Explicit
Property Let Description(ByVal TextBeingPassed As String)
Me.CourseDescription.Value = TextBeingPassed
End Property
Property Get Description() As String
Description = Me.CourseDescription.Value
End Property
Private Sub cmdOK_Click()
Me.Hide
End Sub
Private Sub cmdCancel_Click()
Unload Me
End
End Sub