Calling an async function in Task.Run - vb.net

I use a waiting form with an animated gif inside.
The only way i've found to display this form with the animated gif "moving" is :
frmWaiting.Show()
Await Task.Run(Sub() AnyKindOfActionInASub)
frmWaiting.Close()
It works well but what if i need to call an async function returning an object instead of a simple sub? If i do :
Dim TwitterAccountToAdd As TwitterAccount
frmWaiting.Show()
TwitterAccountToAdd = Await nsTweetInvi.CallTwitterApi.GetApiTwitterDatasAccount(Me.txtTwitterId.Text)
frmWaiting.Close()
The animated gif is replaced by a white square.
Considering that GetApiTwitterDatasAccount return a Task(Of TwitterAccount), how can i do this?

Following Jimi advices, i found a solution that works perfectly.
Private TwitterAccountToAdd As New TwitterAccount
Dim strTwitterId As String = Me.txtTwitterId.Text
Dim frmWaitingTwitter As New frmWaiting
Me.TopMost = False
With frmWaitingTwitter
.TopMost = True
.Show()
.StartPosition = FormStartPosition.CenterScreen
End With
TwitterAccountToAdd = Await Task.Run(Function() CheckAndGetTwitterAccount(strTwitterId))
frmWaitingTwitter.Close()
Me.TopMost = True

Related

Task.WaitAll() in DataGridView.RowEnter event causes DataError

I am using Advanced DataGridView (https://github.com/davidegironi/advanceddatagridview) to display some data in my application.
Within the RowEnter event I create tasks like this:
Dim One As String = dgv.Item("Col1", e.RowIndex)
Dim Two As String = dgv.Item("Col2", e.RowIndex)
Dim Three As String = dgv.Item("Col3", e.RowIndex)
Dim tOne As Task(Of DataTable) = Task.Run(Function() Return GetData(One) End Function)
Dim tTwo As Task(Of DataTable) = Task.Run(Function() Return GetData(Two) End Function)
Dim tThree As Task(Of DataTable) = Task.Run(Function() Return GetData(Three) End Function)
Task.WaitAll(tOne, tTwo, tThree)
'Do stuff with the results of tasks to update other labels etc on the form
lblDate.Text = tOne.Result.Rows(0).Item("Date").ToString
The reason for this is that if the GetData() function takes, say, 5 seconds to return doing this without tasks would result in a 15 second wait (5+5+5) whereas with tasks it only takes 5 seconds (or whatever the slowest task is) and Task.WaitAll gives control back to UI (unsure on correct terminology) so it feels a bit snappier.
However when I apply a filter I get the following exception repeatedly, where [n] is any number, I've seen indexes of -1 upwards (seems to depend on the number of visible rows?):
System.IndexOutOfRangeException: Index [n] does not have a value.
at System.Windows.Forms.CurrencyManager.get_Item(Int32 index)
at System.Windows.Forms.DataGridView.DataGridViewDataConnection.GetError(Int32 boundColumnIndex, Int32 columnIndex, Int32 rowIndex)
If I remove the Task.WaitAll() or do the functions synchronously I don't get the above error.
I'm completely at a loss as to what the underlying cause of that error is, or what I should do different to avoid it.
I have seen this answer but I'm not sure how to apply to my situation.
Use async event handler and await the tasks using Task.WhenAll
' Mark the event handler with Async so you can use Await in it.
Private Async Sub Grid_RowEnter(sender As Object, e As WhateverEventArgs)
Dim One As String = dgv.Item("Col1", e.RowIndex)
Dim Two As String = dgv.Item("Col2", e.RowIndex)
Dim Three As String = dgv.Item("Col3", e.RowIndex)
Dim tOne As Task(Of DataTable) = Task.Run(Function() Return GetData(One) End Function)
Dim tTwo As Task(Of DataTable) = Task.Run(Function() Return GetData(Two) End Function)
Dim tThree As Task(Of DataTable) = Task.Run(Function() Return GetData(Three) End Function)
' await all the tasks
Await Task.WhenAll(tOne, tTwo, tThree)
' back on UI thread
'Do stuff with the results of tasks to update other labels etc on the form
lblDate.Text = tOne.Result.Rows(0).Item("Date").ToString
End Sub
Reference Async/Await - Best Practices in Asynchronous Programming

I need to use Async/Await in my already running program to make it faster

I am having a program in VB, which searches for image in a website as follows:
www.website.com/1.jpg | www.website.com/2.jpg | www.website.com/3.jpg
in loop, and the program opens up only 3.jpg in browser, since 1 and 2 jpg does not exit in server. The program is up and running, but is very very slow, around 120 searches in a minute. However one of my colleagues designed the same program in Angular and that program is running very fast, around 500/600 searches in a minute.
He told me, what he did is generated 50 asynchronous calls to server, and then again 50, and then again 50 and went on like this. thus making his program very fast and ovbiously accurate.
I studied and learnt, that in Visual Basic too, we have async and wait calls to server request, but I cannot figure out how.
Here goes my existing code. Can anyone help me.
Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
Dim MYURL as string
itemsF = 0
While Not itemsF = 50000
MYURL = "www.website.com/" & itemsF & ".jpg"
CheckPageExists(MYURL)
itemsF=itemsF+1
End While
End Sub
Private Function CheckPageExists(ByVal url As String) As Boolean
Dim request As Net.HttpWebRequest
Dim response As Net.HttpWebResponse
request = Net.WebRequest.Create(url)
request.Timeout = 5000
Try
response = request.GetResponse()
Catch ex As Exception
'IMAGE DOES NOT EXITS
Exit Function
End Try
Process.Start(url)
End Function
First, you have to make CheckPageExists an Async method:
Private Async Function CheckPageExists(ByVal url As String) As Task(Of Boolean)
Dim request As Net.HttpWebRequest = Net.WebRequest.Create(url)
request.Timeout = 5
Dim Result As Boolean
Try
Using response As HttpWebResponse = Await request.GetResponseAsync.ConfigureAwait(False)
Using responseReader As New IO.StreamReader(response.GetResponseStream)
Dim actualResponse As String = Await responseReader.ReadToEndAsync
Result = True
End Using
End Using
Catch ex As Exception
'IMAGE DOES NOT EXITS
Result = False
End Try
Console.WriteLine(url)
''Process.Start("chrome.exe", url)
Return Result
End Function
As you can see, instead of GetResponse we are using GetResponseAsync, which is Async itself. This method is very similar to what you were doing before, I
just added Return statements for clarity and a StreamReader to read the response of your website.
Once you've done that, you just need to change your Button2_Click method to call this other method, which incorporates all you were doing before:
Async Function MakeRequests() As Task
Dim tasks As List(Of Task(Of Boolean)) = New List(Of Task(Of Boolean))
Dim itemsF As Integer = 5
For i = 1 To itemsF
Dim MYURL As String = "http://www.touchegolfschool.com/images/" & i & ".jpg"
tasks.Add(CheckPageExists(MYURL))
Next
While tasks.Select(Function(x) x.Result).Count < tasks.Count
Thread.Sleep(100)
End While
End Function
The main change was adding tasks.Select(Function(x) x.Result).Count < tasks.Count; what you were seeing before was a request made but never returning because of the time it took to return; telling the main function to wait until all requests have a result makes the application wait long enough for the responses to come.
Here the main difference is the use of Tasks.
Check this for official documentation on asynchronous programming.

LiveCharts not showing in vb.net

I have tried to implement LiveCharts in vb.net project but unsuccessful. Tried searching the internet but unfurtunately found no sample code. What might be the problem in my code?sample code
Private Sub InitializeChart()
Dim labelPoint As Func(Of ChartPoint, String) = Function(chartPoint) String.Format("{0} ({1:P})", chartPoint.Y, chartPoint.Y, chartPoint.Participation)
'pieChartMain = New LiveCharts.WinForms.PieChart
Dim seriesCollection As SeriesCollection = New SeriesCollection
Dim pieSeries1 As PieSeries = New PieSeries
Dim pieSeries2 As PieSeries = New PieSeries
pieSeries1.Title = "MALE"
pieSeries1.Values = New ChartValues(Of Double) From {3}
pieSeries1.PushOut = 15
pieSeries1.DataLabels = True
pieSeries1.LabelPoint = labelPoint
pieSeries2.Title = "FEMALE"
pieSeries2.Values = New ChartValues(Of Double) From {6}
pieSeries2.DataLabels = True
pieSeries2.LabelPoint = labelPoint
seriesCollection.Add(pieSeries1)
seriesCollection.Add(pieSeries2)
pieChartMain.Series.Add(seriesCollection)
pieChartMain.LegendLocation = LegendLocation.Bottom
pieChartMain.Show()
End Sub
I think the sample needs more information to someone try a help...
What is the PIE control name?
Are you IMPORTING the libraries?
Imports LiveCharts.WinForms
Imports LiveCharts.Wpf
Imports LiveCharts
After the last PieChart command, try to force the redraw. For instance, in a Gauge control, I get success if utilize:
Gauge.resumelayout
So, try to REFRESH and also RESUMELAYOUT over the control

VB.net function that returns an object that is specified by the input

I am trying to write a function that returns a newly created object ( a form ) that is specified by the input. I'm having trouble with how to work out the concept of giving a type as an input then creating an object of that type in the body of the function. Here is an outline of what I'm working on.
Public Function MakeMyForm(ByVal frmType as Form) as Form
Dim NewObj as New frmType
Return NewObj
End Function
I'd like to be able to call the function in this way:
Dim myform as CustomFormType
myform = MakeMyForm(CustomFormType)
Can my concept be accomplished in VB.net?
Ok, if I understand you, you just want a generic method:
Public Function MakeMyForm(Of T As {New, Form})() As T
Return New T()
End Function
and call it like this:
Dim myform As CustomFormType = MakeMyForm(Of CustomFormType)()
of course, why wouldn't you just use:
Dim myform As New CustomFormType()
Well you can try this:
Dim frmnew() As Form
Dim createdforms As Integer = 0
Private Sub createform(wintext As String, height As Integer, width As Integer, backcolor As Color, topmost As Boolean, formborderstyle As FormBorderStyle, winstate As FormWindowState, opacity As Decimal, startposition As FormStartPosition, enabled As Boolean) 'add as many properties as you like
ReDim Preserve frmnew(createdforms)
frmnew(createdforms) = New Form
With frmnew(createdforms)
.Text = wintext
.Height = height
.Width = width
.BackColor = backcolor
.TopMost = topmost
.FormBorderStyle = formborderstyle
.WindowState = winstate
.Opacity = opacity
.StartPosition = startposition
.Enabled = enabled
End With
frmnew(createdforms).Show()
createdforms += 1
End Sub
and you can test it with the code below:
createform("Afnan Makhdoom", 500, 700, Color.Aqua, False, Windows.Forms.FormBorderStyle.Fixed3D, FormWindowState.Normal, 0.9, FormStartPosition.CenterScreen, True)
Public Function Makemyform(ByVal frmType As Form) As Form
Dim obj As Form
obj = newfunc(frmType)
Return obj
End Function
Public Function newfunc(ByVal mytype As Form) As Form
Return New Form
End Function
This is usually done using generics in a function such as:
Public Function GetItem(Of T)(key As String) As T
Usage:
myIntVar = myFoo.GetItem(Of Int32)(bar)
The purpose of which is for the code calling it to specify how it needs the return. In the above a whole bunch of data has been serialized and the original Type lost, so when fetching it back, the Of T helps convert it rather than using Object as the return.
For forms, it is more problematic:
Public Function MakeAForm(Of T)() As Form ' cant do As T
You'd have to add more code to cast Form to Form1 or frmCust to avoid tbName is not a member of System.Windows.Forms.Form errors. Even the correct way as shown by Mr Dokjnas present problems trying to do more with the form:
Public Function MakeAForm(Of T As {New, Form})() As T
Dim frm As New T
If frm.GetType Is frm8088.GetType Then
frm.textbox1.text = "ziggy" ' error
End If
Return frm
Here, it is 'TextBox is not a member of T`. If your forms were compiled to a ClassLib so the IDE could know more about the Types (forms) you could get it to work. But the first sign of futility is revealed in using it:
Dim frm As Form = MakeAForm(Of frm8100VI)()
frm.Show()
It takes more code to call the FormMaker than to just create an instance.

Start task without waiting

I would like to start a background task without using the Await keyword. Instead I want to monitor the task at various points and restart it when necessary to update information in the background. Here is the method I am trying to call:
Public Async Function UpdateVehicleSummaries(p_vehicleID As Int32) As Task(Of Boolean)
Dim tempVehicle As Koolsoft.MARS.BusinessObjects.Vehicle
For Each tempVehicle In Vehicles
If p_vehicleID = 0 Or p_vehicleID = tempVehicle.VehicleID Then
Await UpdateVehicleStats(tempVehicle)
End If
Next
Return True
End Function
The code I am trying to start the task doesn't seem to work and I'm not sure how to provide the parameter. I get an error that "Task(Of Boolean) cannot be converted to System.Action and or an error on the parameter"
Dim tempTask As Task
tempTask = New Task(UpdateVehicleSummaries(tempVehicleID))
tempTask.Start()
Any help would be appreciated.
Since UpdateVehicleSummaries is already asynchronous, you should be abel to just do:
Dim tempTask As Task(Of Boolean) = UpdateVehicleSummaries(tempVehicleID)
The returned Task(Of T) will be "hot" (running), but shouldn't block, as the Await call will immediately return control flow to the caller at that point.
A more typical use of this method, if you need to perform other work while this runs, would be to do the following:
Dim tempTask = UpdateVehicleSummaries(tempVehicleID)
' Do your other work
Dim success = Await tempTask ' Get the boolean result asynchronously...
' use the result