Vb.NET instruction execution not triggered by event - arrays

I have a very basic question on vb.NET, so basic that I didn't find any answer elsewhere.
I've written with VB express a code that is a simple form proposing various choices through checkboxes.
These choices have to be registered in an array, which I convert later into a textfile to be Perl-processed.
I'm searching a way to zeroise this big array with loops before use, but in fact I don't know how to execute instructions which wouldn't be triggered by events in my main form.
The frame looks like that :
Public Class Form1
'Variables declaration...
'Several boxes like that :
Private Sub CheckBox1_CheckedChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles CheckBox1.CheckedChanged
'Instructions...
End Sub
End Class
To launch this application, I simply click on the associated .exe file.
Basically, my question is :
is there a way to execute instructions that wouldn't be launched by an user event, but immediatly launched when the main Form1 is shown ?
Sorry for being retarded, and thank you in advance if you can help me.

The Form.Load event for instance gets launched as soon as the form is about to be shown :)

The Form will raise a Shown event when it is shown simply handle that:
Private Sub Form_Shown(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Me.Shown
'Instructions...
End Sub
It can be better to do initialisation in the constructor right after the Winforms designer adds the comment: 'Add any initialization after the InitializeComponent() call

Related

How to call event handler programatically

I have an event handler CellEndEdit of datagridview in winforms application, which runs when i finish write something in datagridview. Now i want to call this event also on button click. How can i achieve this? thanks.
Let's take a look at a regular event handler. (BTW, you didn't specify C# or VB so I'll use VB)
The name of this subroutine is "Button1_Click" and it takes 2 arguments, of type Object and System.EventArgs.
Normally, an event handler is just called by the system, but there is no reason you can't call the event handler yourself.
The challenge will be if your code in the event handler depends on the sender and event arguments, you'll have a hard time replicating that, but if your code doesn't depend on that, just pass in Nothing or null for each argument.
However, if there is common behaviour, I would define a separate function, and call that function from both event handler, and button click.
Example:
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
rgv_CellEndEdit(Nothing, Nothing)
End Sub
Private Sub rgv_CellEndEdit(ByVal sender As System.Object, ByVal e As Telerik.WinControls.UI.GridViewCellEventArgs) Handles rgv.CellEndEdit
FillGridLine()
If rgv.CurrentCell.ColumnInfo.Name = "ProductId" And rgv.CurrentCell.Value Is Nothing And IsDBNull(rgv.CurrentCell.Value) = True Then
rgv.CurrentRow = rgv.Rows(rgv.CurrentRow.Index)
rgv.CurrentColumn = rgv.Columns("ProductId")
End If
End Sub

Visual Basic sender not coming back as control

I have a windows application with 2 forms.
frmMain has a button (btnEditAgent) on it that opens frmEditAgent:
Public Sub btnEditAgent_Click(sender As Object, e As EventArgs) Handles btnEditAgent.Click
frmAgentEdit.ShowDialog()
End Sub
Then on frmEditAgent load:
Private Sub frmAgentEdit_Load(ByVal sender As Object, e As EventArgs) Handles MyBase.Load
MsgBox(sender.Name, vbOKOnly, "verify")
End Sub
The sender comes back as "frmEditAgent" and not "btnEditAgent"
I do not understand why this is happening. In order for the rest of my code to work I need to know which button opened frmEditAgent. Why is sender referring to the same form?
The sender of the Load event is the Form that was just loaded. The semantics of sender wouldn't make sense otherwise.
Think of it this way: the button opens the form, but the form does its own loading, and so calls its own Load event with itself as the sender.
If you want to know which button opened the form, then you can add an instance variable to your dialog form's class and then just set that variable in btnEditAgent_Click.
frmAgentEdit can then just use that instance variable to know which button caused it to be opened.

host WPF windows in another WPF window

here is my problem: i have created a main window called mainWindow in XAML (vb.net) and inside i have 2 buttons (valid and stop) and a grid in the center.
I have two others little windows (valid window and stop window) written in XAML (vb.net) which have buttons, textbox...
I want, when i click on valid button or stop button, display the valid window or the stop window inside the grid of my mainWindow, so i have this code in my mainWindow.vb:
enter code here
Private Sub valid_Click(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs) Handles valid.Click
Dim content As Object = valid_.Content /*Classe valid_ (a window in xaml)*/
valid_.Content = Nothing
Me.Grid.Children.Add(content)
End Sub
Private Sub stop_Click(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs) Handles stop.Click
Dim content As Object = stop_.Content
stop_.Content = Nothing
Me.Grid.Children.Add(content)
End Sub
So when i click on button valid in my mainWindow, it's ok it displays the valid window in my grid.
First problem: then when i click on button stop in my mainWindow, the stop window is placed just above the valid window, it is not nice, is there a way to clear the grid before display this second window?
And finally, the biggest problem: i need to click many times on valid button or stop button but when i click the second time i have a null reference exception: Me.Grid.Children.Add(content) content is null after the first call so i am only able to click one time on my button.
How can i fix it in order to click many times on my buttons please?
I give you thanks.
Yet again, someone writes some invalid code and then says why isn't this code working? If you look at your code, you should see something:
Private Sub valid_Click(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs) Handles valid.Click
Dim content As Object = valid_.Content /*Classe valid_ (a window in xaml)*/
Me.Grid.Children.Add(content)
End Sub
I'm guessing that you have a private variable for your Valid Window as you call it: valid. On this line, what are you doing to your variable?:
valid_.Content = Nothing
That's right! You're setting its Content property to null. Therefore, I'm not really sure why you were surprised that it was null after the first attempt.
How can i fix it in order to click many times on my buttons please?
Try removing the line that sets the Content property to null.
UPDATE >>>
Your problem is really caused by the fact that you cannot display any UI element in two places at once. Your whole idea of copying the Window.Content to your DataGrid is entirely wrong, but in the name of brevity, your fix is simply to move your content back to the Window, rather than setting it to null each time. Try something like this:
Private Sub valid_Click(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs) Handles valid.Click
If Me.Grid.Children.Count > 0 Then 'Put content back where it belongs
Dim oldContent = Me.Grid.Children(0)
stop.Content = oldContent
Me.Grid.Children.Remove(oldContent)
EndIf
Dim content As Object = valid_.Content /*Classe valid_ (a window in xaml)*/
valid_.Content = Nothing
Me.Grid.Children.Add(content)
End Sub
Please forgive any code mistakes, as I don't write VB.NET. Also, you'll need to update your other method likewise.

Open User Control from user control in main window grid WPF

Maybe first I will tell about my application.
When for example an employee will log into my application, he will have loaded "Employee Menu":
Dim Empl As New Employee
MainGrid.Children.Add(Empl)
Grid.SetRow(Empl, 1)
This is from Window_Loaded event. Menu is User Control, and there I have few buttons to open and operate another user controls. When I press for example button "Question":
Public Class Employee
Dim mw As New MainWindow
Private Sub btnQuestionAdd_Click(sender As System.Object, e As System.Windows.RoutedEventArgs) Handles btnQuestionAdd.Click
Dim Que As New QuestionAdd
mw.MainGrid.Children.Add(Que)
Grid.SetRow(Que, 2)
Grid.SetColumn(Que, 1)
End Sub
End Class
I don't know why nothing is loading after button_click.....
Is it so hard to navigate main windows grid from other controls?
This is just a guess as you've not provided much information, but I noticed the following issue which may be the culprit.
In your first code snippet, you seem to be creating the employee from the MainForm:
Dim Empl As New Employee
MainGrid.Children.Add(Empl)
Grid.SetRow(Empl, 1)
Your following comment would seem to confirm that assumption:
This is from Window_Loaded event. Menu is User Control, and there I have few buttons to open and operate another user controls. When I press for example button "Question"
And yet, in your Employee class, you are creating a brand new instance of MainWindow and then adding data to it:
Public Class Employee
Dim mw As New MainWindow
Private Sub btnQuestionAdd_Click(sender As System.Object, e As System.Windows.RoutedEventArgs) Handles btnQuestionAdd.Click
Dim Que As New QuestionAdd
mw.MainGrid.Children.Add(Que)
Grid.SetRow(Que, 2)
Grid.SetColumn(Que, 1)
End Sub
End Class
If this observation is correct, then I think you need to go back to the books and understand the concept of classes and instances.
You essentially created a second form (which is hidden because you never explicitly show it) and then modify this second form rather than the original. To prove this hypothesis, try adding the following line of code:
Public Class Employee
Dim mw As New MainWindow
Private Sub btnQuestionAdd_Click(sender As System.Object, e As System.Windows.RoutedEventArgs) Handles btnQuestionAdd.Click
Dim Que As New QuestionAdd
mw.MainGrid.Children.Add(Que)
Grid.SetRow(Que, 2)
Grid.SetColumn(Que, 1)
mw.Show() ' <---
End Sub
End Class
You'll likely see a second form pop up with all of the changes your were expecting on your first form.
As to how to fix this, the easiest path is going to be adding a parameter to your initializer ("Sub New") which accepts the MainForm as a value. You can then assign the value to a field or property (probably just your mw field) and continue on your merry way. This is going to give you headaches down the road, though, so it might be a good time to start learning more about software architecture, especially the concept of separation of concerns.

Threaded Function has multiple parameters and returns data

I am working on a WPF .NET 3.5 application that does a few long tasks that I would like to make a seperate thread to the UI thread to process the data and then when completed update some labels in the UI. The problem I am having is that the function I have uses two parameters and I am struggling to work out how to call a function with multiple parameters in a thread and update the UI.
I have been playing around with using a Delegate Sub to call the function (it is located in a seperate Class), and my code was also attempting to return a dataset from the function for the calling thread to update the UI, but I am not sure if this is the best practice to achieve this or wether I should use a dispatcher for the called function to do the UI updating (feedback would be greatly appreciated).
My code is as follows.
Private Delegate Sub WorkHandler(ByVal input1 As String, ByVal input2 As String)
Private Sub Window_Loaded(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs)
Dim test_helper As New test_global
Dim worker As New WorkHandler(AddressOf test_helper.getWeatherData)
worker.BeginInvoke("IDA00005.dat", "Adelaide", AddressOf weatherCallBack, Nothing)
' The following is what I was using prior to attempting to work with threads, do I continue to update the UI here getting the called function to return a dataset, or do I have the called function do the UI updating?
'Dim ls As DataSet = test_helper.getWeatherData("IDA00005.dat", "Adelaide")
'Dim f_date As String = ls.Tables("weather").Rows(1).Item(3).ToString
End Sub
Public Sub weatherCallBack(ByVal ia As IAsyncResult)
CType(CType(ia, Runtime.Remoting.Messaging.AsyncResult).AsyncDelegate, WorkHandler).EndInvoke(ia)
End Sub
And my function that I am attempting to call is as follows:
Class test_global
Public Sub getWeatherData(ByVal filename As String, ByVal location As String) 'As DataSet
...
End Sub
End Class
My problem is if I was to have the calling thread to update the UI, how do I have the called thread to return a dataset, or if the called thread is to update the UI, how do I go about achieving this?
Update:
Following the recomendations provided, I have impletemented a BackgroundWorker that raises a DoWork and RunWorkerCompleted events to get the data and update the UI, respectively. My updated code is as follows:
Class Weather_test
Implements INotifyPropertyChanged
Private WithEvents worker As System.ComponentModel.BackgroundWorker
Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged
Private Sub NotifyPropertyChanged(ByVal info As String)
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(info))
End Sub
Private Sub Window_Loaded(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs)
Dim test_helper As New test_global
Dim worker = New System.ComponentModel.BackgroundWorker
worker.WorkerReportsProgress = True
worker.WorkerSupportsCancellation = True
Dim str() = New String() {"IDA00005.dat", "Adelaide"}
Try
worker.RunWorkerAsync(str)
Catch ex As Exception
MsgBox(ex.Message)
End Try
End Sub
Private Sub worker_DoWork(ByVal sender As Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles worker.DoWork
Dim form_Helpder As New test_global
Dim ds As DataSet = form_Helpder.getWeatherData(e.Argument(0), e.Argument(1))
e.Result = ds
End Sub
Private Sub worker_Completed(ByVal sender As Object, ByVal e As RunWorkerCompletedEventArgs) Handles worker.RunWorkerCompleted
If e.Error IsNot Nothing Then
MsgBox(e.Error.Message)
Else
...
NotifyPropertyChanged("lbl_minToday")
...
End If
End Sub
End Class
I then have in a seperate class my functions that get and process the data.
I am able to debug the code in Visual Studio 2010 and the form displays but the labels are not updating, and when I put a breakpoint at the RunWorkerAsync line the line is called and the Window_Loaded sub completes but it appears that none of the DoWork or RunWorkerCompleted events are called (well at least the functions are not).
Can anyone provide some assistance on how I can debug the code to see why these functions are not being called?
Also, is the above code the correct method that was recommended in the answers?
Any assistance provided will be greatly appreciated.
Matt
You should use the BackgroundWorker component.
You should call your function in the DoWork handler and set e.Result to the returned DataSet.
You can then update the UI in the RunWorkerCompleted handler.
Use a BackgroundWorker. Implement your long-running method, and pass the arguments to the method in the DoWorkEventArgs parameters of the DoWork event handler. Do not update the UI, either directly or indirectly (i.e. don't update properties of your view model), in this method.
Use progress reporting to update the UI while the method is running: call ReportProgress in the long-running method, passing any information that needs to appear in the UI in the UserState parameter. In the ProgressChanged event handler, get the state from the ProgressChangedEventArgs and update the UI (by, one hopes, updating the appropriate properties of your view model and raising PropertyChanged).
You'll need to implement a class to contain the user state for progress reporting, since UserState is of type object.
Note that you can also update the UI with the results of the long-running method when it's complete. This is done in a similar fashion to progress reporting: implement a class to contain the results, set the Result property of the DoWorkEventArgs to an instance of this class, and the result will be available in the Result property of the WorkCompletedEventArgs when the RunWorkerCompleted event is raised.
Make sure that you handle any exceptions that the long-running method raises by checking the Error property of the WorkCompletedEventArgs.
I don't have much experience with BackgroundWorker (I have only used it once), but it is definitely a solution to your problem. However, the approach I always use is to start a new Thread (not ThreadPool thread via delegates), which acquires a lock and then updates all of the properties. Provided that your class implements INotifyPropertyChanged, you can then use databinding to have the GUI automatically update any time the property changes. I have had very good results with this approach.
As far as passing a Dispatcher to your thread goes, I believe you can do that as well. However, I would tread lightly because I believe I have run into cases with this where the Dispatcher I think I'm using is no longer associated with the main thread. I have a library that needs to call a method that touches GUI elements (even though the dialog might not be displayed), and I solved this problem by using Dispatcher.Invoke. I was able to guarantee that I was using the Dispatcher associated with the main thread because my application uses MEF to Export it.
If you'd like more details about anything I've posted, please comment and I'll do my best to embellish on the topics.

Resources