Call a tabPage from a separate window form - vb.net

I have literally just started programming VB today; so forgive my incompetence.
I currently have two window forms, one to navigate though different pages via TabControl (Which is called BusinessSalesPage.vb) and the other is separate (BusinessQuestion.vb). The second window form opens when a button is pressed on BusinessSalesPage.vb.
When the second window opens it has two buttons, I would like the user to be able to click one button which then takes them to the third tab from the TabControl that is on the first window (BusinessSalesPage.vb). Thanks in advance.
Here is my code:
Public Class BusinessQuestion
Inherits System.Windows.Forms.Form
Friend WithEvents mainMenu As System.Windows.Forms.TabControl
Friend WithEvents TabPage3 As System.Windows.Forms.TabPage
Private Sub yesButn_Click(sender As Object, e As EventArgs) Handles yesButn.Click
mainMenu.SelectedTab = TabPage3()
End Sub
End Class
Here is the error I am getting:
NullReferenceException was handled - Object reference not set to an instance of an object.
Edit
Public Class BusinessSalesPage
Inherits System.Windows.Forms.Form
Public Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
mainMenu.SelectedTab = TabPage2
BusinessQuestion.Show()
End Sub
End Class

#LarsTech I tried this as well but mine is having issues with the code. On First Form when I type the mainMenu.SelectedTab = TabPage 1 I get error: "SelectedTab is not a member of System.Windows.Forms.MainMenu and TabPage1 is not declared. It may be inaccessible due to its protection level. On Second Form it doen not find InitializeControls, only InitializeComponents. I am in VS 2010 and here is my code:
First Form:
Public Class WCC
Inherits System.Windows.Forms.Form
Public Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
mainMenu.SelectedTab = TabPage1
Dim MSO As New MSO2014CC(mainMenu:=, TabPage1:=)
MSO.ShowDialog()
End Sub
End Class
Second Form:
Public Class MSO2014CC
Inherits System.Windows.Forms.Form
Friend WithEvents mainMenu As System.Windows.Forms.TabControl
Public Sub New(mainMenu As TabControl, tabPage1 As TabPage)
InitializeComponent()
Me.mainMenu = mainMenu
Me.TabPage1 = tabPage1
End Sub
End Class

You need a reference to the actual instance of the mainForm. You currently don't.
You can try passing a reference in the constructor:
Public Class BusinessQuestion
' code...
Public Sub New(mainMenu As TabControl, tabPage3 As TabPage)
InitializeControls()
Me.mainMenu = mainMenu
Me.TabPage3 = tabPage3
End Sub
' code...
End Class
Your BusinessSalesPage should probably look something like this:
Public Class BusinessSalesPage
Inherits System.Windows.Forms.Form
Public Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
mainMenu.SelectedTab = TabPage2
Dim bq As New BusinessQuestion(mainMenu, TabPage3)
bq.ShowDialog()
End Sub
End Class

Related

PropertyChanged subroutine is not called/triggered

Windows Forms Application in VS2012
Target .NET Framework: 4.5
Created a project containing a Form (Form1) and a class (Class1). Form has a button (Button1) object. Code below:
Class1:
Imports System.ComponentModel
Public Class Class1
Implements INotifyPropertyChanged
Private _PropValue As String
Public Property PropValue As String
Get
Return _PropValue
End Get
Set(value As String)
_PropValue = value
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs("PropValue"))
MsgBox("Event Raised") ' FOR TESTING
End Set
End Property
Public Event PropertyChanged(sender As Object, e As PropertyChangedEventArgs) Implements INotifyPropertyChanged.PropertyChanged
End Class
Form1:
Imports System.ComponentModel
Public Class Form1
Private WithEvents _Class1 As Class1
Private Sub _Class1_PropertyChanged(sender As Object, e As PropertyChangedEventArgs) Handles _Class1.PropertyChanged
MsgBox("Property Changed subroutine") ' FOR TESTING
End Sub
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim tmpObj As Class1 = New Class1
tmpObj.PropValue = "Value-1"
tmpObj.PropValue = "Value-2"
End Sub
End Class
When executing the application and clicking on Button1, received 2 popup messages "Event Raised" from MsgBox in Class1. I'm trying to get the message "Property Changed subroutine" from _Class1_PropertyChanged subroutine. Haven't been successful so far.
You're not changing the property value of the object whose event you're handling. You're handling the event of the object you assign to the _Class1 field but you're changing the property value of a different object assigned to the tmpObj local variable. Get rid of that local variable and use the field only.

Handle a Form Controls Events from a Class

The next code works for my, but I d'ont know if it's the best way to do it.
Of this way I need to write: _Button1 = Button1 and _MyVar = MyVar
This way of doing it seems repetitive and long when the parameters
passed to the Class Constructor are many more.
Public Class Form1
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles Me.Load
Dim myVar As String = "My children"
Dim NewClass1 As New Class1(Button1, myVar)
'... do more ...
End Sub
End Class
Public Class Class1
Private WithEvents _Button1 As Button
Private _MyVar As String
Public Sub New(ByVal Button1 As Button, ByVal MyVar As String)
_Button1 = Button1
_MyVar = MyVar
'... do more ...
End Sub
Private Sub _Button1_Click(sender As Object, e As EventArgs) _
Handles _Button1.Click
MsgBox("Button1 clicked and I love: " & _MyVar)
End Sub
End Class
_Button1 = Button1 and _MyVar = MyVar This way of doing it seems repetitive and long when the parameters passed to the Class Constructor are many more.
This is actually dependency injection and is usually a good thing. If you end up with too many constructor parameters then it should be a clue that your class is doing too many things and is in violation of SOLID principles.
However, your Class1 should not have dependencies on Form1, it should be the other way around. Your code could much more simply be:
Public Class Class1
Friend Sub DoSomething(ByVal MyVar As String)
MsgBox(MyVar)
End Sub
End Class
Public Class Form1
Public Property class1() As Class1
Sub New()
InitializeComponent()
Me.class1 = New Class1()
End Sub
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
class1.DoSomething("blah, blah")
End Sub
End Class
While I am not demonstrating dependency injection here (for simplicity), you can see Class1 only knows that it can do some work, it doesn't know about the Form at all.
You could subscribe to the button click event in Class1 instead of passing the button into it. It would be cleaner, but still bad design.

How to check which form is used to access current form?

I have 3 forms, namely Form1, Form2 & Form3.
Both Form1 and Form2 are able to access to Form3. However, I am going to give different function to a button in Form3 depends which form is used to access Form3.
Is anyone free to explain it to me how to code should work? Also, if you guys have link that this question previously answered or better concept, I will highly appreciate it. Thanks in advance.
My rough idea:
Public Class Form3
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
If 'user are access from Form1 Then
'Action if user are access from Form1 here
Else
'Action if user are access from Form2 here
End If
End Sub
End Class
The problem with this code
If Me.Owner.Equals(Form1) Then . . .
this way you have tightly coupled objects - Form2 and 1 knows Form3 and Form3 knows 2 and 1. It may be not a problem for you or for now. But in OOP it is a problem, and in the future it may be a problem for your object scalability. Here is OOP approach. Writing from my head, so there could be syntactically incorrect items:
Public Interface IFormCanDoSomething
Sub DoSomething()
ReadOnly Property FormAction As EnumFormActions
End Interface
Public Class Form1
Implements IFormCanDoSomething
ReadOnly Property FormAction As EnumFormActions Implements IFormCanDoSomething.FormAction
Get
Return EnumFormActions.Action1
End Get
End Property
Sub DoSomething() Implements IFormCanDoSomething.DoSomething
Dim f As New Form3(Me)
f.Show()
End Sub
End Class
Public Class Form2
Implements IFormCanDoSomething
ReadOnly Property FormAction As EnumFormActions Implements IFormCanDoSomething.FormAction
Get
Return EnumFormActions.Action2
End Get
End Property
Sub DoSomething() Implements IFormCanDoSomething.DoSomething
Dim f As New Form3(Me)
f.Show()
End Sub
End Class
Public Class Form3
Private _owner As IFormCanDoSomething
Public Sub New(owner As IFormCanDoSomething)
_owner = owner
End Sub
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
If _owner.FormAction = EnumFormActions.Action1 Then
'Action if user needs one thing
ElseIf _owner.FormAction = EnumFormActions.Action2 Then
'Action if user needs another thing here
ElseIf _owner.FormAction = EnumFormActions.Action3 Then
'Action if user needs third thing
End If
End Sub
End Class
So what is the gain here? Look at Button1_Click. Do you see? - now you can have many forms for which Form3 needs to perform Action1, or/and many forms that Form3 needs to perform Action2, etc. This could go farther but for now good enough
Assuming you're using the Show method, pass the Form as the Owner into it:
Form3.Show(Form1)
Then reference the Form3 Owner:
Public Class Form3
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
If Me.Owner.Equals(Form1) Then
'Action if user are access from Form1 here
ElseIf Me.Owner.Equals(Form2) Then
'Action if user are access from Form2 here
End If
End Sub
End Class

VB.Net Inheritance Override

I'm new to VB.Net (I'm from a foxpro background) and have had my head in a book for the last two weeks trying to get started with some of the basics.
I'm trying to master class inheritance and have what I hope is not too much of a challenging question.
I've created a class and compiled it as a DLL. It simply allows me to place a button on a form. I just want to capture the Click event - which I've managed to do but would like to override the inherited code rather than having both fire which seems to be happening at the moment.
I realise I could just double click the control and enter code directly into the MyButton1 click event but wanted to trap this programmatically instead via the handler.
I thought this would just be a case of using the overridable / overrides options.
Here's the code in my class:
Imports System.Windows.Forms
Imports System.Drawing
Public Class MyButton
Inherits Windows.Forms.Button
Sub New()
End Sub
Protected Overridable Sub MyButton_Click(sender As Object, e As System.EventArgs) Handles Me.Click
MsgBox("Base Click")
End Sub
End Class
Then I place the button on my form and name it MyButton1 and in the load event:
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
AddHandler MyButton1.Click, AddressOf Button_Click
End Sub
Private Sub Button_Click()
MsgBox("Actual Click")
End Sub
Problem is, both events fire and I want the option to override / turn off the base event.
I thought I could just add the 'overrides' keyword to the Button_Click routine i.e.:
Private Sub Overrides Button_Click()
but I get an error message Sub Button_Click() cannot be declared 'overrides' because it does not override a sub in a base class
So to clarify - at the moment my code fires both events so I get two messages. I want to be able to turn off / supress the base class event.
Any help would be much appreciated.
I thought this would just be a case of using the overridable /
overrides options.
The fundamental problem here is that you're trying to push a square peg into a round hole.
To override something, you need to have inheritance involved. The derived class is overriding something that was inherited from the base class. For instance, if you inherited from your MyButton class to create a new type of Button called MyButtonDerived, then you could do it as expected:
Public Class MyButton
Inherits Windows.Forms.Button
Protected Overridable Sub MyButton_Click(sender As Object, e As System.EventArgs) Handles Me.Click
MsgBox("Base Click")
End Sub
End Class
Public Class MyButtonDerived
Inherits MyButton
Protected Overrides Sub MyButton_Click(sender As Object, e As EventArgs)
' We don't call the base method...
' MyBase.MyButton_Click(sender, e)
' ... and instead do something else:
MsgBox("Derived Click")
End Sub
End Class
In contrast, when you've placed MyButton onto the Form as in your original problem description, no inheritance has taken place. Instead what you've setup is "object composition"; the form contains an instance of the button (not derived from it). While it may be possible to change what happens when the button is clicked from the form itself, this is not a case that can be solved with OOP, inheritance and overriding.
If MyButton was not designed in such a way that allows the end user to suppress its base functionality, then your options are limited in how you can use it. Here is an example of what it might look like if MyButton was designed to allow the end user to suppress its base click functionality:
Public Class Form1
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
MyButton1.SuppressDefaultClick = True
End Sub
Private Sub MyButton1_Click(sender As Object, e As EventArgs) Handles MyButton1.Click
MsgBox("Form Click Code")
End Sub
End Class
Public Class MyButton
Inherits Windows.Forms.Button
Private _suppress As Boolean = False
Public Property SuppressDefaultClick As Boolean
Get
Return _suppress
End Get
Set(value As Boolean)
_suppress = value
End Set
End Property
Protected Overridable Sub MyButton_Click(sender As Object, e As System.EventArgs) Handles Me.Click
If Not SuppressDefaultClick Then
MsgBox("Base Click")
End If
End Sub
End Class
If MyButton didn't include a way to suppress its built-in click handler like above then you'd have to resort to other means to solve your problem. In that case you'd have to prevent the button from ever receiving the message that the left mouse button has been clicked at all, and instead implement your own routine. This approach would be a considered a hack, since you are working around the limitations of something and not using it in the way it was originally intended. Here's one way the hack could be implemented:
Public Class Form1
Private WithEvents TMBC As TrapMyButtonClick
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
TMBC = New TrapMyButtonClick(Me.MyButton1)
End Sub
Private Sub TMBC_Click(sender As MyButton) Handles TMBC.Click
MsgBox("Form Click Code")
End Sub
Private Class TrapMyButtonClick
Inherits NativeWindow
Private _mb As MyButton
Private Const WM_LBUTTONDOWN As Integer = &H201
Public Event Click(ByVal sender As MyButton)
Public Sub New(ByVal mb As MyButton)
If Not IsNothing(mb) AndAlso mb.IsHandleCreated Then
_mb = mb
Me.AssignHandle(mb.Handle)
End If
End Sub
Protected Overrides Sub WndProc(ByRef m As Message)
Select Case m.Msg
Case WM_LBUTTONDOWN
RaiseEvent Click(Me._mb) ' raise our custom even that the form has subscribed to
Exit Sub ' Suppress default behavior
End Select
MyBase.WndProc(m)
End Sub
End Class
End Class
Public Class MyButton
Inherits Windows.Forms.Button
Protected Overridable Sub MyButton_Click(sender As Object, e As System.EventArgs) Handles Me.Click
MsgBox("Base Click")
End Sub
End Class
You are getting 2 messages because you have set 2 different event handlers for the Click event:
The MyButton_Click method defined in your MyButton class.
The Button_Click method set in your AddHandler call on the form.
As noted in a comment above, you need to override the Button.OnClick method in your MyButton class instead of creating a new method:
Imports System.Windows.Forms
Imports System.Drawing
Public Class MyButton
Inherits Windows.Forms.Button
Sub New()
End Sub
' Override the OnClick event defined in "Button" class.
Protected Overrides Sub OnClick(e As System.EventArgs)
' Call the Click event from "Button" class.
MyBase.OnClick(e)
' Some custom events.
MsgBox("MyButton Click")
End Sub
End Class
It might be a good exercise to set breakpoints in the Button_Click and MyButton.OnClick methods so you can see exactly how the stack is created.

Can't Remove Listview Item From Second Form

I am having an issue when trying to delete ListView Items from a second form.
For example, if I use the following command on Form1 it works:
Listview1.SelectedItems(0).Remove
However, if I attempt to remove from Form2 like so:
Form1.Listview1.SelectedItems(0).Remove
I get the following error:
"Invalid argument=value of '0' is not valid for 'index'. Parameter name: index"
I then tried to get a count of items from the listview on Form2 and it gives me a return of 0
Form1.Listview1.Items.Count
I'm not sure what my problem is.
Update
I have posted a brief example of my code (using your suggestion as I can understand it):
frmShowMessages
Private Sub ViewMessage()
Dim frm As New frmViewMailMessage
frm.Show()
End Sub
Public Sub DeleteItem(ByVal index As Integer)
lsvReceivedMessages.Items(index).Remove()
End Sub
frmViewMessage
Private instanceForm as frmShowMessages
Private Sub frmViewMailMessage_Load(sender As Object, e As EventArgs) Handles MyBase.Load
instanceForm = New frmShowMessages()
End Sub
Private Sub cmdDelete_Click(sender As Object, e As EventArgs) Handles cmdDelete.Click
instanceForm.DeleteItem(_index)
End Sub
Hopefully my code can help identify where my issue is.
In VB.net usually you get a default Form instance for each of your Form. Probably you are creating an instance of Form1 and then you are trying to access ListView1 of default instance.
E.g.
Sub ButtonClick()
Dim f As New Form1()
f.Show()
' at this point if you access f's ListView you will get correct count
f.ListView1.Items.Count
' however if you try to access default instance it will NOT have any item
Form1.ListView.Items.Count
End Sub
It means your instance f is NOT equal to default Form1 instance.
Solution can be, make the f variable as class level variable and use it everywhere. Or if Form1 will have only 1 instance, then you can use the default instance everywhere.
Personally I would NOT go with direct control accessing over forms. I would create a Public method which should return the data as list to the caller, in this case your Form2.
UPDATED-2:
As per your given scenario, I am simplifying things for you, and doing implementation using Event.
Public Class frmShowMessages
Private Sub btnOpenMessage_Click(sender As System.Object, e As System.EventArgs) Handles btnOpenMessage.Click
Dim frmView As New frmViewMessage(Me.ListView1.SelectedItems(0).Index)
AddHandler frmView.MessageDeleted, AddressOf DeleteMessageHandler
frmView.Show()
End Sub
Private Sub DeleteMessageHandler(sender As Object, e As frmViewMessage.MessageDeletedEventArgs)
Me.ListView1.Items.RemoveAt(e.MessageIndex)
End Sub
End Class
Public Class frmViewMessage
' a class which will be used for Event communication
Public Class MessageDeletedEventArgs
Inherits EventArgs
Public Property MessageIndex As Integer
Public Sub New(ByVal iIndex As Integer)
MyBase.New()
Me.MessageIndex = iIndex
End Sub
End Class
' main event which will alert the parent that a message deletion should be done
Public Event MessageDeleted As EventHandler(Of MessageDeletedEventArgs)
' private variable that will hold the MessageIndex
Private Property MessageIndex As Integer
' method that is responsible to raise event
Protected Overridable Sub OnMessageDeleted()
RaiseEvent MessageDeleted(Me, New MessageDeletedEventArgs(Me.MessageIndex))
End Sub
' we want to create this Form using the MessageIndex of ListView
Public Sub New(ByVal iMessageIndex As Integer)
Me.InitializeComponent()
Me.MessageIndex = iMessageIndex
End Sub
' the delete button will raise the event to indicate parent that
' a deletion of message should be done
Private Sub btnDelete_Click(sender As System.Object, e As System.EventArgs) Handles btnDelete.Click
Me.OnMessageDeleted()
End Sub
End Class