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!
Related
I don't understand much about Async, but I get no errors when running this, and still I don't get an image in C:\temp\test.jpg - what am I doing wrong?
Private Sub WebView22_Click(sender As Object, e As EventArgs) Handles WebView22.Click
grabpic()
End Sub
Public Async Function grabpic() As Threading.Tasks.Task
Dim ms As System.IO.Stream
Await (WebView22.CoreWebView2.CapturePreviewAsync(CoreWebView2CapturePreviewImageFormat.Jpeg, ms))
Dim file As New System.IO.FileStream("C:\temp\test.jpg", IO.FileMode.Create)
ms.CopyTo(file)
ms.Close()
End Function
There are a few problems with your code:
You're not awaiting the graphics() function, and therefore exceptions are being silently swallowed. Also, I'm not exactly sure what triggers the Click event of a WebView2 control (assuming it can actually be triggered). So, instead, you probably should test using a button click, for example.
You're not initializing ms at all, which will cause a NullReferenceException. You probably wanted to create an instance of MemoryStream.
You're calling ms.CopyTo after writing to the stream. At that point, the current position will be at the end of the stream and nothing will be written to file. You either need to set the position at the beginning (i.e., ms.Seek(0, SeekOrigin.Begin) or use ms.WriteTo() instead.
With that, the graphics() method would look something like this:
Public Async Function grabpic() As Task
Using ms As New IO.MemoryStream()
Await WebView22.CoreWebView2.CapturePreviewAsync(
CoreWebView2CapturePreviewImageFormat.Jpeg, ms)
Using file As New IO.FileStream("C:\temp\test.jpg", IO.FileMode.Create)
ms.Seek(0, IO.SeekOrigin.Begin)
ms.CopyTo(file)
End Using
End Using
End Function
However, you don't really need a MemoryStream in this case. You could just write directly to the FileStream. Here's what the final code should look like:
Private Async Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Await grabpic()
End Sub
Public Async Function grabpic() As Task
Using file As New IO.FileStream("C:\temp\test.jpg", IO.FileMode.Create)
Await WebView22.CoreWebView2.CapturePreviewAsync(
CoreWebView2CapturePreviewImageFormat.Jpeg, file)
End Using
End Function
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?
I created a simple hello world program to download data from the web.
Private Sub cmdSurf_Click(sender As Object, e As EventArgs) Handles cmdSurf.Click
Dim wb As New System.Net.WebClient
Dim uri1 = New Uri(TextBox1.Text)
Dim str = wb.DownloadString(uri1)
TextBox2.Text = str
End Sub
It's pretty simple right. I use a WebClient object to download a string syncrhonously and then I display the result in a TextBox.
Now, I want to do so asynchronously.
Basically, after I download the URI I do something else.
Then after the download is finished, I am doing what it should be.
So I did
Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
Dim wb As New System.Net.WebClient
Dim uri1 = New Uri(TextBox1.Text)
wb.DownloadStringAsync(uri1)
TextBox2.Text = ""
End Sub
It turns out DownloadStringAsync(uri1) is a Sub, so it doesn't return anything.
So, well, what should be displayed in TextBox2 then? What am I missing?
Update: I realized that I should have used DownloadStringAsyncTask().
So I did this:
Private Async Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
Await downloadStringAndAssignText()
TextBox2.Text = "I am downloading this. This text will change once the download is finished"
End Sub
Private Async Function downloadStringAndAssignText() As Task
Dim wb As New System.Net.WebClient
Dim uri1 = New Uri(TextBox1.Text)
Dim str = Await wb.DownloadStringTaskAsync(uri1)
TextBox2.Text = str
End Function
This is almost correct.
The thing is I want to do this properly so that
TextBox2.Text = "I am downloading this. This text will change once..."
is called BEFORE wb.DownloadStringTaskAsync(uri1) is finished.
Also I do not want warning. So how exactly should I do that?
You can set the text of your TextBox before you call the async method that downloads the string. When Await DownloadStringAndAssignText() is called, control will return to the calling Thread (the UI Thread), so the TextBox.Text is set and shown.
When the async method returns, you can assign to the same TextBox the returned string value.
Your method should return a value, the string it downloaded, not assign a value to a UI element that doesn't belong to this method (its responsibility is to download a string): here the return type is set to Task(Of String).
You can directly assign the return value of the async method to the same TextBox.Text property.
The WebClient object should be declared with a Using statement, so it's disposed of when the method completes.
You can directly return the result of WebClient.DownloadStringTaskAsync(), since this method returns a string, the same return type of your method.
Imports System.Net
Private Async Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
TextBox2.Text = "I am downloading..."
TextBox2.Text = Await DownloadStringAndAssignText(TextBox1.Text)
End Sub
Private Async Function DownloadStringAndAssignText(url As String) As Task(Of String)
Using wb As New WebClient()
Return Await wb.DownloadStringTaskAsync(New Uri(url))
End Using
End Function
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.