calling a function from a form into a module - vb.net

I have a an array in my module, so I want to display the contents of my array in a form textbox, here is my array
Module Module1
Sub AddCourse()
Dim Subjects() = {"Ms Office 2007", "internet and commmunications", "Lifetime skills"}
For i = 0 To UBound(Subjects) ' FOR LOOP TO WRITE AN ARRAY
i = i +1
Subjects(i)
Next
txtComputer.Text = subjects()
my problem is, when I try to use my texbox txtComputer in my module I get an error.
My question is, how do I make a form textbox to be used in a module
I get an error that reads "Error'txtComputer' is not declared. It may be inaccessible due to its protection level."
My question is based on, how do I get this error fixed?

There are several suggestions I have for you.
First, don't use UBound. That's an old VB6 function that is only provided for backwards compatability. You should instead use Subjects.Length.
Next, when you're incrementing the i variable, you don't need to say i = i + 1. You can just use the += operator for that (e.g. i += 1).
However, you shouldn't be explicitly incrementing i inside your For loop, anyway. The loop automatically increments the variable for you each time it iterates through the loop. If you do it explicitly yourself inside the loop, like that, it will skip every other item.
Next, in this case, you really should just use a For Each loop, rather than an iterator:
For Each subject As String in Subjects
'...
Next
Next, you aren't actually concatenating the items together inside you loop. You should be doing something like this:
For Each subject As String in Subjects
txtComputer.Text += subject
Next
However, in that case, for efficiency sake, you really ought to use a StringBuilder, like this:
Dim builder As New StringBuilder()
For Each subject As String in Subjects
builder.Append(subject)
Next
txtComputer.Text = builder.ToString()
But, all of this is moot because all you really need to do is to call String.Join:
txtComputer.Text = String.Join(", ", Subject)
As far as why you can't access the text box from the module, that is because the module is a separate object, so the text box is entirely out of scope. For instance, what if you had two instances of your form displayed at the same time? How in the world would this module know which form's text box you were referring to? The simplest way to correct that would be to pass a reference to your form into the module's method, like this:
Module Module1
Sub AddCourse(f As MyFormName)
f.txtComputer.Text = "Hello world"
End Sub
End Module
And then you could call it from the form, like this:
AddCourse(Me)
However, that would be exceptionally bad practice. Ideally, nothing outside of the form's code should ever deal directly with any of the controls on the form. So, the far better way to do it would be to simply have the method return the data, and then have the form set it's own control to the data that is returned, for instance:
Module Module1
Function GetCourse() As String
Return "Hello world"
End Function
End Module
And then call it from the form like this:
txtComputer.Text = GetCourse()

You can use String.Join to create a string which separates each subject with Environment.NewLine:
txtComputer.Text = String.Join(Environment.NewLine, Subjects)
The problem with your for loop is that it makes no sense at all since you have already declared and initialized the array in one line.
If you want to use a loop anyway, you can use a StringBuilder to concat all strings together:
Dim subjectBuilder = New System.Text.StringBuilder
For Each subject In Subjects
subjectBuilder.Append(subject).Append(Environment.NewLine)
Next
If subjectBuilder.Length <> 0 Then subjectBuilder.Length -= Environment.NewLine.Length
txtComputer.Text = subjectBuilder.ToString()

Related

Loading My.Settings in other solution

I need to load My.settings from the first Solution into the Second.
In the first project I have a helper class for getting the Settings.
Public Class format
Public NotInheritable Class Helper
Private Sub New()
End Sub
Public Shared Function getAppSetting() As String
Dim returnValue As Object = My.Settings.format
If returnValue Is Nothing Then
Return String.Empty
Else
Return returnValue.ToString
End If
End Function
Public Shared Function getAppSettingTheme() As String
Dim returnValue_theme As Object = My.Settings.theme_selected
If returnValue_theme Is Nothing Then
Return String.Empty
Else
Return returnValue_theme.ToString
End If
End Function
End Class
End Class
This class I have implemented in the other Solutions (Even addiction I have a set)
In other Solutions I have the code for getting the Settings.
Imports MyProject.format.Helper
MsgBox(getAppSettingTheme())
But in the MsgBox I see Default Settings (Unchanged) While in the first solution displays the user-changed settings.
My.Settings I have saved [My.settings.save]
If you choose to do this in a complicated way,what you have to do is,make classes in the first project to read My.Settings,then reference the project in the other project and use it...
However,i'll rather describe an easier way(tho the above one is not that hard) to achieve your goal.
Firstly,i believe that you are using My.Settings to store some data,right ?If so,you can simply save the data to a text file and read it from anywhere you want.This will save plenty of your code and make it tidy :)
As you seem to be a beginner,i would try to keep it as simple as possible and explain in the simplest words...
Now,let's assume,you have 4 My.Settings entries as follows :
UserName
Password
Phone
Age
Instead of writing this data in My.Settings,write it to a text file.Let's assume a user inputs relevant data and it looks somewhat like this :
Zack Rayan
1212121
+090990809809
20
To write a text file, you can simply use File.WriteAllText but as we see that here,our data is in multiline,we can make use of the following :
FIle.WriteAllLines
File.AppendText
Well,let's use File.AppendText method(you can use the below code with WriteAllText as well :)) :
File.AppendText("C:\test.mycustomExtension" , "Zack Rayan" + Environment.NewLine + "1212121" + Environment.NewLine + "+090990809809" + Environment.NewLine + "20")
This will write a text file for you containing the given data in separate lines as well as you will have your own custom extension for the file :)
Now,how to use it ?
Simple :
Dim ReadFile as New List(Of String)(File.ReadAllLines("C:\Test.mycustomextension"))
Now,let's assume,when you were using My.Settings, you used some codes like this :
If My.Settings.UserName = "Zack" Then
......
Scroll up and look closely.UserName was your first entry in My.Settings and when we wrote the text file,ZACK RAYAN-the assumed username was also written in the first line
A few lines above, i created a list which reads the text file.It needs no saying that it will read(and store) the first line first and then move on gradually. So,where you used :
If My.Settings.UserName = "Zack" Then
u should now use :
If ReadFile(0) = "Zack" Then
I hope this helps to enrich your knowledge :)

How do I select specific variables based on checkbox state as I iterate through a For Each

I'm working on a project that requires I iterate through a list of controls on a tabpage to find all of the checkboxes. Then depending on the state of the box (checked or unchecked) select individual variables (filenames) to then perform either a batch rename or delete of files on the filesystem (cb.checked = perform action).
I have managed to create the "for each" for the iteration of the controls (thanks google) but I'm struggling to figure out how to pick the variables. They are all named differently, obviously, as are the checkboxes. Also the checkboxes are statically assigned to the form/tabpage. Here's what I have at the moment.
Public Sub delBut_code(ByRef fname As String)
If (Sanity = 1) Then
For Each cb As Control In Form1.Controls
If TypeOf cb Is CheckBox AndAlso DirectCast(cb,
CheckBox).Checked Then
If My.Computer.FileSystem.FileExists(fname) Then
My.Computer.FileSystem.DeleteFile(fname)
End If
End If
Next
MessageBox.Show("All Actions Completed Successfully")
Else
MessageBox.Show("Please select a File To Delete")
End If
End Sub
and here is an example of some of the variables:
Dim castle As String = selPath & "\zm_castle_loadingmovie.txt"
Dim factory As String = selPath &
"\zm_factory_load_factoryloadingmovie.txt"
Dim island As String = selPath & "\zm_island_loadingmovie.txt"
N.B selpath collects a user entered folder path and can be ignored here
I would really appreciate any pointers.
First, you can do much better with the loop:
Public Sub delBut_code(ByRef fname As String)
If Sanity <> 1 Then
MessageBox.Show("Please select a File To Delete")
Exit Sub
End If
Dim checked = Form1.Controls.OfType(Of CheckBox)().Where(Function(c) c.Checked)
For Each box As CheckBox in checked
Try
'A file not existing is only one reason among many this could fail,
' so it needs to be in a Try/Catch block.
' And once you're using a Try/Catch block anyway,
' the FileExists() check becomes a slow and unnecessary extra trip to the disk.
My.Computer.FileSystem.DeleteFile(fname)
Catch
'Do something here to let the user know it failed for this file
End Try
Next
MessageBox.Show("All Actions Completed")
End Sub
But now you need to know how have the right value in that fname variable. There's not enough information in the question for us to fully answer this, but we can give some suggestions. There a number of ways you could do this:
Set the Tag property in the Checkboxes when you build the string variables. Then fname becomes DirectCast(box.Tag, String).
Inherit a custom control from CheckBox to use instead of a normal Checkbox that has an additional String property for the file name. Set this property when you build the string variables.
Name your string variables in a way that you can derive the string variable name from the CheckBox variable name, and then use a Switch to pick the right string variable from each box.Name.
Keep a Dictionary(Of CheckBox, String) that maps the Checkboxes to the right string values.
But without knowing more context of the application, I hesitate to recommend any of these over the others as best for your situation.

Pass value from form to button text vb.net

I am learning vb.net and I'm having issues searching for what I need. I want to create a button that is "re-usable" throughout my application without needing to write code for each instance. So, what I would like to start with is take a variable in a form, example, public integer value and when this value changes I want to write to the text of a button. I know I can easily do this by writing code in the form btn_xxx.text = variable, but what if I have several buttons and each button looks at the same variable? Currently what I do is create a component which inherits a button and have a timer that on tick will look at the variable and write to the text. I'm sure there is a better way. Can anyone point me in the right direction? I know part of my problem is I don't know the nomenclature on what things are called, so hopefully I asked my question without too much confusion.
I saw this, https://www.daniweb.com/programming/software-development/threads/124842/detect-variable-change, but I don't see how to adapt that to my situation.
Here is what I have:
Private WithEvents Active_Alarm As New Nav_Active_Alarm
Then inside of a sub that calculates the count:
Active_Alarm.Count = CInt(dt_Active_Alarms.Rows.Count)
The user control:
Public Class Nav_Active_Alarm
Private mActive_Alarm_Count As Integer
Public Event Active_Alarm_Count_Changed(ByVal mvalue As Integer)
Public Property Count() As Integer
Get
Count = mActive_Alarm_Count
End Get
Set(ByVal value As Integer)
mActive_Alarm_Count = value
If Not Me.DesignMode Then
RaiseEvent Active_Alarm_Count_Changed(mActive_Alarm_Count)
test()
End If
End Set
End Property
Private Sub test()
If Not Me.DesignMode Then
If mActive_Alarm_Count = 0 Then
Me.btn_Goto_Active_Alarm.Image = My.Resources.Alarm_Clock_Static
'console or msgbox will work but updating the image will not
Else
Me.btn_Goto_Active_Alarm.Image = My.Resources.Alarm_Clock_Animation
'console or msgbox will work but updating the image will not
End If
End If
End Sub
End Class
If I write to console or add a msgbox I will see the event working. But, the image will not change. If I call the test sub from a timer it will work. Why won't the button update (by the way, I did try refresh and update in the code)?
Observer pattern is what you probably looking for.
This is quick and dirty.
Create a class to hold the variable value. Add a method that adds a button instance to a list.
Then a button that needs to know about the variable calls the register method.
When the value of the variable changes, it iterates through the list of buttons and sets the Text property of each one.
You might have jumped in a bit too deep too quick here. Google Custom data binding in .net, there's loads of built in stuff you can use. Though do it yourself is a good exercise.
A simple method to do this might be:
Create a form level list to hold the buttons you are interested in
Add the buttons you are interested in, into the list (maybe in form load or some other place where you have initialization code)
Create a private property in your form with a backing variable to hold the value you want to have applied to the buttons. In the setter portion spin through the list and set each buttons text.
Dim lstButtons As New List(Of Button)
Sub SetupButtons()
'call from form load or other init code
lstButtons.Add(btnPopulate)
lstButtons.Add(btnPopulate)
End Sub
Private _buttonText As String
Private Property ButtonText As String
Get
Return _buttonText
End Get
Set(value As String)
_buttonText = value
For Each b As Button In lstButtons
b.Text = value
Next
End Set
End Property
When you set the property - which now acts as your variable - it will update all of your textboxes for you.
I realize you mentioned without having to write code - but something has to tie things together. Even if you used the observer pattern (which is an elegant solution for this - so props to those who suggested it) you'd probably end up creating a class to hold the property and have that class implement the INotifyPropertyChanged from System.ComponentModel, and then you'd also have to have each button have a databinding for its text property to the property in the object of your class. There isn't really a way (that I can think of) to get around having to write some code for each form you do this in (though the class part you'd only have to write once of course).

Alternatives to using a Collection class

I have been looking through old code to get familiar with the system I use and found a piece of code that I feel can be used better.
What goes on here is some data gets added to the collection(around 150 string variables, some with two variables(variableName/VariableValue), most with only one(VariableName)). It will try to set a module level string variable to the item of the collection passing it the index(variableName) then if there's a value setting the VariableVAlue to the module level variable.
What I feel needs work is that if the collection is passed a variable and the variable doesn't have a value it will return a "" which would cause a runtime error hence there's a On Error GoTo Handler code to manually add a "" to the collection. I feel there's a better way to do this rather than knowing there will be a runtime issue then solving it after catching it. Would there be a way to have a return "" not throw an exception or would the use of an Array also work here since it's a "collection" as well?
Here's an example to try to help visualize:
Public Function GetCollectionVariable(ByVal varName as string) as String
If collection1 Is Nothing Then
m_collection1 = New Collection
End If
On Error GoTo Handler
GetCollectionVariable = collection1.Item(VarName)
exit function
Handler:
collection1.add("", VarName)
GetCollectionVariable = ""
End FUnction
Thanks for your time!!
If Collection1 is a dictionary, you can use TryGetValue.

Hiding/replacing code in VBA

I have a large chunk of code that manually defines each element of an array for me that is annoyingly long and positioned at the beginning of one of my functions. I would like to hide the code or set it somewhere else WITHOUT changing its current meaning in any way if possible. I would like to avoid making the array global. It's also not reasonable to pass the array from all the places that the function is called.
Is there some way to simply have the code sit somewhere else while VBA sees it as being a part of the function, i.e. as if I had all the elements defined at the beginning of the function? I imagine having some sort of "Sub" that's not actually a Sub (I might call it an "Excerpt") of code with the elements populated there with a single line in the function that calls the "Excerpt" by name.
You can return an array from a function so it could be in a module on its own;
public Function getArr() as string()
Dim arr(10) as string
...
arr(5) = "Cakey"
getArr = arr
End Function
Called with
Dim arry() as string: arry = getArr()
msgbox arry(5)