I have written some VB.Net code using WebView2 control to try to download a PDF file from a specific magazine.
My VB.Net code is following
Imports Microsoft.Web.WebView2.Core
Public Class Form1
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles Me.Load
Call InitializeAsync()
End Sub
Async Sub InitializeAsync()
Await wv.EnsureCoreWebView2Async()
wv.CoreWebView2.Navigate("https://journal.cinetelerevue.sudinfo.be")
End Sub
Private Sub wv_NavigationCompleted(sender As Object, e As CoreWebView2NavigationCompletedEventArgs) Handles wv.NavigationCompleted
Threading.Thread.Sleep(1000)
Call ClickOnPdfButton()
Threading.Thread.Sleep(1000)
End Sub
Async Sub ClickOnPdfButton()
Dim sButtonCmd = "document.getElementById('readPdfBtn').click();"
Dim task = Await wv.ExecuteScriptAsync(sButtonCmd)
End Sub
End Class
The first Navigate() method display correctly requested URL.
The Javascript document.getElementById('readPdfBtn').click(); method works also correclty. It open a NEW window because Javascript code linked to click() method do following action
var e = window.open("","pdf_view");
When program has run, I obtain following result
I have painted a red circle around PDF button in first Window.
My problem is that I need to continue to click on another PDF button contained in new Window to initiate PDF download.
How can I access it using wv WebView2 variable ?
In tasks manager, I can see that new Windows is attached to Extract-PDF-From-Web application that is the name of my VB.Net application.
To solve this issue, I have added an event handler in wv.NavigationCompleted event in which I have changed e.NewWindow property.
I have also try to set URI but without success.
The full VB.Net solution that works using Visual Studio 2022 is following
Imports Microsoft.Web.WebView2.Core
Public Class Form1
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles Me.Load
Call InitializeAsync()
End Sub
Async Sub InitializeAsync()
Await wv.EnsureCoreWebView2Async()
wv.CoreWebView2.Navigate("https://journal.cinetelerevue.sudinfo.be")
End Sub
Public Sub NewWindowRequested(sender As Object, e As CoreWebView2NewWindowRequestedEventArgs)
e.Handled = True
Dim cwv As CoreWebView2 = sender
e.NewWindow = cwv
End Sub
Private Sub wv_NavigationCompleted(sender As Object, e As CoreWebView2NavigationCompletedEventArgs) Handles wv.NavigationCompleted
AddHandler wv.CoreWebView2.NewWindowRequested, AddressOf Me.NewWindowRequested
Threading.Thread.Sleep(1000)
Call ClickOnPdfButton()
End Sub
Async Sub ClickOnPdfButton()
Dim sButtonCmd = "document.getElementById('readPdfBtn').click();"
Dim task = Await wv.ExecuteScriptAsync(sButtonCmd)
End Sub
End Class
After running this code, I obtain following result
Related
I am a volunteer for the National Park Service trying to convert an interactive display originally created 20 years ago in a language called ToolBook into Visual Basic. The program consists of several projects under a single solution. The starting project, called "MainMenu", can be thought of as a library, with buttons that bring up “books.” The project called Geology is an example “book” and GeologyMenu can be thought of as the index of a book. The buttons on GeologyMenu connect to “chapters” that explain and show examples of geologic processes in the park. The “chapters” are within the project “Geology” and work fine within the project. All forms used in the program have timers that allow the program to re-set itself to MainMenu when not in use.
In a previous post, with the help of Idle Mind (thank you again), the following code for works fine for going from MainMenu to GeologyMenu and in the reverse direction as long as no button is pushed on GeologyMenu. However, if I go to a “chapter” I can no longer get back to the MainMenu from the GeologyMenu. Here is the relevant code:
MainMenu
Public Class frmMainMenu
Private Sub BtnGeology_Click(sender As Object, e As EventArgs) Handles btnGeology.Click
Dim formNew As New Geology.frmGeologyMenu
AddHandler formNew.FormClosed, AddressOf formNew_FormClosed
TimerMain.Stop()
formNew.Show()
Me.Hide()
End Sub
Private Sub formNew_FormClosed(Sender As Object, e As FormClosedEventArgs)
lblTime.Text = 8
TimerMain.Start()
Me.Show()
End Sub
GeologyMenu
Public Class frmGeologyMenu
Public Sub frmGeologyMenu_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
lblTime.Text = 6
TimerGeologyMenu.Enabled = True
Me.Show()
End Sub
Private Sub BtnErosion_Click(sender As Object, e As EventArgs) Handles btnErosion.Click
TimerGeologyMenu.Stop()
frmErosionP01.Show()
Me.Hide()
End Sub
The code below for takes the viewer to the Erosion “chapter”
Private Sub BtnErosion_Click(sender As Object, e As EventArgs) Handles btnErosion.Click
TimerGeologyMenu.Stop()
frmErosionP01.Show()
Me.Hide()
End Sub
Erosion “Chapter” . This is the code for the button on every form in Erosion that takes the program back to GeologyMenu
Public Class frmErosionP02
Private Sub BtnGeologyMenu_Click(sender As Object, e As EventArgs) Handles btnGeologyMenu.Click
My.Computer.Audio.Stop()
frmGeologyMenu.lblTime.Text = 10
frmGeologyMenu.TimerGeologyMenu.Enabled = True
frmGeologyMenu.Show()
Me.Close()
End Sub
The code for forms within Erosion takes me back to GeologyMenu, but then MainMenu won’t show when I close GeologyMenu and I don’t understand why or how to fix it. Thank you in advance for your help!
Simply, pass the previous menu/Form to the new one in a parameterized constructor and keep it in a class variable, then handle the Form.Closed event of the new menu to show the previous one.
Example for the relevant code:
Public Class frmMainMenu
Inherits Form
Private Sub BtnGeology_Click(sender As Object, e As EventArgs) Handles btnGeology.Click
Dim formNew As New frmGeologyMenu(Me)
Me.Hide()
formNew.Show()
End Sub
End Class
Public Class frmGeologyMenu
Inherits Form
Private PreviousMenu As Form
Private Sub New()
InitializeComponent()
'...
End Sub
Sub New(prevMenu As Form)
Me.New()
PreviousMenu = prevMenu
AddHandler FormClosed,
Sub(s, e)
PreviousMenu.Show()
End Sub
End Sub
Private Sub BtnErosion_Click(sender As Object, e As EventArgs) Handles btnErosion.Click
Dim frmErosion As New frmErosionP02(Me)
Me.Hide()
frmErosion.Show()
End Sub
End Class
Public Class frmErosionP02
Inherits Form
Private PreviousMenu As Form
Private Sub New()
InitializeComponent()
'...
End Sub
Public Sub New(prevMenu As Form)
Me.New()
PreviousMenu = prevMenu
AddHandler FormClosed,
Sub(s, e)
PreviousMenu.Show()
End Sub
End Sub
End Class
I have a form Main that calls open a dialog, which gets a text value response and that value would need to be used in Main. I was wondering how to achieve this where Main would have access to this after the dialog closes. The minimal example of this is as follows:
Private Class Main
Private Sub btn_Click(sender As Object, e As EventArgs) Handles btnOpenDialog.Click
Dim dialog As New Dialog
dialog.Show()
End Sub
End Class
and Dialog
Private Class Dialog
Private response As String
Private Sub btnOK_Click(sender As Object, e As EventArgs) Handles btnOK.Click
response = txtResponse.Text
Me.Close()
End Sub
End Class
Just provide a pass-through property for the Text of the TextBox:
Public ReadOnly Property Response As String
Get
Return txtResponse.Text
End Get
End Property
It's hard to know for certain but you probably out to be displaying the form as a modal dialogue:
Dim response As String = Nothing
Using dlg As New Dialog
If dlg.ShowDialog() = DialogResult.OK Then
response = dlg.Response
End If
End Using
'...
Now you don't need a Click event handler for your Button as you can just set its DialogResult property to OK in the designer.
In that case the simple solution could be the following:
Private Class Main
Private Sub btnOpenDialog_Click(sender As Object, e As EventArgs) Handles btnOpenDialog.Click
Dim AskDialog As New Dialog
Dim Response As String
Response = AskDialog.DialogShow()
End Sub
End Class
and Dialog
Public Class Dialog
Private response As String
Public Function DialogShow() As String
Me.ShowDialog()
Return response
End Function
Private Sub btnOK_Click(sender As Object, e As EventArgs) Handles btnOK.Click
response = txtResponse.Text
Me.Close()
End Sub
End Class
I have 3 form ;
Form1 - Main Form
Form2 - Sub form ( contains progress bar and timer)
Form3 - Sub form with heavy contains which takes time for loading ( like parsing data from webpage and writing it to Datagridview at form load event)
I need to show form2 with a progress bar running while form3 is loading
I have following codes at Form1 ;
Me.Hide
Form2.Show()
Form3.Show()
codes from Form 2 ;
Public Class Form2
Private Sub Form2_Load(sender As Object, e As EventArgs) Handles MyBase.Load
LoadingTimer.Enabled = True
End Sub
Private Sub LoadingTimer_Tick(sender As Object, e As EventArgs) Handles LoadingTimer.Tick
If MyProgressBar.Value <= MyProgressBar.Maximum - 1 Then
MyProgressBar.Value += 10
End If
If MyProgressBar.Value = 100 Then
LoadingTimer.Enabled = False
Me.Close()
End If
If Label1.ForeColor = Color.LimeGreen Then
Label1.ForeColor = Color.White
Else
Label1.ForeColor = Color.LimeGreen
End If
End Sub
End Class
The problem is progress bar starting but freezing at the beginning while Form3 is loading
Any idea for solution?
If you're new to programming then this may be a bit confusing but the answer is to push the code from the Load event handler of Form3 into an Async method and await it. Your UI freezes because you're doing work synchronously on the UI thread. You need to either use a secondary thread explicitly or use Async/Await. This:
Private Sub Form3_Load(sender As Object, e As EventArgs) Handles MyBase.Load
'Do some work.
End Sub
would become this:
Private Async Sub Form3_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Await DoWork()
End Sub
Private Async Function DoWork() As Task
Await Task.Run(Sub()
'Do some work.
End Sub).ConfigureAwait(False)
End Function
Actually, that's probably more complex than necessary and this should work fine:
Private Async Sub Form3_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Await Task.Run(Sub()
'Do some work.
End Sub).ConfigureAwait(False)
End Sub
Having reread your question, what you probably need to do is have your async method be a function that retrieves and returns the data from the web page or whatever and then you load that data into your DataGridView synchronously afterwards, e.g.
Private Async Sub Form3_Load(sender As Object, e As EventArgs) Handles MyBase.Load
DataGridView1.DataSource = Await GetDataAsync()
End Sub
Private Async Function GetDataAsync() As Task(Of DataTable)
Return Await Task.Run(Function()
Dim table As New DataTable
'Populate table here.
Return table
End Function).ConfigureAwait(False)
End Function
So the GetDataAsync method returns a Task(Of DataTable), i.e. a Task that asynchronously executes a function that returns a DataTable. In the Load event handler, you call that method and await the Task, which means that your code will wait until the Task has executed and returned its data but without blocking the UI thread as a synchronous call would do. The synchronous equivalent would be this:
Private Sub Form3_Load(sender As Object, e As EventArgs) Handles MyBase.Load
DataGridView1.DataSource = GetData()
End Sub
Private Function GetData() As DataTable
Dim table As New DataTable
'Populate table here.
Return table
End Function
Try making the process asynchronous, it is in my understanding that the timer tick is already asynchronous but in the form1, you could use could have that code inside an Task
Me.Hide
Task.Run(Function() Form2.Show())
Form3.Show()
I never reached this far on vb.net since i started to program on c# but this should do the trick
I'm learning on how to use Async and Await. I wrote this code to fetch a website and write it on a RichTextBox in every one second. The problem is, if there's a network slowdown, the fetching of the page slows down too and the UI freezes briefly. This is the code:
Imports System.Net
Imports System.IO
Public Class Form1
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Timer1.Start()
End Sub
Private Async Function FetchData(uri As String) As Task(Of String)
Using wb As New WebClient
Dim stream As New StreamReader(wb.OpenRead(uri))
Return Await stream.ReadToEndAsync
End Using
End Function
Private Async Sub Timer1_Tick(sender As Object, e As EventArgs) Handles Timer1.Tick
RichTextBox1.Text = Await FetchData("http://someurl")
End Sub
End Class
My program was opening a different form to what I wanted it to. The answers solved it.
Basically I wanted to stop a form opening when the program started, but when it opened manually (on a button press), it updated the data. The second part of the problem has not been solved, but the first part has been.
You can try something like this:
Public Class HomeForm
Private WithEvents m_DataChangeForm As DataChangeForm
Private Sub HomeForm_Load(sender As Object, e As EventArgs) Handles MyBase.Load
m_DataChangeForm = New DataChangeForm()
m_DataChangeForm.Show()
End Sub
Private Sub OnDataSourceChanged(sender As Object, args As EventArgs) Handles m_DataChangeForm.OnDataSourceChanged
MessageBox.Show("Data source changed!")
End Sub
End Class
Public Class DataChangeForm
Inherits Form
Public Event OnDataSourceChanged(sender As Object, args As EventArgs)
Private WithEvents m_Button As Button
Public Sub New()
m_Button = New Button()
m_Button.Text = "Change"
m_Button.Parent = Me
End Sub
Public Sub buttonClick(sender As Object, args As EventArgs) Handles m_Button.Click
RaiseEvent OnDataSourceChanged(sender, args)
Me.Close()
End Sub
End Class
Reason your form is displayed before HomeForm is becaouse you call ShowDialog, it blocks until DataChangeForm is closed.
You should move your code from "Load" to "Shown" Event.
Private Sub Homefrm_Shown(sender As Object, e As EventArgs) Handles Me.Shown
Using fp = New dataChangefrm(m_database)
If fp.ShowDialog() = DialogResult.OK Then
uwgHome.DataSource = Nothing
loadData()
End If
End Using
Me.Location = New Point(0, 0)
loadData()
End Sub
Please have a look on the Handle in the first line. It depends on your Project.