I'm trying to start two WebView2 browsers.
1 with arguments and 1 without arguments.
But i can't get it to work, i get a error with:
WebView_Browser2.CoreWebView2.Navigate("https://www.google.nl/")
Microsoft.Web.WebView2.WinForms.WebView2.CoreWebView2.get returned Nothing.
This is the code i have so far.
Private Async Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Await InitializeAsync()
End Sub
Private Async Function InitializeAsync() As Task
Dim opts As New CoreWebView2EnvironmentOptions With {.AdditionalBrowserArguments = "--kiosk-printing"}
Dim env = Await CoreWebView2Environment.CreateAsync(Nothing, Nothing, opts)
Await WebView_Browser1.EnsureCoreWebView2Async()
Await WebView_Browser2.EnsureCoreWebView2Async(env)
AddHandler WebView_Browser1.CoreWebView2.FrameNavigationCompleted, AddressOf WebView_Browser1_FrameNavigationCompleted
AddHandler WebView_Browser2.CoreWebView2.FrameNavigationCompleted, AddressOf WebView_Browser2_FrameNavigationCompleted
End Function
Private Sub WV_CoreWebView2Ready1(sender As Object, e As EventArgs) Handles WebView_Browser1.CoreWebView2InitializationCompleted
WebView_Browser1.CoreWebView2.Navigate("https://www.google.nl/")
End Sub
Private Sub WV_CoreWebView2Ready2(sender As Object, e As EventArgs) Handles WebView_Browser2.CoreWebView2InitializationCompleted
WebView_Browser2.CoreWebView2.Navigate("https://www.google.nl/")
End Sub
If i start both Await WebView_Browser1.EnsureCoreWebView2Async() with 'env' arguments they work.
But i would like to start 1 with and without arguments.
Thank you for your time!
UPDATE
When i create two seperate CoreWebView2Environment.CreateAsync using no arguments i get the same error.
If i change the Arguments to something else i get the error.
But when i use the exact same arguments .AdditionalBrowserArguments = "--kiosk-printing"} it works.
Maybe you can't start two WebView2 browsers on one form with different or one with and one without arguments?
Related
I'm trying to print using the Webview2 control.
I can print with a dialog using the follwing code:
Await WebView_Browser1.CoreWebView2.ExecuteScriptAsync("window.print();")
But i would like to print silently.
With Edge we can use the parameter --Kiosk-printing like in Chrome.
This is the property i'm looking for: CoreWebView2EnvironmentOptions.AdditionalBrowserArguments
But how can i start the Webview2 control using these parameters?
I've looked up some examples like this: Link
But converting them to VB.NET using https://converter.telerik.com/ doesn't seem to work.
Is it possible using WinForms?
Thank you for your time!
This is what i have so far:
Dim opts As New CoreWebView2EnvironmentOptions With {.AdditionalBrowserArguments = "--kiosk-printing"}
Dim env As CoreWebView2Environment = Await CoreWebView2Environment.CreateAsync(Nothing, Nothing, Nothing), opts)
Dim tsk As Task = WebView_Browser1.EnsureCoreWebView2Async(env)
But is giving me the error: 'Await' can only be used within an Async method. Consider marking this method with the 'Async' modifier and changing its return type to 'Task'.
And i dont know how to use this could somebody please give me an example?
UPDATE
This is the current code that i have but it's not working.
Private Async Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Await InitializeAsync()
End Sub
Private Async Function InitializeAsync() As Task
Dim opts As New CoreWebView2EnvironmentOptions With {.AdditionalBrowserArguments = "--kiosk-printing"}
Dim env = Await CoreWebView2Environment.CreateAsync(Nothing, Nothing, opts)
AddHandler WebView_Browser1.CoreWebView2.FrameNavigationCompleted, AddressOf WebView_Browser1_FrameNavigationCompleted
Await WebView_Browser1.EnsureCoreWebView2Async(env)
End Function
Private Sub WV_CoreWebView2Ready1(sender As Object, e As EventArgs) Handles WebView_Browser1.CoreWebView2InitializationCompleted
WebView_Browser1.CoreWebView2.Navigate("https://www.google.com")
End Sub
I don't know how to use the arguments with my WebView_Browser1.
Can somebody please give me an example of how do deal with this?
Thank you!
I have a form used to display options about processes.
When options are applyed :
frmOptions
For Each ltvi As ListViewItem In ltvProcesses.CheckedItems
Dim proc As Process = CType(ltvi.Tag, Process)
targeted_processes.Add(proc)
AddHandler proc.Exited, AddressOf frmAET.a_target_process_has_been_exited
proc.EnableRaisingEvents = True
Next
And in a tools module :
Public Sub a_target_process_has_been_exited(sender As Object, e As EventArgs)
frmAET.btnStatus.ForeColor = Color.Red
msgbox("OK")
End Sub
And... the messagebox displays its message but the color doesn't change.
After some tries, the problem is when a_target_process_has_been_exited is actived by the handler.
If I do this (Button1 belongs to frmAET, like btnStatus) :
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
a_target_process_has_been_exited()
End Sub
It works ! But not when I really want (when a process is ended).
So, the problem is when the sub is called by the process end event.
And when I try to specify this (maybe a frmAET's sub can modify its controls) :
AddHandler leproc.Exited, AddressOf frmAET.a_target_process_has_been_exited
Error : Reference to a non-shared member requires an objet reference
Could you help me ?
Your AddHandler seems to use AddressOf frmAET.a_target_process_has_been_exited, that means method in frmAET form itself. Not tools module as you stated.
Let's consider your frmOptions is correct and frmAET is containing this (with removed explicit reference to frmAET, since it's local)
Public Sub a_target_process_has_been_exited(sender As Object, e As EventArgs)
btnStatus.ForeColor = Color.Red
MsgBox("OK")
End Sub
As comments already explained, your event handler is called in another thread and you need to sync yourself to main UI thread. For example like this:
Public Sub a_target_process_has_been_exited(sender As Object, e As EventArgs)
Me.BeginInvoke(Sub() HandleProcessExit())
End Sub
Public Sub HandleProcessExit
btnStatus.ForeColor = Color.Red
MsgBox("OK")
End Sub
This version will block main UI thread until you click on the MsgBox button.
You should add some Try/Catch block. Exception in another threads are difficult to detect otherwise.
This code depends on implicit form instances that VB.NET creates for you. I expect your frmAET is actually My.Forms.frmAET instance to make this work.
I need to display a form for some amount of time - basically a "please wait, loading" form with progress bar. When certain operation completes, I want this window to disappear. Here's my try at it:
If IsNothing(mlLabels) Or mblnIsLoading Then Exit Sub
If mstrPrinterA.Equals(Me.cmbPrinters.Text, StringComparison.OrdinalIgnoreCase) Then
Exit Sub
End If
Dim th As New Threading.Thread(AddressOf WaitPrinter)
th.Start()
If mlLabels.IsPrinterOnLine(Me.cmbPrinters.Text) Then
Me.cmbPrinters.BackColor = Drawing.Color.Green
Else
Me.cmbPrinters.BackColor = Drawing.Color.Red
End If
th.Abort()
Do While th.IsAlive
Loop
th = Nothing
mstrPrinterA = Me.cmbPrinters.Text
Private Sub WaitPrinter()
Dim fw As New FormWaiting
fw.ShowDialog()
fw = Nothing
End Sub
However, I then read that using Thread.Start() and Thread.Abort() is not considered a good practice. Is there another way I can do that?
Here is a simple example of what I described in my comment above. Create a WinForms project with two forms, adding a Button to Form1 and a BackgroundWorker to Form2. Add this code to Form1:
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
'Display a dialogue while the specified method is executed on a secondary thread.
Dim dialogue As New Form2(New Action(Of Integer)(AddressOf Pause), New Object() {5})
dialogue.ShowDialog()
MessageBox.Show("Work complete!")
End Sub
Private Sub Pause(period As Integer)
'Pause for the specified number of seconds.
Threading.Thread.Sleep(period * 1000)
End Sub
and this code to Form2:
Private ReadOnly method As [Delegate]
Private ReadOnly args As Object()
Public Sub New(method As [Delegate], args As Object())
' This call is required by the designer.
InitializeComponent()
' Add any initialization after the InitializeComponent() call.
Me.method = method
Me.args = args
End Sub
Private Sub Form2_Load(sender As Object, e As EventArgs) Handles MyBase.Load
BackgroundWorker1.RunWorkerAsync()
End Sub
Private Sub BackgroundWorker1_DoWork(sender As Object, e As ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork
'Execute the specified method with the specified arguments.
method.DynamicInvoke(args)
End Sub
Private Sub BackgroundWorker1_RunWorkerCompleted(sender As Object, e As ComponentModel.RunWorkerCompletedEventArgs) Handles BackgroundWorker1.RunWorkerCompleted
'Close the dialogue when the work is complete.
Close()
End Sub
Run the project and click the Button on the startup form. You'll see the dialogue displayed while the work is executed and then disappear when it's done. The dialogue is written in such a way that it can be used to invoke any method with any arguments. It's the caller that gets to define what the work to be performed is.
In this particular case, the "work" is a simple sleep but you can put anything you like in there. Just note that it is executed on a secondary thread so no direct interaction with the UI is allowed. If you need UI interaction then that could be accomplished but you'd need slightly more complex code. Note that the code as it is also does not allow for returning a result from the executed method, but you could support that fairly easily too.
Consider this example:
Private Async Function ComputeText() As Task(Of String)
Dim result As String = Await Task.Run(Function()
'do whatever
Return "Done"
End Function)
Return result
End Function
Now could anyone tell me whether there is diffrence between those two button event handlers? From my perspective this is the same but 'better approach' is to use the 1st one, am i right?
'1st:
Private Async Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
LabelCounter.Text = "Running"
Dim value As string = Await ComputeText()
LabelCounter.Text = value
End Sub
'2nd:
Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
LabelCounter.Text = "Running"
Dim task = ComputeText().ContinueWith(
Sub(x)
LabelCounter.Invoke(
Sub()
LabelCounter.Text = x.Result
End Sub)
End Sub)
End Sub
Await does use ContinueWith under the hood. However, ContinueWith is a dangerous API and should not be used directly. As I describe in my post on task continuations:
ContinueWith has an unfortunate choice of default sceduler; it uses TaskScheduler.Current rather than TaskScheduler.Default.
ContinueWith does not understand asynchronous delegates, so it will return an extra task wrapper that you have to unwrap (by calling Unwrap).
ContinueWith does not have appropriate default option arguments for asynchronous code (e.g., TaskContinuationOptions.DenyChildAttach and TaskContinuationOptions.ExecuteSynchronously).
For these reasons, you should use Await instead of ContinueWith for asynchronous code.
Also, it's shorter and prettier. :)
I have a small VB.Net project with link to sql using web service (SOAP).
I have to make sure that all forms are totally responsive no matter what, and it's working pretty well. My only problem is on loading the application!
The main start-up form has only single line of code:
Private Async Sub frmMain_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Await objWebService.GetCurrentSessionsAsync
End Sub
But while this "awaitable" code is being executed the form is unresponsive, frozen and wait cursor is displayed.
Any idea on what might be causing this issue and how to handle it?
In regard to your answer, the code can be much cleaner if you don't combine different programming patterns, check this out:
Private Async Sub frmMain_Load(sender As Object,
e As EventArgs) Handles MyBase.Load
Dim res = Await GetCurrentSessionsAsync()
End Sub
Private Async Function GetCurrentSessionsAsync() As Task(Of com.services.Server)
Try
Return Await Task.Factory.
StartNew(Function() objWebService.GetCurrentSessions)
Catch ex As Exception
Glob.ErrorLog("GetCurrentSessions", ex, True)
Return New com.services.Server
End Try
End Function
References:
try-catch (C# Reference)
Async Return Types (C# and Visual Basic)
The key problem is that Async does not magically make your method asynchronous. It only lets compiler know that your method will have Await keywords, and that the code needs to be converted into a state machine. Any code that is not awaited is executed synchronously, even if the method is marked as Async. Consider the following example:
Private Async Sub Form1_Load(sender As Object,
e As EventArgs) Handles MyBase.Load
Await LongRunning1() 'opens the form, then asynchronously changes
'Text property after 2 seconds
End Sub
Private Async Function LongRunning1() As Task
Await Task.Factory.StartNew(Sub() Threading.Thread.Sleep(2000))
Me.Text = "Finished LongRunning1"
End Function
Here a long running process, Thread.Sleep as an example, is wrapped into a Task, and there is an Await keyword. It tells the compiler to wait for the statements inside the task to finish, before executing the next line. Without the Await, the Text property would be set immediately.
Now suppose you have some long running synchronous code in your Async method:
Private Async Sub Form1_Load(sender As Object,
e As EventArgs) Handles MyBase.Load
Await LongRunning2() 'synchronously waits 2 seconds, opens the form,
'then asynchronously changes Text property after 2 seconds
End Sub
Private Async Function LongRunning2() As Task
Threading.Thread.Sleep(2000)
Await LongRunning1()
Me.Text = "Finished LongRunning2"
End Function
Notice in this case it synchronously waits for the Thread.Sleep to finish, so for the end user you app appears as hanging. Bottom line is - you have to know which method calls can be long running, and wrap them into a task based await model. Otherwise you may be seeing the problem you are seeing.
If this sounds too complicated, you can fire up a background worker (.NET 2.0+), or use TPL (.NET 4.0+) to start a task. If you wish to go into lower level, threading is available since .NET 1.1. Then display some wait/progress window/overlay on top of the form/control, for which the functionality is not yet available. Check these out:
Loading data from DB asynchronously in win forms
Await async with event handler
Thanks to #PanagiotisKanavos for pointing me in the right direction.
So here what it is (I have to say that the answer of Neolisk and Panagiotis led me to the solution):
What made my loading form unresponsive is what appeared to be a bug in web services, only the first call of my web service would produce this issue. So If the first call was made after form load, on another event, I would face same problem.
To fix this, I changed the way I call my first method through web service using TaskCompletionSource variable, and calling my first method using Thread. I'll post my before/after code to be sure I delivered my fix clearly.
Before:
Private Async Sub frmMain_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Dim res = Await objWebService.GetCurrentSessionsAsync
End Sub
After:
Private Async Sub frmMain_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Dim res = Await GetCurrentSessionsAsync()
End Sub
Dim _tcServer As New TaskCompletionSource(Of MyProject.com.services.Server)
Private Async Function GetCurrentSessionsAsync() As Task(Of com.services.Server)
Try
Dim th As New System.Threading.Thread(AddressOf GetCurrentSessions)
th.Start()
Return Await _tcServer.Task
Catch ex As Exception
Return New MyProject.com.services.Server
End Try
End Function
Private Sub GetCurrentSessions()
Try
Dim res = objWebService.GetCurrentSessions
_tcServer.SetResult(res)
Catch ex As Exception
Glob.ErrorLog("GetCurrentSessions", ex, True)
End Try
End Sub
I hope this can help others in the future.
Thank you.