WebView2 download event is not firing - Windows forms application - path

I create windows forms application using WebView2 control, in there I add DownloadStarting event for select the downloading path to the user but unfortunately DownloadStarting event not firing.
private async void InitBrowser()
{
var environment = await CoreWebView2Environment.CreateAsync(null, #"C:\Temp", null);
webView.CoreWebView2InitializationCompleted += WebView_CoreWebView2InitializationCompleted;
await webView.EnsureCoreWebView2Async(environment);
webView.NavigationCompleted += WebView_NavigationCompleted;
webView.Source = new Uri("myurl");
}
private void WebView_CoreWebView2InitializationCompleted(object sender, CoreWebView2InitializationCompletedEventArgs e)
{
webView.CoreWebView2.DownloadStarting += CoreWebView2_DownloadStarting;
}
private void CoreWebView2_DownloadStarting1(object sender, CoreWebView2DownloadStartingEventArgs e)
{
downloadOperation = e.DownloadOperation;
e.Handled = true;
e.ResultFilePath = Savefile();
}

Related

Call to Dropbox API Client.Files.DownloadAsync does not return metadata when call from an instance of TimerCallback

I’ve got a mobile crossplatform Xamarin.Forms project in which I try to download a file from a Dropbox repository at startup. It’s a tiny json file of less than 50kB. The code operating the Dropbox API call is shared between my Android and my iOS projects, and my Android implementation works as intended. It’s a Task method which I’ll call the downloader here for convenience.
UPDATED: With the iOS version, I can download the file successfully only when calling my downloader’s launcher (which is a also Task) directly from the BackgroundSynchronizer.Launch() method of my only AppDelegate, but not when delegating this call using a timer to call my downloader through a TimerCallback which calls an EventHandler at recurring times.
I can’t figure out why.
The downloader:
public class DropboxStorage : IDistantStoreService
{
private string oAuthToken;
private DropboxClientConfig clientConfig;
private Logger logger = new Logger
(DependencyService.Get<ILoggingBackend>());
public DropboxStorage()
{
var httpClient = new HttpClient(new NativeMessageHandler());
clientConfig = new DropboxClientConfig
{
HttpClient = httpClient
};
}
public async Task SetConnection()
{
await GetAccessToken();
}
public async Task<Stream> DownloadFile(string distantUri)
{
logger.Info("Dropbox downloader called.");
try
{
await SetConnection();
using var client = new DropboxClient(oAuthToken, clientConfig);
var downloadArg = new DownloadArg(distantUri);
var metadata = await client.Files.DownloadAsync(downloadArg);
var stream = metadata?.GetContentAsStreamAsync();
return await stream;
}
catch (Exception ex)
{
logger.Error(ex);
}
return null;
}
UPDATED: The AppDelegate:
using Foundation;
using UIKit;
namespace Izibio.iOS
{
// The UIApplicationDelegate for the application. This class is responsible for launching the
// User Interface of the application, as well as listening (and optionally responding) to
// application events from iOS.
[Register("AppDelegate")]
public partial class AppDelegate : global::Xamarin.Forms.Platform.iOS.FormsApplicationDelegate
{
private BackgroundSynchronizer synchronizer = new BackgroundSynchronizer();
//
// This method is invoked when the application has loaded and is ready to run. In this
// method you should instantiate the window, load the UI into it and then make the window
// visible.
//
// You have 17 seconds to return from this method, or iOS will terminate your application.
//
public override bool FinishedLaunching(UIApplication app, NSDictionary options)
{
global::Xamarin.Forms.Forms.Init();
LoadApplication(new App());
return base.FinishedLaunching(app, options);
}
public override void OnActivated(UIApplication uiApplication)
{
synchronizer.Launch();
base.OnActivated(uiApplication);
}
}
}
EDIT: The intermediary class (which embeds the DownloadProducts function):
public static class DropboxNetworkRequests
{
public static async Task DownloadProducts(IDistantStoreService distantStorage,
IStoreService localStorage)
{
try
{
var productsFileName = Path.GetFileName(Globals.ProductsFile);
var storeDirectory = $"/{Globals.StoreId}_products";
var productsFileUri = Path.Combine(storeDirectory, productsFileName);
var stream = await distantStorage.DownloadFile(productsFileUri);
if (stream != null)
{
await localStorage.Save(stream, productsFileUri);
}
else
{
var logger = GetLogger();
logger.Info($"No file with the uri ’{productsFileUri}’ could " +
$"have been downloaded.");
}
}
catch (Exception ex)
{
var logger = GetLogger();
logger.Error(ex);
}
}
private static Logger GetLogger()
{
var loggingBackend = DependencyService.Get<ILoggingBackend>();
return new Logger(loggingBackend);
}
}
UPDATED: And the failing launcher class (the commented TriggerNetworkOperations(this, EventArgs.Empty);
in the Launch method succeeds in downloading the file) :
public class BackgroundSynchronizer
{
private bool isDownloadRunning;
private IDistantStoreService distantStorage;
private IStoreService localStorage;
private Timer timer;
public event EventHandler SynchronizationRequested;
public BackgroundSynchronizer()
{
Forms.Init();
isDownloadRunning = false;
distantStorage = DependencyService.Get<IDistantStoreService>();
localStorage = DependencyService.Get<IStoreService>();
Connectivity.ConnectivityChanged += TriggerNetworkOperations;
SynchronizationRequested += TriggerNetworkOperations;
}
public void Launch()
{
try
{
var millisecondsInterval = Globals.AutoDownloadMillisecondsInterval;
var callback = new TimerCallback(SynchronizationCallback);
timer = new Timer(callback, this, 0, 0);
timer.Change(0, millisecondsInterval);
//TriggerNetworkOperations(this, EventArgs.Empty);
}
catch (Exception ex)
{
throw ex;
}
}
protected virtual void OnSynchronizationRequested(object sender, EventArgs e)
{
SynchronizationRequested?.Invoke(sender, e);
}
private async void TriggerNetworkOperations(object sender, ConnectivityChangedEventArgs e)
{
if ((e.NetworkAccess == NetworkAccess.Internet) && !isDownloadRunning)
{
await DownloadProducts(sender);
}
}
private async void TriggerNetworkOperations(object sender, EventArgs e)
{
if (!isDownloadRunning)
{
await DownloadProducts(sender);
}
}
private void SynchronizationCallback(object state)
{
SynchronizationRequested(state, EventArgs.Empty);
}
private async Task DownloadProducts(object sender)
{
var instance = (BackgroundSynchronizer)sender;
//Anti-reentrance assignments commented for debugging purposes
//isDownloadRunning = true;
await DropboxNetworkRequests.DownloadProducts(instance.distantStorage, instance.localStorage);
//isDownloadRunning = false;
}
}
I set a logging file to record my application behaviour when trying to download.
EDIT: Here are the messages I get when calling directly TriggerNetworkOperations from the Launch method:
2019-11-12 19:31:57.1758|INFO|xamarinLogger|iZiBio Mobile Launched
2019-11-12 19:31:57.4875|INFO|persistenceLogger|Dropbox downloader called.
2019-11-12 19:31:58.4810|INFO|persistenceLogger|Writing /MAZEDI_products/assortiment.json at /Users/dev3/Library/Developer/CoreSimulator/Devices/5BABB56B-9B42-4653-9D3E-3C60CFFD50A8/data/Containers/Data/Application/D6C517E9-3446-4916-AD8D-565F4C206AF2/Library/assortiment.json
EDIT: And are those I get when launching through the timer and its callback (with a 10 seconds interval for debugging purposes):
2019-11-12 19:34:05.5166|INFO|xamarinLogger|iZiBio Mobile Launched
2019-11-12 19:34:05.8149|INFO|persistenceLogger|Dropbox downloader called.
2019-11-12 19:34:15.8083|INFO|persistenceLogger|Dropbox downloader called.
2019-11-12 19:34:25.8087|INFO|persistenceLogger|Dropbox downloader called.
2019-11-12 19:34:35.8089|INFO|persistenceLogger|Dropbox downloader called.
EDIT: In this second scenario, the launched task event eventually gets cancelled by the OS:
2019-11-13 09:36:29.7359|ERROR|persistenceLogger|System.Threading.Tasks.TaskCanceledException: A task was canceled.
at ModernHttpClient.NativeMessageHandler.SendAsync (System.Net.Http.HttpRequestMessage request, System.Threading.CancellationToken cancellationToken) [0x002a5] in /Users/paul/code/paulcbetts/modernhttpclient/src/ModernHttpClient/iOS/NSUrlSessionHandler.cs:139
at System.Net.Http.HttpClient.SendAsyncWorker (System.Net.Http.HttpRequestMessage request, System.Net.Http.HttpCompletionOption completionOption, System.Threading.CancellationToken cancellationToken) [0x0009e] in /Users/builder/jenkins/workspace/xamarin-macios/xamarin-macios/external/mono/mcs/class/System.Net.Http/System.Net.Http/HttpClient.cs:281
at Dropbox.Api.DropboxRequestHandler.RequestJsonString (System.String host, System.String routeName, System.String auth, Dropbox.Api.DropboxRequestHandler+RouteStyle routeStyle, System.String requestArg, System.IO.Stream body) [0x0030f] in <8d8475f2111a4ae5850a1c1349c08d28>:0
at Dropbox.Api.DropboxRequestHandler.RequestJsonStringWithRetry (System.String host, System.String routeName, System.String auth, Dropbox.Api.DropboxRequestHandler+RouteStyle routeStyle, System.String requestArg, System.IO.Stream body) [0x000f6] in <8d8475f2111a4ae5850a1c1349c08d28>:0
at Dropbox.Api.DropboxRequestHandler.Dropbox.Api.Stone.ITransport.SendDownloadRequestAsync[TRequest,TResponse,TError] (TRequest request, System.String host, System.String route, System.String auth, Dropbox.Api.Stone.IEncoder`1[T] requestEncoder, Dropbox.Api.Stone.IDecoder`1[T] resposneDecoder, Dropbox.Api.Stone.IDecoder`1[T] errorDecoder) [0x000a5] in <8d8475f2111a4ae5850a1c1349c08d28>:0
at Izibio.Persistence.DropboxStorage.DownloadFile (System.String distantUri) [0x00105] in /Users/dev3/Virtual Machines.localized/shared/TRACAVRAC/izibio-mobile/Izibio/Izibio.Persistence/Services/DropboxStorage.cs:44
2019-11-13 09:36:29.7399|INFO|persistenceLogger|No file with the uri ’/******_products/assortiment.json’ could have been downloaded.
I’ll simply add a last observation: when debugging the DownloadFile Task from the BackgroundSynchronizer, I can reach the call to client.Files.DowloadAsync: var metadata = await client.Files.DownloadAsync(downloadArg);, but I won’t retrieve any return from this await statement.
OK, I finally found a way out of this by replacing the .NET timer by the iOS implementation (NSTimer).
My new code for the BackgroundSynchronizer class:
public class BackgroundSynchronizer
{
private bool isDownloadRunning;
private IDistantStoreService distantStorage;
private IStoreService localStorage;
private NSTimer timer;
public event EventHandler SynchronizationRequested;
public BackgroundSynchronizer()
{
Forms.Init();
isDownloadRunning = false;
distantStorage = DependencyService.Get<IDistantStoreService>();
localStorage = DependencyService.Get<IStoreService>();
Connectivity.ConnectivityChanged += TriggerNetworkOperations;
SynchronizationRequested += TriggerNetworkOperations;
}
public void Launch()
{
try
{
var seconds = Globals.AutoDownloadMillisecondsInterval / 1000;
var interval = new TimeSpan(0, 0, seconds);
var callback = new Action<NSTimer>(SynchronizationCallback);
StartTimer(interval, callback);
}
catch (Exception ex)
{
throw ex;
}
}
protected virtual void OnSynchronizationRequested(object sender, EventArgs e)
{
SynchronizationRequested?.Invoke(sender, e);
}
private async void TriggerNetworkOperations(object sender, ConnectivityChangedEventArgs e)
{
if ((e.NetworkAccess == NetworkAccess.Internet) && !isDownloadRunning)
{
await DownloadProducts();
}
}
private async void TriggerNetworkOperations(object sender, EventArgs e)
{
if (!isDownloadRunning)
{
await DownloadProducts();
}
}
private void SynchronizationCallback(object state)
{
SynchronizationRequested(state, EventArgs.Empty);
}
private async Task DownloadProducts()
{
isDownloadRunning = true;
await DropboxNetworkRequests.DownloadProducts(distantStorage, localStorage);
isDownloadRunning = false;
}
private void StartTimer(TimeSpan interval, Action<NSTimer> callback)
{
timer = NSTimer.CreateRepeatingTimer(interval, callback);
NSRunLoop.Main.AddTimer(timer, NSRunLoopMode.Common);
}
}
Which produces the following logging lines:
2019-11-13 14:00:58.2086|INFO|xamarinLogger|iZiBio Mobile Launched
2019-11-13 14:01:08.5378|INFO|persistenceLogger|Dropbox downloader called.
2019-11-13 14:01:09.5656|INFO|persistenceLogger|Writing /****_products/assortiment.json at /Users/dev3/Library/Developer/CoreSimulator/Devices/****/data/Containers/Data/Application/****/Library/assortiment.json
2019-11-13 14:01:18.5303|INFO|persistenceLogger|Dropbox downloader called.
2019-11-13 14:01:19.2375|INFO|persistenceLogger|Writing /****_products/assortiment.json at /Users/dev3/Library/Developer/CoreSimulator/Devices/****/data/Containers/Data/Application/****/Library/assortiment.json
But I’m still open to an enlighted explanation of the reason why both timers result in such different behaviours.

How to get returned result from function in Task.Run(Xamarin.Android)?

I have the following views in my activity:
private Button btn;
private TextView txtView;
I have the following button click event:
private async void Btn_Click(object sender, System.EventArgs e)
{
var mDialog = new ProgressDialog(this);
mDialog.SetMessage("Loading data...");
mDialog.SetCancelable(false);
mDialog.Show();
string str;
await Task.Run((() => str = Foo()));
// Alternatively
// await Task.Delay(10000);
mDialog.Dismiss();
txtView.Text = str;
}
And I also have the following method:
string Foo()
{
for (int i = 0; i < 10; i++)
{
Thread.Sleep(1000);
}
return "hello";
}
What I want is txtView.Text to be set to hello after the ProgressDialog is dismissed
Task.Run is not meant to be used like that what it does is Queues the specified work to run on the ThreadPool and returns a task or Task<TResult> handle for that work.
What you should do is make a method with a return type of Task<string> and then await that method
Then use that method to update your textview data
Solution:
You can set a dismiss listener by using SetOnDismissListener to do some work after the ProgressDialog is dismissed.
First, let your activity inherit from IDialogInterfaceOnDismissListener:
public class MainActivity : AppCompatActivity, IDialogInterfaceOnDismissListener
In your button click event, set your activity as the listener:
private async void Btn_Click(object sender, System.EventArgs e)
{
var mDialog = new ProgressDialog(this);
mDialog.SetMessage("Loading data...");
mDialog.SetCancelable(false);
//set your activity as the listener
mDialog.SetOnDismissListener(this);
mDialog.Show();
await Task.Delay(10000);
mDialog.Dismiss();
}
Then you should implement the interface(IDialogInterfaceOnDismissListener) member OnDismiss, in this function, you can do whatever you want to do after the ProgressDialog is dismissed:
public void OnDismiss(IDialogInterface dialog)
{
Toast.MakeText(this, "You used the 'SetOnDismissListener'.", ToastLength.Long).Show();
txtView.Text = "hello";
}
You can refer:
IDialogInterfaceOnDismissListener
using-setondismisslistener-with-dialog

BackRequested is triggering more than once in UWP app

I have an app in which i mainly have a webview. i am having a problem. i have made the back button to goto previous webpage of webview it works fine and when it has no previous pages it quits with a MessageBox(Popup). The problem is when i navigate another page and press back it recursively triggers back button event and shows the MessageBox
Windows.UI.Core.SystemNavigationManager.GetForCurrentView().BackRequested += (s, e) =>
{
e.Handled = true;
if (Web_view.CanGoBack)
{
Web_view.GoBack();
e.Handled = true;
}
else
{
quit();
e.Handled = true;
}
};
The above is code of my main page
private async void quit()
{
MessageDialog msg = new MessageDialog("Do you really want to quit?", "Quit");
msg.Commands.Add(new UICommand("Yes") { Id = 0 });
msg.Commands.Add(new UICommand("No") { Id = 1 });
var ans = await msg.ShowAsync();
if(ans.Id.Equals(0))
{
//System.Diagnostics.Debug.WriteLine("Exit");
App.Current.Exit();
}
}
this is the code of quit function.
I am navigating to another page from this using code
private void about_Click(object sender, RoutedEventArgs e)
{
Frame.Navigate(typeof(BlankPage1));
}
And the backRequested code of blanckPage1 is
SystemNavigationManager.GetForCurrentView().BackRequested += (s,e)=>
{
e.Handled = true;
// Windows.UI.Core.SystemNavigationManager.GetForCurrentView().BackRequested -= BlankPage1_BackRequested;
//System.Diagnostics.Debug.WriteLine("BackRequested");
if (Frame.CanGoBack)
{
e.Handled = true;
Frame.GoBack();
}
else
{
e.Handled = true;
}
};
To make it more clear for example when i open the app the webview navigates to www.example.com then following the links there i will get to some other page(for example www.example.com/link/firstlink). then i will navigate my frame to blankpage1 and from there i will press back. then insted of coming back to previous page (www.example.com/link/firstlink) it comes to beginning page (www.example.com) and shows the quit popup how can i fix this?
Thank you for all your replay.
Your problem is that you are still keeping the event handler: In your code when navigating back from BlankPage1, both .BackRequested handlers are called. You would need to deregister from .BackRequested on MainPage when leaving it, for example like this:
MainPage:
protected override void OnNavigatedTo(NavigationEventArgs e) {
SystemNavigationManager.GetForCurrentView().BackRequested += OnBackRequested;
}
protected override void OnNavigatedFrom(NavigationEventArgs e) {
SystemNavigationManager.GetForCurrentView().BackRequested -= OnBackRequested;
}
private void OnBackRequested(object sender, BackRequestedEventArgs e) {
// Your code to navigate back
if (Web_view.CanGoBack)
{
Web_view.GoBack();
e.Handled = true;
}
else
{
quit();
e.Handled = true;
}
}
And the same on BlankPage1... Though it would be far easier to register to BackRequested in your App.xaml.cs where you would handle your (Window.Current.Content as Frame) for the whole app, something like this. To make it "nice" code also with an interface:
INavigationPage:
public interface INavigationPage {
// When overriding the method returns true or false if the Page handled back request
bool HandleBackRequested();
}
App.xaml.cs:
// ... Code before
protected override void OnLaunched(LaunchActivatedEventArgs e) {
SystemNavigationManager.GetForCurrentView().BackRequested += OnBackRequested;
}
private void OnBackRequested(object sender, BackRequestedEventArgs e) {
Frame frame = Window.Current.Content as Frame;
if (frame == null) return;
INavigationPage page = frame.Content as INavigationPage;
if (page == null) return;
// Ask if the page handles the back request
if (page.HandleBackRequested()) {
e.Handled = true;
// If not, go back in frame
} else if (frame.CanGoBack) {
e.Handled = true;
frame.GoBack();
}
}
// ... Code after
MainPage.xaml.cs:
... class MainPage : Page, INavigationPage {
// ... Code before
// Implement the interface handling the backRequest here if possible
public bool HandleBackRequested() {
if (Web_view.CanGoBack) {
Web_view.GoBack();
return true;
}
return false;
}
// ... Code after
}
Then the BlankPage does not require any code and no subscribing to .BackRequested.

Need to print UWP MapControl with route results

I have a MapControl working just creating my route. Now, I just need to figure out a way to print it out. Using the UWP printing sample, I get a black box where the control should be. The map and route are being built, just not rendered correctly in the print preview. I thought I saw a MapControl.Print... but I think that was in the Bing.Maps stuff. Any pointers would be appreciated. Thanks.
Using the UWP printing sample, I get a black box where the control should be.
It seems the MapControl can not be printed.
As a workround, we can use RenderTargetBitmap to get the image from the MapControl. That we can print the image.
Using a RenderTargetBitmap, you can accomplish scenarios such as applying image effects to a visual that originally came from a XAML UI composition, generating thumbnail images of child pages for a navigation system, or enabling the user to save parts of the UI as an image source and then share that image with other apps.
Because RenderTargetBitmap is a subclass of ImageSource, it can be used as the image source for Image elements or an ImageBrush brush.
For more info,see RenderTargetBitmap.
For example:
RenderTargetBitmap renderTargetBitmap = new RenderTargetBitmap();
await renderTargetBitmap.RenderAsync(MyMap);
MyImage.Source = renderTargetBitmap;
The printing code:
public sealed partial class MainPage : Page
{
private PrintManager printmgr = PrintManager.GetForCurrentView();
private PrintDocument printDoc = null;
private PrintTask task = null;
public MainPage()
{
this.InitializeComponent();
printmgr.PrintTaskRequested += Printmgr_PrintTaskRequested;
}
private void Printmgr_PrintTaskRequested(PrintManager sender, PrintTaskRequestedEventArgs args)
{
var deferral = args.Request.GetDeferral();
task = args.Request.CreatePrintTask("Print", OnPrintTaskSourceRequrested);
task.Completed += PrintTask_Completed;
deferral.Complete();
}
private void PrintTask_Completed(PrintTask sender, PrintTaskCompletedEventArgs args)
{
//the PrintTask is completed
}
private async void OnPrintTaskSourceRequrested(PrintTaskSourceRequestedArgs args)
{
var def = args.GetDeferral();
await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal,
() =>
{
args.SetSource(printDoc?.DocumentSource);
});
def.Complete();
}
private async void appbar_Printer_Click(object sender, RoutedEventArgs e)
{
if (printDoc != null)
{
printDoc.GetPreviewPage -= OnGetPreviewPage;
printDoc.Paginate -= PrintDic_Paginate;
printDoc.AddPages -= PrintDic_AddPages;
}
this.printDoc = new PrintDocument();
printDoc.GetPreviewPage += OnGetPreviewPage;
printDoc.Paginate += PrintDic_Paginate;
printDoc.AddPages += PrintDic_AddPages;
bool showPrint = await PrintManager.ShowPrintUIAsync();
}
private void PrintDic_AddPages(object sender, AddPagesEventArgs e)
{
printDoc.AddPage(this);
printDoc.AddPagesComplete();
}
private void PrintDic_Paginate(object sender, PaginateEventArgs e)
{
PrintTaskOptions opt = task.Options;
printDoc.SetPreviewPageCount(1, PreviewPageCountType.Final);
}
private void OnGetPreviewPage(object sender, GetPreviewPageEventArgs e)
{
printDoc.SetPreviewPage(e.PageNumber, this);
}
}

listbox not working in C#

I would like to add items to list box.
It's completely fine in the button click function.
However, if I put it in other function, it doesn't work.
or in other words, I can see "start" in the box, but not the "mission".
Thanks
private void button1_Click(object sender, EventArgs e)
{
MessageBox.Show("start");
listBox1.Items.Add("start");
token = textBox1.Text;
shipid = textBox2.Text;
host = textBox3.Text;
shipid = shipid.Replace(",", "%2C");
System.Timers.Timer t = new System.Timers.Timer(5000);
t.Elapsed += new System.Timers.ElapsedEventHandler(Mission3);
t.AutoReset = true;
t.Enabled = true;
}
private void Mission3(object source, System.Timers.ElapsedEventArgs e)
{
progress = action(progress, 0, 0, "0");
listBox1.Items.Add("mission");
}

Resources