Can't Remove Listview Item From Second Form - vb.net

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

Related

VB - Find child form from parent

I am in a project with multiple form.
I create a TicTacToe form here :
Private Sub MenuTicTacToe(ByVal sender As Object, ByVal e As System.EventArgs)
Dim page As Form = New TicTacToe
page.Show(Me)
End Sub
Here is a TicTacToe form:
Public Class TicTacToe
Public opponent as String
'Some code where user set opponent
Public Function Receive(S As String)
if string = opponent
'Some code
End Function
End Class
I would like to call my function Receive in my main form
If i do:
TicTactoe.Receive(S)
It call a instance of Receive where opponent does not exist.
I would like to find the oppened form of TicTacToe and call Receive
Thanks
Comments in line
Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
page.Receive("Joe")
End Sub
'A form level variable to hold a reference to the instance of TicTacToe
'Although vb.net can use default instances, you have created an explicit
'instance of TicTacToe so you need to keep a reference if you want to
'refer to this instance.
Private page As TicTacToe
Private Sub MenuTicTacToe(ByVal sender As Object, ByVal e As System.EventArgs)
page = New TicTacToe()
page.Show(Me)
End Sub
Partial Public Class TicTacToe
Inherits Form
Public opponent As String
'Functions must be declared as a Type
'If you do not need a return value use a Sub
Public Function Receive(S As String) As String
Dim someString As String = ""
If S = opponent Then
'Do something
End If
'There must be a return Value
Return someString
End Function
End Class
Use this to show the form
Dim page As TicTacToe
Private Sub MenuTicTacToe(ByVal sender As Object, ByVal e As System.EventArgs)
page = New TicTacToe
page.Show(Me)
End Sub
Then you can use
page.Receive(S)
Edit
To use multiple forms
For Each f As TicTacToe in Application.OpenForms().OfType(Of TicTacToe)
f.Receive (S)
Next
In C#, you'd need a new instance, but as you are in VB, the compiler already does that for you.
What you are currently doing, is creating a new instance of the TicTacToe form and showing it:
Private Sub MenuTicTacToe(ByVal sender As Object, ByVal e As System.EventArgs)
Dim page As Form = New TicTacToe
page.Show(Me)
End Sub
But you don't save that instance anywhere. Then, in your next piece of code, you are using a different instance, which is the static one created by the compiler:
TicTacToe.Receive(S) // TicTacToe is the static instance
Therefore, you end up calling two different instances, which explains why there is no opponent set.
To get around this problem, do not create a new instance. In your Private Sub MenuTicTacToe, just use the instance created by the compiler, and you won't have this problem, just like this:
Private Sub MenuTicTacToe(ByVal sender As Object, ByVal e As System.EventArgs)
TicTacToe.Show(Me)
End Sub
Hope this helps.

Handling a timer from a class

I want some values in a class to decrease whenever the timer in the main form ticks. I am creating multiple instances of the same class as my program is a simulation application and I am not storing these instances in an array or any list in that matter. I simply declare them and add their picture box to the controls on the main form. However I am hoping to have a sub routine inside the class that triggers whenever the timer in the main form ticks. I thought of something like this:
Public Class Jimmy
Dim _a As Integer = 10
Sub decreseNum(sender As Object, e As EventArgs) Handles mainapp.tmrLog.Tick
_a -= 1
End Sub
End Class
with mainapp being the name of the main form and tmrLog being the timer I want to associate my sub routine with. However the above code doesn't work
You could try defining a local reference to the timer in the Jimmy class:
Public Class Jimmy
Dim _a As Integer = 10
Private WithEvents tmr As Timer
Public Sub New(ByRef MainTmr As Timer)
tmr = MainTmr
End Sub
Sub decreseNum(sender As Object, e As EventArgs) Handles tmr.Tick
_a -= 1
End Sub
End Class
If you want all your classes react to timer.elapsed event, just sign up for it. The program below is fully operational. It is example what you can do to have your children to react to timer events of single parent/timer
Imports System
imports system.timers
Public Module Module1
Public Sub Main()
dim mc as new MainClass()
mc.CreateChildren(5)
System.Threading.Thread.Sleep(60000) ' wait and monitor output of childern
mc.Stop()
Console.WriteLine("All should stop now...")
Console.Read()
End Sub
End Module
public class MainClass 'This class could be your form
private _timer as new Timer(5000)
public sub CreateChildren(count as integer)
For i as integer = 1 to count
dim c as new Child(i)
Addhandler _timer.Elapsed, addressof c.DoWhentimerTicks
next
Console.WriteLine("timer should run now...")
_timer.Start()
end sub
public sub [Stop]()
_timer.Stop()
End Sub
End class
public class Child
private _myNO as integer
public sub new (no as integer)
_myNo = no
end sub
public sub DoWhentimerTicks(sender as object , e as ElapsedEventArgs)
Console.WriteLine(string.format("Child #{0} just ticked. Time = {1}", _myNo, e.signaltime))
end sub
End class
I found my solution, posting here for further reference.
My situation was trying to have my timer in the mainform triggering a sub in a class, and I used the following solution.
Class:
Sub addHandlesToSub
AddHandler Form1.Timer1.Tick, AddressOf subToBeTriggered
End Sub
Sub subToBeTriggered(sender As Object, e As EventArgs)
'My code
End Sub
The parameters in subToBeTriggered are useful when you want to remove the handler with
RemoveHandler Form1.Timer1.Tick, AddressOf subToBeTriggered
Otherwise, there will be an error without the parameters.
Thanks for all the answers though.

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.

How do I get an event to fire as soon as an object's constructor has finished?

Research tells me that raising an event from the constructor itself is not feasible as the object may not be fully initialised... so where can I fire an event from as soon as the constructor has fired?
One thing you can do is add a method to handle additional post ctor tasks:
Friend Class FooBar
Public Sub New
' your code here
End Sub
Public Sub Create
' do anything you want
End Sub
End Class
Elsewhere:
Friend WithEvents Foo As Foobar
' ...
Foo = New FooBar ' Foo doesnt exist until ctor code executes and the
' code returns to here.
Foo.Create ' do whatever you like, as long as any other
' objects referenced have been created.
The reason calling a sub from the ctor to raise an event wont work with a class is this:
Private Sub SomeEvent(sender As Object, e As EventArgs) Handles Foo.SomeEvent
Console.Beep()
End Sub
the key is Handles Foo.SomeEvent
There is no Foo yet to handle the event. It doesnt crash and there event is raised, but there is no object for the listener to catch/handle the event. Enough of a form is created in InitializeComponents, that it does work with a form.
There might also be an Interface to implement something like this, I know of some for Components, but not classes.
You could use the Load or Show events from the Shown.
Private Sub myForm_Shown(sender As Object, e As EventArgs) Handles Me.Shown
End Sub
or
Private Sub myForm_Load(sender As Object, e As EventArgs) Handles Me.Load
End Sub
You can accomplish this by adding an Action(Of T) parameter to your constructor and invoke the delegate on the very last line.
Public Class Foo
Public Sub New(ByVal action As Action(Of Foo))
'...
'...
'...
If (Not action Is Nothing) Then action.Invoke(Me)
End Sub
End Class
Example
Public Class Form1
Private Sub Button1_Click(sender As Object, ev As EventArgs) Handles Button1.Click
Dim foo1 As New Foo("foo1", AddressOf Me.HandleFooCtor)
Dim foo2 As New Foo("foo2", Sub(f As Foo) MessageBox.Show(f.Name))
End Sub
Private Sub HandleFooCtor(f As Foo)
MessageBox.Show(f.Name)
End Sub
Public Class Foo
Public Sub New(name As String, Optional ByVal action As Action(Of Foo) = Nothing)
'...
'...
'...
Me.Name = name
If (Not action Is Nothing) Then action.Invoke(Me)
End Sub
Public ReadOnly Name As String
End Class
End Class

Form data erased after Windows form is closed

I have a subsidiary form where I can enter data and then save it before closing the form and going back to using the main form.
When I re-open the subsidiary form, I cannot see the changes in the data that I had entered earlier.
Can anyone tell me where I'm wrong ?
MainForm.vb
Public Class Maincls
oTestObj as New Testcls
oTestObj.XYZ = "XYZ"
Private Sub SoftwareSettingsToolStripMenuItem_Click(sender As System.Object, e As System.EventArgs) Handles SoftwareSettingsToolStripMenuItem.Click
Testcls.tbXYZ.Text = oTestObj.m_XYZ
Testcls.Show()
End Sub
End Class
Form_Testcls.vb
Public Class Testcls
Structure Params
Dim m_XYZ as String
End Structure
Dim oParams as Params
Public Sub New ()
InitializeComponent()
End Sub
Private Sub btnOK_Click(sender As System.Object, e As System.EventArgs) Handles btnOK.Click
XYZ = tbXYZ.Text
Me.Hide()
End Sub
Public Property XYZ() As String
Get
Return Me.oparams.m_XYZ
End Get
Set(ByVal value As String)
Me.oparams.m_XYZ = value
End Set
End Property
End Class
I think in windows forms the work around for this is to create a static class and add properties according to your requirement. Then populate these static properties on closing of your form. Now you can use the value set in the static data members, unless otherwise you change them on any other event.
Edit: In vb.net the Static is actually NonInheritable