How value from different class/form can be changed without ByRef - vb.net

Let's assume following example below:
When I call Form2 from Form1 and pass _name of Form1 value. When I show Form1's _name = Alex? I didn't change pname in Form2 and constructor doesn't contains ByRef.
Example code:
Public Form1
Public _name as String
Sub New
_name = "John"
Dim bla as New Form2(_name)
'now _name=Alex !!
End Sub
End Class
Public Form2
Property _name2 as String
Sub New(pname as String) 'no ByVal !!
_name2 = pname 'even if would be ByVal no pname changed !
_name2 = "Alex"
End Sub
End Class
Why is that happening?

Use Shared in variable _name:
Class Form1
Public Shared _name As String
Public Sub New()
_name = "John"
End Sub
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
' Message "John"
MessageBox.Show(_name)
Dim bla As New Form2()
bla.Show()
' Message "Alex"
MessageBox.Show(_name)
End Sub
End Class
Public Class Form2
Property _name2 As String
Sub New()
_name2 = "Alex"
Form1._name = _name2
End Sub
Private Sub Form2_Load(sender As Object, e As EventArgs) Handles MyBase.Load
' message Alex
MessageBox.Show(_name2)
' message Alex
MessageBox.Show(Form1._name)
End Sub
End Class
Or
When the modifier is not entered in the signature of a constructor (method), the default is byVal. If you want to change the value of the variable in form2 you must inform byref in the form2 constructor signature:
Sub New (byref _name as String)

To change the underlying value of an argument you must use the ByRef modifer:
Specifies that an argument is passed in such a way that the called procedure can change the value of a variable underlying the argument in the calling code.
This differs slightly from ByVal:
Specifies that an argument is passed in such a way that the called procedure or property cannot change the value of a variable underlying the argument in the calling code.
By not specifying a modifier in VB.NET the compiler by default will use ByVal.
It would be good to note here that although VB.NET uses ByVal by default if not specified, VBA does not and instead by default uses ByRef. Beware of this should you ever port code from one to the other.
Furthermore you are changing name2 when instead you should be changing pname if you want to change the underlying value.
Have a look at the following code based on your example:
Public Class Form1
Private _name As String
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles Me.Load
_name = "John"
Dim bla As New Form2(_name)
bla.Show()
Debug.WriteLine(_name)
End Sub
End Class
Public Class Form2
Private Property _name2 As String
Public Sub New(ByRef pname As String)
InitializeComponent()
_name2 = pname
pname = "Alex"
End Sub
End Class
Before passing the value to Form2 the name is "John":
At this point I am changing the value of pname to "Alex":
Note that although I have changed the value of pname To "Alex", _name2 is still set to "John".
Notice how the value of _name changes to "Alex" because of the change made to pname:
I'm not sure what it is you're trying to achieve here but hopefully this example gives you a better understanding. The alternative way would be to use a shared variable as explained in the other answer.

Related

How to raise an events when global variable changed in vb.net

I've global variables in module level with below COM-interface types.
Imports System.IO
Imports simpackcomslvLib
Imports simpackcompostLib
Module Globals
Public Srv As SpckCOMApp
Public Mdl As IScrModel
Public Post As PostComApp
Public Res As PostComProject
End Module
In another classes some of my procedures change their object values. I'd like to run some of my procedures which made some changes on my tool's GUI when the for example Mdl value is changed.
I tried with below method which made for an integer type parameter but i didnt succeded for my case, i think because of their object(I types belongs to COM-interface.
Public Class myVar
Private mValue As Integer
Public Event VariableChanged(ByVal mvalue As Integer)
Public Property Variable() As Integer
Get
Variable = mValue
End Get
Set(ByVal value As Integer)
mValue = value
RaiseEvent VariableChanged(mValue)
End Set
End Property
End Class
Usage of above code in an example
Public Class Form1
Private WithEvents test As New myVar
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
test.Variable = CInt(TextBox1.Text)
End Sub
Private Sub VariableChanged(ByVal NewValue As Integer) Handles test.VariableChanged
MessageBox.Show(NewValue)
End Sub
End Class
Is there anyway to implement my below variables in a module such a way also using in module level is wrong should i move them under the class?
Module Globals
Public Srv As SpckCOMApp
Public Mdl As IScrModel
Public Post As PostComApp
Public Res As PostComProject
End Module

Using a datagridview to add records

I am using VS2008/.net fw 3.5sp1 and back into the coding life - and a little rusty to say the least :) Any help on the below would be great please.
I need to gather a list of input from a user, that I will work with later (pass to a teradata DWH with some other values). The input list involves two parts, a BSB ID and an Account ID. After some research it looks like the best option would be to create a class for the accounts, a list of accounts and bind that to a datagridview - of which I have done - but it looks like I can't add/edit. I have added a new button/add button to alter the data grid and get an error that I cannot programmatically add.
When I use accountList.AllowNew() = TRUE -- Error -- constructor on type bankaccount not found - but - I thought the constructor is the "new" sub in the class?
When I try accountsBindingSource.IsFixedSize = False it advises the property is read only.
For this - I've cut all the other code out to just this section, which requires one form (frmAccountLoad), with a datagridview dgvAccounts and a button btnNewLine.
Imports System
Imports System.Collections.Generic
Imports System.ComponentModel
Imports System.Data
Imports System.Data.Common
Imports System.Diagnostics
Imports System.Drawing
Imports System.Data.SqlClient
Imports System.Windows.Forms
'--------------------------------------------------------------------
Public Class frmAccountLoad
' This BindingSource binds the list to the DataGridView control.
Private accountsBindingSource As New BindingSource()
Private Sub frmAccountLoad_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
'Create list to hold accounts
Dim accountList As New BindingList(Of BankAccount)
accountList.AllowNew() = True
'accountList.AllowEdit = True
accountsBindingSource.DataSource = accountList
dgvAccounts.DataSource = accountsBindingSource
'dgvAccounts.Columns(0).HeaderText = "BSB"
'dgvAccounts.Columns(1).HeaderText = "Account"
End Sub
Private Sub btnNewLine_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnNewLine.Click
'accountsBindingSource.IsFixedSize = False
accountsBindingSource.AddNew()
End Sub
End Class
'--------------------------------------------------------------------
Public Class BankAccount
'----------------------------------------------------------
'a bank account has both a BSB and an account number
Private m_BSB As String
Private m_Account As String
'----------------------------------------------------------
'Public Property
Public Property BSB() As String
Get
Return m_BSB
End Get
Set(ByVal value As String)
m_BSB = value
End Set
End Property
Public Property Account() As String
Get
Return m_Account
End Get
Set(ByVal value As String)
m_Account = value
End Set
End Property
'----------------------------------------------------------
Public Sub New(ByVal new_Bsb As String, ByVal new_Account As String)
m_BSB = new_Bsb
m_Account = new_Account
End Sub
End Class
To be able to call AddNew on your BindingList the BankAccount should have a parameter-less constructor.
You need to have a public parameter-less constructor if you need some initialization, or just remove any constructor if you don't need initialization, then the default parameter-less constructor will be used.
Public Class BankAccount
Public Property BSB As String
Public Property Account As String
Public Sub New()
'Do initialization here if you need
'Or Remove the constructor if you don't need any initialization.
End Sub
End Class
Also you don't need to set accountList.AllowNew = True. It's enough to use a BindingList(Of T) as DataSource.
Private accountList As BindingList(Of BankAccount)
Private Sub frmAccountLoad_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
accountList = New BindingList(Of BankAccount)
dgvAccounts.DataSource = BS
End Sub
Then you can call accountList.AddNew() wherever you need.

VB.NET Class with no constructor (no new instance can be declared)

I'm making my own message box class (called MessageBoxC, whatever), and like System.Windows.Forms.MessageBox, I want to make my class with no constructors and no possibility to declare a new instance of it.
E.g.:
Public Class MessageBoxC
Public Overloads Sub Show(ByVal message As String)
Me.Message = message
ProcessData() '(*)
Me.ShowDialog()
End Sub
End Class
Public Class Form1
Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
System.Windows.Forms.MessageBox.Show("Hello World!") 'works fine
MessageBoxC.Show("Hello World!") 'works fine
Dim msgBox As New System.Windows.Forms.MessageBox 'and you'll get an error message here (**)
Dim msgBoxC As New MessageBoxC 'no error message
End Sub
End Class
(*) Not important. It just calculates text size (width and height in pixels) to correct form size if needed and the corresponding label gets value of Me.Message property.
(**) This one is what I mean. You cannot make a new instance of a MessageBox class, you'll get following error-message: "Type System.Windows.Forms.MessageBox has no constructors."
Well, my class has also no constructors, but it's possible to declare an instance of it. What's the trick here?
Thanks a lot!
Solved. Thanks to OneFineDay.
Public Class MessageBoxC
Private Sub New()
'Empty
End Sub
Public Overloads Shared Function Show(ByVal message As String) As System.Windows.Forms.DialogResult
Return Show(message, Constants.MyAppName, Constants.messageTitle, MessageBoxCButtons.OK, MessageBoxCIcon.Undefined)
End Function
Public Overloads Shared Function Show(ByVal message As String, _
ByVal caption As String, _
ByVal title As String, _
ByVal buttons As Library.MessageBoxCButtons, _
ByVal icon As Library.MessageBoxCIcon) As System.Windows.Forms.DialogResult
Dim msgBoxC As New CBox(message, caption, title, buttons, icon)
msgBoxC.ShowDialog()
Return msgBoxC.DialogResult
End Function
Private Class CBox
Inherits System.Windows.Forms.Form
Sub New(ByVal message As String, _
ByVal caption As String, _
ByVal title As String, _
ByVal buttons As Library.MessageBoxCButtons, _
ByVal icon As Library.MessageBoxCIcon)
MyBase.New()
InitializeComponent()
Me.Message = message
Me.Text = caption
Me.Title = title
Me.Buttons = buttons
Me.Icon64 = icon
Me.OptimizeMe()
End Sub
End Class
End Class
Public Class Form1
Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
Dim dialogResult As New DialogResult
dialogResult = MessageBoxC.Show("This is a simple message.")
MessageBox.Show(dialogResult.ToString)
End Sub
End Class
If you don't declare any constructors, a default constructor is automatically created (this is a public constructor with no parameters).
To prevent anyone creating an instance of your class, you can create a private constructor, like so:
Public Class MessageBoxC
Private Sub New()
' Prevents anyone creating an instance of this class.
End Sub
End Class
Note that your Show method will need to be declared Shared, otherwise you won't be able to call it. In fact, it would need to be Shared, even with the code you provided.
Here is one way to hide the constructor - mainly because the class in question is not accessible.
Public Class Form1
Private Sub meLoad() Handles Me.Load
'Usage
FooBar.Show("Hi")
End Sub
'...
End Class
Public Class FooBar
Private Sub New()
End Sub
Public Shared Sub Show(message As String)
Dim mbc As New MessageBoxC(message)
mbc.ShowDialog()
End Sub
'MessageBoxC is not exposed outside of Foobar which is the entry point
Private Class MessageBoxC : Inherits Form
'define cTor's as needed
Public Sub New(message As String)
Me.Text = message
End Sub
'define content
End Class
End Class

VB.Net Passing values to another form

I would like to know how to pass a value from form1 to another form's public sub.
The problem is that it says "it is not accesible in this context because it is 'Private'."
I've tried changing Form 1 Private Sub to Public Sub but the same error remains. How should i make it work?
Public Class Form1
Dim test(), text1 As String
Const asd = "abcabc"
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
text1 = Space(LOF(1))
test = Split(text1, asd)
HOST = test(1)
End Sub
And i want to pass HOST = test(1) value to another form
Public Class Form2
Public Sub Check()
'get the value to here
End Sub
You could pass it as a parameter:
Public Sub Check(valueToCheck as String)
'get the value to here
End Sub
Or create a property on form2 to receive it:
private _HostOrSomething As String = ""
Friend Property HostOrSomething As String
Get
Return _HostOrSomething
End Get
Set(ByVal value As String)
_HostOrSomething = value
End Set
In which case, Sub Check could use _HostOrSomething since it is local var. To use these:
HOST = Test(1)
frm2.Check(HOST)
or
HOST = Test(1)
frm2.HostOrSomething = HOST
frm2.Check
You can use global variables to pass data from one from to another
Dim A As New Integer= 10
Here how you declare the global The class can be define anywhere in the application.
Public Class GlobalVariables
Public Shared INTver As Integer
End Class
And how you use global variable to store the answer is here
GlobalVariables.INTver= A
put this lines in your "privet sub" and you can access the variable to any of your form that is in your WINDOWS application.

When can I access a form's members?

At first I tried this:
Public Class frmWait
Public Sub New(ByVal uWindowText As String, ByVal uInfoText As String)
Me.Text = uWindowText
Me.lblInfoText.Text = uInfoText
End Sub
End Class
But when I initiated the form like this
Dim wait As New frmWait("blah", "blah")
wait.Show()
... it told me "NullReference Exception" at this line:
Me.lblInfoText.Text = uInfoText
Settings the form's text worked fine.
Okay, I subspected the label was just not created yet when I tried to set the text.
Then I tried the following. I stored the texts in a variable and wanted to set them when the controls have been created. I thought "Form_Load" was a good event for that:
Public Class frmWait
Private m_sWindowText As String = ""
Private m_sInfoText As String = ""
Public Sub New(ByVal uWindowText As String, ByVal uInfoText As String)
'we can not set the texts right away because lblInfo does not seem to exist yet
m_sWindowText = uWindowText
m_sInfoText = uInfoText
End Sub
Private Sub frmWait_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
Me.Text = m_sWindowText
Me.lblInfoText.Text = m_sInfoText
End Sub
End Class
But it threw the same error.
Ehhhhh, when exactely can I set the control's text?
Did I make any beginner's mistake?
Don't delete what the designer does :) It's trying to help you. When you Create the
Public Sub New(...)
on a form, it will automatically put in :
Public Sub New()
' This call is required by the designer.
InitializeComponent()
' Add any initialization after the InitializeComponent() call.
End Sub
The InitializeComponent() is required to get the form ready for you to work with it.
Looks like you are missing the InitializeComponents call:
Public Sub New(ByVal uWindowText As String, ByVal uInfoText As String)
InitializeComponents()
m_sWindowText = uWindowText
m_sInfoText = uInfoText
End Sub
The method call loads all of the controls that were created using the designer. Without that call, your form is empty.