webView2: inject jQuery in the current page - winforms

Starting to play with webView2 in Winforms Net 6.
Is there a way to inject the jQuery library in the current page (which I don't control), so that I can later use jQuery when passing scripts to the page through ExecuteScriptAsync or add event listeners.

The AddScriptToExecuteOnDocumentCreatedAsyncevent is what you need. You can find the documentation here: CoreWebView2.AddScriptToExecuteOnDocumentCreatedAsync(String)
First: Download jQuery and save it to disk.
Next, subscribe to the CoreWebView2InitializationCompleted event:
private async void WebView21_CoreWebView2InitializationCompleted(object sender, Microsoft.Web.WebView2.Core.CoreWebView2InitializationCompletedEventArgs e)
{
string script = await File.ReadAllTextAsync(#"C:\path\jquery.js");
await webView21.CoreWebView2.AddScriptToExecuteOnDocumentCreatedAsync(script);
}
Now you will be able to call jquery functions using await ExecuteScriptAsync().

Related

Why does async method block MVVM Light Relay Command

I'm new to async and need to consume an API that has it. I've read I should "go async all the way" back the UI command. So far I've propagated async back to my view model.
The code below blocks the Upload button in my UI. Is this because the RelayCommand's implementation calls it using await?
// In the ViewModel:
public MyViewModel()
{
...
UploadRelayCommand = new RelayCommand(mUpload, () => CanUpload);
...
}
private async void mUpload()
{
...
await mModel.Upload();
...
}
// In the model:
public async Task UploadToDatabase()
{
...
projectToUse = await api.CreateProjectAsync(ProjectName);
...
}
// In the API
public async Task<Project> CreateProjectAsync(Project project){}
Update: Sven's comment led me to find that CreateProjectAsync was running in a simulation mode that synchronously wrote to memory. When I wrapped that end code in Task.Run, it no longer blocked my Upload button. When not running in simulation mode, the API natively makes asynchronous calls to interact with a web server, so those also don't block.
Thanks.
The await itself will not block your UI. It is more likely that your Upload() method does not do any real asynchronous work.
(As Jim suggested, Task.Run() can be used in such a case. It will use the thread pool to run the operation in the background. Generally speaking, for IO-bound operations like uploads/downloads you should check if your API supports asynchronous calls natively. If such an implementation exists, it may make more efficient use of system resources than using a thread.)

Which strategy to employ for long to load pages?

Trying to address the following issue :
To provide a progress indicator that will be shown until the navigation target finishes loading. The target being navigated to can take up to 30 seconds for loading as there are images being fetched from different sources on the Internet.
Problem lies on handling such task using events of NavigationService or Page as they are always raised before the Page has loaded its content which is done inside the Loaded event. The loading process is asynchronous for not blocking UI and as such, cannot be moved to the constructor as it cannot be marked as async.
Is there an efficient pattern for addressing such problem ?
One option here is to have the constructor create a method that returns a Task<T>, and then store this in a class member.
Your Loaded event can then use await on the created task to extract out the data and display it asynchronously.
This will look something like:
Task<YourData> initialDataLoadTask;
// In your constructor:
YourPage()
{
this.InitializeComponent();
// Use async method here to fetch data, but do NOT use await
this.initialDataLoadTask = FetchDataAsync();
this.Loaded += this.OnLoaded;
}
private async void OnLoaded(object sender, RoutedEventArgs e)
{
// Use await to pull out the data asynchronously now...
var data = await this.initialDataLoadTask;
// Display data as needed...
}
This allows the entire chain to be asynchronous, with the constructor still initializing the fetch of the data (so it comes in as fast as possible).

Wicket - Inter-session communication or Create new Request(Cycle) manually

I have some wicket panel store in a static Hashmap from different sessions, i want to do some like if some panel notifies the map, then the map notifies all other panel.
for example:
public class PanelMap{
private static Map<Long, List<MyPanel>> map = new HashMap<Long, List<MyPanel>>();
public static void subscribe(Long id, MyPanel panel){
if (!map.containsKey(id)){
map.put(id, new ArrayList<MyPanel>());
}
map.get(id).add(panel);
}
}
public static void notify(Long id, String notification){
if (map.containsKey(id)){
List<MyPanel> panels = map.get(id);
for(MyPanel panel : panels){
panel.newNotification(notification);
}
}
}
}
In Panel, newNotification(String notification) i want to send request to server and refresh my panel in browser.
public void String newNotification(String notification){
// do some business logic depends on notification
onMyRequest();
}
i've made some search among wicket behavior source files and about i found in AbstractDefaultAjaxBehavior i tried to make my own onRequest method inside my wicket panel as follows
private void onMyRequest(){
AjaxRequestTarget target = ((WebApplication)getApplication()).newAjaxRequestTarget(getPage());
target.add( _some_wicket_components_ );
RequestCycle.get().scheduleRequestHandlerAfterCurrent(target);
}
but all i did is some Ajax error in Wicket Ajax Debug about
Wicket.Ajax.Call.processComponent: Component with id _containerdiv_ was not found while trying to perform markup update.
ERROR: Cannot find element with id: _someComponentIdOnPanel_
(those components are exist)
How could i send my own request to server (or how can i get valid AjaxRequestTarget to update my components? )
Update: I need inter-session communication.
To update panels on different user's sessions, you obviously can't use the current AjaxRequestTarget as this in a way represents a single communication between the server and the requesting user of which another user's Browser has no way of knowing. (Very very basically spoken)
You could either use an AjaxSelfUpdatingTimerBehavior to poll for updates. This would generate new AjaxRequestTarget for every user at regular intervals that you can use to attach changed panels to. It's a very basic and simple implementation that will most likely impact your systems performance and generate quite some traffic.
The other way would be to use something like Atmosphere, which is supported by Wicket-Atmosphere (quickstart can be found here) and has some examples over at the wicket-library.com, but that's all I know about this.
Use Wicket event bus system. Have a look to the "Wicket events infrastructure" chapter of the free Wicket guide.
First you need to create one class to encapsulate the notification and the AjaxRequestTarget and pass them using the events infrastructure.
private class Notification {
private String message;
private AjaxRequestTarget target;
... constructor, getters, setters...
}
Then the panels that want to recive the event have to override onEvent method, something like this:
public void onEvent(IEvent<?> event) {
if (event.getPayload() instanceof Notification) {
Notification notification = (Notification) event.getPayload();
... do whatever you want before updating the panel ...
// Update the panel
notification.getTarget().add(this);
}
}
All the components will recive all the events that are send using Wicket events infrastructure. So you can send the event from any other panel using one method like this
protected void sendMessage(String message, AjaxRequestTarget target) {
send(getSession(), Broadcast.BREADTH, new Notification(message, target));
}
Remember that if you want to update the components using AJAX, you need to set setOutputMarkupId(true). And if it's a component that can be hidden and you want to make it visible using AJAX, then you need to set setOutputMarkupPlaceholderTag(true).

Getting Progress Updates using HttpWebRequest and TPL (Tasks)

I would like to track the progress of a download taking place on a separate thread. I know that System.Net.WebClient has a DownloadStringAsync method, but it doesn't work directly with the new TPL types (TaskFactory, Task, etc.).
Can progress be tracked using the HttpRequest and HttpResponse classes?
What's the best class for tracking progress? The less overhead the better.
Are there times when the size of the response is unknown, aka, progress can't be tracked?
What's the best way to synchronize with the UI thread whenever progress is made?
Most examples show Tasks updating the UI only after the entire task is complete. These examples use continuations taking a UI synchronization context that avoids needing to work with a Dispatcher directly.
The idea is to show a grid view (in WPF) with all the downloads with progress bars. I am going to adding new rows and updating progress bars all the time. I'm trying to avoid turning this code into a mess.
DownloadStringAsync and the other event methods work very well with TPL in .NET 4.0 (check for EAP and TPL). In general, TPL does support event async programming through the TaskCompletionSource. The Begin/EndXXX model (APM) is supported through the Task.FromAsync method. You can find a detailed description TPL and Traditional .NET Asynchronous Programming.
The ParallelExtensionExtras library has a set of WebClient extensions methods like DownloadStringTask that return a task which completes when the appropriate event is fired.
The following code will create a Task that will complete when download finishes:
public Task<string> DownloadStringTask(WebClient client,Uri uri)
{
var tcs = new TaskCompletionSource<string>();
client.DownloadStringCompleted += (o, a) => tcs.SetResult(a.Result);
client.DownloadStringAsync(uri);
return tcs.Task;
}
As for updating the UI, you can easily use the DownloadProgressChanged event to provide feedback,eg:
using (var client = new WebClient())
{
client.DownloadProgressChanged += (o, a) => Console.WriteLine("{0}",a.ProgressPercentage);
var task = DownloadStringTask(client,new Uri("http://www.stackoverflow.com"));
var write=task.ContinueWith(t => Console.WriteLine("Got {0} chars", t.Result.Length));
write.Wait();
Console.ReadKey();
}
If you use data binding to provide the progress values to your progress bars, you can just update the progress value properties. If you update the progress bars directly (not a good idea), you will have to marshal the call to the UI thread using the progress bar's dispatcher, eg. like this
void UpdateProgress(int percent)
{
if (progressBar1.CheckAccess())
progressBar1.Value = percent;
else
{
progressBar1.Dispatcher.Invoke(new Action(()=>UpdateProgress(percent)));
}
}
....
client.DownloadProgressChanged += (o, a) => UpdateProgress(a.ProgressPercentage);

Asynchronous Callback method is never called to give results from web service from Silverlight

I'm calling off asynchronously to a web service (Amazon Web Services) from a Silverlight app and my callback method is never actually triggered after I start the asynchronous call.
I've set up another web service proxy in a console app, and I'm able to make a synchronous call and get a response using the same arguments without any issues.
Am I possibly having problems with the fact that this is called from within a browser? I'm not sure where to start, since I don't get a response at all, much less an error.
Below is the code I'm using:
private void btnQueryAmazon_Click(object sender, RoutedEventArgs e)
{
if (!string.IsNullOrEmpty(txtQuery.Text))
{
ItemSearch search = new ItemSearch();
/// set authentication and search parameters
AmazonService.AWSECommerceServicePortTypeClient service = new AmazonService.AWSECommerceServicePortTypeClient();
service.ItemLookupCompleted += new EventHandler<AmazonService.ItemLookupCompletedEventArgs>(service_ItemLookupCompleted);
service.ItemSearchAsync(search);
}
}
void service_ItemLookupCompleted(object sender, AmazonService.ItemLookupCompletedEventArgs e)
{
txtError.Text = e.Result.Items.Count().ToString();
grdItems.ItemsSource = e.Result.Items;
}
Well, there's your problem ;)
It looks like you're calling the ItemSearch method on the service, but you're wiring up and handling the ItemLookup method.
I do it all the time.

Resources