Proper way to wait all tasks while still updating the UI thread - async-await

I'm trying to download the page source from multiple urls using tasks to download multiple sites at one time. The issue is that I want to keep the UI updated as each individual task completes. When I try to wait all tasks it stops updating the UI until they all finish. Here is the current code that I am using.
EDIT: I'm assuming I was down voted due to me not explaining well enough. I guess a better way to put this is why is the continueWith not being run before Task.WaitAll. I want the UI to update on each completion of the source being downloaded. Once that is all finished then the listbox would be updated to let the user know everything is done.
private void btnGetPages_Click(object sender, EventArgs e)
{
for (int i = 1; i < 11; i++)
{
string url = $"http://someURL/page-{i}.html";
listBoxStatus.Items.Add($"Downloading source from {url}...");
Task t = new Task(() =>
{
DownloadSource(url);
});
t.ContinueWith(prevTask => listBoxStatus.Items.Add($"Finished Downloading {url} source..."), TaskScheduler.FromCurrentSynchronizationContext());
tasks.Add(t);
t.Start();
}
Task.WaitAll(tasks.ToArray());
listBoxStatus.Items.Add("All Source files have completed...");
}
private void DownloadSource(string url)
{
var web = new HtmlWeb();
var doc = web.Load(url);
pageSource += doc.Text;
}

You really should use an asynchronous download method based on HttpClient instead of the synchronous method you are showing. Lacking that, I'll use this one:
private async Task DownloadSourceAsync(string url)
{
await Task.Run(() => DownloadSource(url));
listBoxStatus.Items.Add($"Finished Downloading {url} source...");
}
Then, you can make your btnGetPages_Click method something like this:
private async void btnGetPages_Click(object sender, EventArgs e)
{
var tasks = new List<Task>();
for (int i = 1; i < 11; i++)
{
string url = $"http://someURL/page-{i}.html";
listBoxStatus.Items.Add($"Downloading source from {url}...");
tasks.Add(DownloadSourceAsync(url));
}
Task.WaitAll(tasks.ToArray());
listBoxStatus.Items.Add("All Source files have completed...");
}

Related

Blazor Time Localisation

Hi i am in trouble with time, I want to display the time and update it in realtime
I already installed "Install-Package Blazored.Localisation"
below is the sample code
string currentLocalTime = "";
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender) // Remove the firstRender check if you want the current local time displayed to continuously update.
{ // Leave the above firstRender check in place to ensure that the call to StateHasChanged() does not trigger an endless update loop.
var browserDateTime = await browserDateTimeProvider.GetInstance();
currentLocalTime = browserDateTime.Now.ToString();
StateHasChanged();
}
}
I realize that you can remove first render check if you want to continuously update.
Just remove If statement.
string currentLocalTime = "";
protected override async Task OnAfterRenderAsync(bool firstRender)
{
var browserDateTime = await browserDateTimeProvider.GetInstance();
currentLocalTime = browserDateTime.Now.ToString();
StateHasChanged();
}

Easy way to stop a task from executing after certain amount of time(Xamarin.Android)

On button click I want to open a ProgressDialog which will show until a task is being executed, but I want to be able to stop the task even if it hasn't been completed after certain amount of time. I saw a lot of solutions on internet but they are very long. I want to know if there is an easier way.
here is my On 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();
CancellationTokenSource cts = new CancellationTokenSource();
cts.CancelAfter(5000);
Task<int> task = new Task<int>(Foo, cts.Token);
task.Start();
int integer = await task;
mDialog.Dismiss();
txtView.Text = integer.ToString();
}
And here is my method which will execute in the task:
public int Foo()
{
System.Threading.Thread.Sleep(10000);
return 100;
}
Is it possible to stop the task at the 5th second of its execution without changing a lot the code I've just pasted, for example with only passing some time or object to the task's constructor, and also without making the Foo() method async
You can try
step 1. Adding a timer
Timer _timer = new Timer {Interval = 5000};
_timer.Elapsed += OnTimeEvent;
_timer.Start();
step 2.Cancel the task on timer event
private void OnTimeEvent(object sender, ElapsedEventArgs e)
{
cts .Cancel();
}

How do you programmatically run Static Code Analysis in Visual Studio 2017?

I'm working on a solution to localize legacy applications. I've written a Visual studio add-in using EnvDte that automates the process of setting the "Localizable" flag for every form in the solution to true, which is a critical step for extracting resources on form designers. I am now trying to deal with any text that is set programmatically, text that trigger the Globalization (CA13##) warnings.
designer.Visible = true;
var host = (IDesignerHost)designer.Object;
var provider = TypeDescriptor.GetProvider(host.RootComponent);
var typeDescriptor = provider.GetExtendedTypeDescriptor(host.RootComponent);
if (typeDescriptor == null)
continue;
var propCollection = typeDescriptor.GetProperties();
var propDesc = propCollection["Localizable"];
if (propDesc != null && host.RootComponent != null &&
(bool?)propDesc.GetValue(host.RootComponent) != true)
{
try
{
propDesc.SetValue(host.RootComponent, true);
}
catch (Exception ex)
{
// log the error
}
// save changes
}
I've been able to run it manually from the menu using: Analyze -> Run Code Analysis -> On Solution to get a list of issues, but I would like to automate this step with another add-in that runs and extracts the results.
Are there any resources that point to accessing the build warnings or the results of the code analysis?
Are there any solutions that already do this using EnvDte or Roslyn?
Ok, I've managed to glean enough information to put together the add-in. Put simply, you use _dte.ExecuteCommand()
Initialize the command:
// nothing to see here...
_package = package ?? throw new ArgumentNullException(nameof(package));
var commandService = ServiceProvider.GetService(typeof(IMenuCommandService)) as OleMenuCommandService;
if (commandService == null)
return;
_dte = (DTE2)ServiceProvider.GetService(typeof(DTE));
var menuCommandId = new CommandID(CommandSet, CommandId);
var menuItem = new MenuCommand(MenuItemCallback, menuCommandId);
commandService.AddCommand(menuItem);
_events = _dte.Events.BuildEvents;
// since static code analysis is a sort of build you need to hook into OnBuildDone
_events.OnBuildDone += OnBuildDone;
Trigger the analysis
private void MenuItemCallback(object sender, EventArgs e)
{
_dte.ExecuteCommand("Build.RunCodeAnalysisonSolution");
}
Extract errors in the OnBuildDone event
private void OnBuildDone(vsBuildScope scope, vsBuildAction action)
{
Dispatcher.CurrentDispatcher.InvokeAsync(new Action(() =>
{
_dte.ExecuteCommand("View.ErrorList", " ");
var errors = _dte.ToolWindows.ErrorList.ErrorItems;
for (int i = 1; i <= errors.Count; i++)
{
ErrorItem error = errors.Item(i);
var code = error.Collection.Item(1);
var item = new
{
error.Column,
error.Description,
error.ErrorLevel,
error.FileName,
error.Line,
error.Project
};
error.Navigate(); // you can navigate to the error if you wanted to.
}
});
}

Xamarin iOS RefreshControl is stuck

I have a problem with RefreshControl... I have this code:
In ViewDidLoad() I call method InitializeRefreshControl();
private void InitializeRefreshControl()
{
if (UIDevice.CurrentDevice.CheckSystemVersion(6, 0))
{
//UIRefreshControl iOS6
ordersCollectionView.RefreshControl = new UIRefreshControl();
ordersCollectionView.RefreshControl.AttributedTitle = new NSAttributedString("Pull To Refresh",
new UIStringAttributes()
{
ForegroundColor = UIColor.Red,
KerningAdjustment = 3
});
ordersCollectionView.RefreshControl.ValueChanged += HandleValueChanged;
}
else
{
// old style refresh button and no PassKit for older iOS
NavigationItem.SetRightBarButtonItem(new UIBarButtonItem(UIBarButtonSystemItem.Refresh), false);
NavigationItem.RightBarButtonItem.Clicked += (sender, e) => { Refresh(); };
}
}
HandleValueChange method and Refresh merhod is here:
private void HandleValueChanged(object sender, EventArgs e)
{
ordersCollectionView.RefreshControl.BeginRefreshing();
ordersCollectionView.RefreshControl.AttributedTitle = new NSAttributedString("Refreshing",
new UIStringAttributes()
{
ForegroundColor = UIColor.Blue,
KerningAdjustment = 5
});
Refresh();
ordersCollectionView.RefreshControl.EndRefreshing();
}
private void Refresh()
{
var viewModel = (OrdersViewModel)DataContext;
viewModel.OnReloadData();
}
My problem is when I pull down collectionVIew so Refresh loading is displayed but is stuck no loading effect and still with text "Pull to refresh". When method Refresh end so for 0,1ms is showing loading effect and text "Refreshing" but not before method Refresh... Someone know how solve this problem? Thanks for answer.
It looks like the issue is related to the Refresh(); method being synchronous. You'll need to make this operation happen in the background so that the UI thread is free to provide the animation for the RefreshControl. For example:
private async void HandleValueChanged(object sender, EventArgs e)
{
ordersCollectionView.RefreshControl.BeginRefreshing();
ordersCollectionView.RefreshControl.AttributedTitle = new NSAttributedString("Refreshing",
new UIStringAttributes()
{
ForegroundColor = UIColor.Blue,
KerningAdjustment = 5
});
// await a Task so that operation is done in the background
await Refresh();
ordersCollectionView.RefreshControl.EndRefreshing();
}
// Marked async and Task returning
private async Task Refresh()
{
var viewModel = (OrdersViewModel)DataContext;
// Need to update this method to be a Task returning, async method.
await viewModel.OnReloadData();
}
The above code refactors what you had to use async/await and Tasks. You may need to refactor some more of your code to make that work, including the OnReloadData() method.
There are lots of resources for getting started with Tasks, async and await. I can start you off with this reference from the Xamarin blog.

Reactive Extensions subscribing to an observable (subject)

I'm just playing around with Reactive Extensions for the first time in a winforms application. Mind you I have been doing web development for the past 4 years, and I am very familiar with observables and observable pattern in knockout, which I am guessing is contributing to my confusion here.
Anyhow, to the question and code. I have a simple winforms experiment (see below) that I was building to illustrate my question. The subscribe below doesn't run until well after the thread in start new is finished. I can trace it the calls to OnNext, but the subscribe doesn't fire at all until sometimes 20-30 seconds later. Can somebody explain this behavior to me?
public partial class Form1 : Form
{
private Subject<int> progress;
private CancellationToken cancellationToken;
private IScheduler _scheduler;
public Form1()
{
InitializeComponent();
CancellationTokenSource source = new CancellationTokenSource();
cancellationToken = source.Token;
_scheduler = new SynchronizationContextScheduler(SynchronizationContext.Current);
}
private void Start_Click(object sender, EventArgs e)
{
progress
.ObserveOn(_scheduler)
//.Throttle(TimeSpan.FromSeconds(5))
.Subscribe(
(i) => {
progressBar1.Do<ProgressBar>(ctl =>
{
ctl.Value = i;
});
},
(ex) => { },
cancellationToken
);
Task counterTask = Task.Factory.StartNew(() =>
{
for (var i = 1; i < 101; i++)
{
Thread.Sleep(500);
progress.OnNext(i);
}
}, cancellationToken,
TaskCreationOptions.LongRunning,
TaskScheduler.FromCurrentSynchronizationContext()
);
}
private void Form1_Load(object sender, EventArgs e)
{
progress = new Subject<int>();
}
}
public static class ControlExtensions
{
public static void Do<TControl>(this TControl control, Action<TControl> action)
where TControl : Control
{
if (control.InvokeRequired)
control.Invoke(action, control);
else
action(control);
}
}
Your issue comes from the fact that your task is running on the UI thread, because you're using TaskScheduler.FromCurrentSynchronizationContext().
Hence your various Sleep calls are blocking the UI thread, freezing the UI (e.g. can't drag the window) and preventing your observable subscription to execute (because the ObserveOn, it's supposed to execute on the UI thread scheduler).
Replace TaskScheduler.FromCurrentSynchronizationContext() by TaskScheduler.Default (background TaskPool threads), and everything will work as you expected.
Note that your call to Do/Invoke is unnecessary, because you're already on the UI thread by the scheduler you've provided.

Resources