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

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();
}

Related

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

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...");
}

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.

Force DispatcherTimer tick

I need to send a server request about once per minute, to get a new products list (in case it was changed via web).
So, i'm using DispatcherTimer
public static void Start()
{
if (timer != null) return;
timer = new DispatcherTimer {Interval = TimeSpan.FromSeconds(0.1)};
timer.Tick += Run;
timer.Start();
}
private static async void Run(object sender, EventArgs e)
{
timer.Interval = TimeSpan.FromSeconds(60); // TODO" add dynamic changes here
timer.Stop();
** Do stuff
timer.Start();
}
However, sometimes, i need to force updating. Is it correct to run
public static void ForceUpdate()
{
Run(null, null);
}
EDIT: i mean, if Do stuff is long enough, wouldn't it be called second time via timer? Or maybe i should use something else for this kind of job?
EDIT: Insert a variable which should store the last update time and check if update had been done in a certain interval.
Ah, well, it is quite simple
public static void ForceUpdate()
{
timer.Stop();
timer.Interval = TimeSpan.FromMilliseconds(10);
timer.Start();
}

Play SoundEffect only on first time page load

WP7.5/Silverlight App...
On my page load, I play a Sound clip (e.g. Hello! Today is a wonderful day.)
private void PhoneApplicationPage_Loaded(object sender, RoutedEventArgs e)
{
seLoadInstance = seLoad.CreateInstance(); //I initialize this seLoad in Initialize method
seLoadInstance.Play();
}
Now I have 3-4 other elements on the page. When user click on any of them, a sound clip for that element plays.
private void ElementClick_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
seElementInstance = seElement.CreateInstance();
seElementInstance .Play();
}
What I want is:
When the page first loads and while the seLoadInstance is being played and user clicks the element, I don't want the seElementInstance to be played.
I can check the state of seLoadInstance like below to not play seElementInstance
private void ElementClick_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
if(seLoadTextInstance.State != SoundState.Playing)
{
seElementInstance = seElement.CreateInstance();
seElementInstance .Play();
}
}
But the problem with above is that I have another element that can play the seLoadInstance on it's click.
Problem: I don't know how to differentiate if the seLoadInstance being played is first time or upon element click.
Possible solution: One way I see is using different instances to play the same sound.
I was hoping some better way like I set a flag upon load but I couldn't find any explicit event for SoundInstance completed or Stopped that I can handle.
Any ideas??
Have not used sounds until now but what I have seen:
Why do you always create new instances when you want to play a sound?
Isn't it possible to create a instance for both "se"-elements and cust check if anyone is running before calling "play"?
For example:
private var seLoadInstance;
private var seElementInstance;
private void PhoneApplicationPage_Loaded(object sender, RoutedEventArgs e)
{
seLoadInstance = seLoad.CreateInstance();
seElementInstance = seElement.CreateInstance();
seLoadInstance.Play(); // no need to check if something is playing... nothing will be loaded
}
private void ElementClick_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
if(seLoadInstance.State != SoundState.Playing && seElementInstance.State != SoundState.Playing)
{
seElementInstance .Play();
}
}
I was able to find a way using flag. Instead of setting a flag upon the firsttime load complete, I set the flag from one of my element that plays the seLoadTextInstance.
Something like below:
private bool isElementLoadSoundPlaying = false; //I set this to true below in another handler
private void ElementClick_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
//This if means LoadTextInstance is playing and it is the first time play
if(seLoadTextInstance.State != SoundState.Playing && isElementLoadSoundPlaying == false )
{
return;
}
seElementInstance = seElement.CreateInstance();
seElementInstance .Play();
}
private void ElementLoadTextClick_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
isElementLoadSoundPlaying = true;
seLoadInstance = seLoad.CreateInstance();
seLoadInstance.Play();
}

How do I detect when toolkit:GestureListener Hold has stopped?

Is there a way I can detect this? I want to keep performing an action as long as the user is holding on an icon.
Instead of using the GestureListener for this you could instead use the mouse manipulation events to detect how long to perform your action. For instance:
Listen for MouseLeftButtonDown to know when the user has touched the icon
Keep performing the action until either MouseLeftButtonUp or MouseLeave fire indicating that the user is no longer touching that icon
You may also have to play with MouseEnter for initiating the action
Today only i did the same thing in my project.I'll tell you the basic logic what i implemented(assuming it has to be done on button).Step 1: On the button _ManipulationStarted_ event start a timer with the interval after which you want to fire the repeat action.
Step 2: On the button _ManipulationCompleted_ event stop the timer.
Step 3: If the timer is fired,stop the timer and start another timer with interval = the repeat interval for your action.And inside the second timer fire handler perform your operation only if the control has focus. In this case, where the control is a button, you can check if the button.IsPressed is true then perform action.
The code will look something like:
Button forward=new Button();
DispatcherTimer forwardHoldTimer = new DispatcherTimer() { Interval = TimeSpan.FromSeconds(2) };
forward.ManipulationStarted += (a, b) => { forwardHoldTimer.Start(); };
forward.ManipulationCompleted += (c, d) => { forwardHoldTimer.Stop(); };
forwardHoldTimer.Tick+=(s1,e1)=>
{
forwardHoldTimer.Stop();
DispatcherTimer t = new DispatcherTimer() { Interval = TimeSpan.FromMilliseconds(100) };
t.Tick += (x, y) =>
{
if (forward.IsPressed)
{
//Your action logic will go here
}
else
t.Stop();
};
t.Start();
};
Hope this helps.
NOTE: Amresh Kumar was correct in suggesting using the manipulation events. Also, I was given the same advice on the Windows Phone App Hubs forums so I've edited this post to reflect the code changes.
Before, the UX was flaky because lifting my finger off the screen didn't always trigger a cancellation. Not surprisingly, the GestureCompleted code in the toolkit appears to be better geared towards touchscreens than are mouse button events.
XAML:
<iconControls:iconUpDownArrow>
<toolkit:GestureService.GestureListener>
<toolkit:GestureListener Tap="RangeUpTap" Hold="RangeUpHold" GestureCompleted="RangeUpCancel" />
</toolkit:GestureService.GestureListener>
</iconControls:iconUpDownArrow>
code:
private void RangeUpTap(object sender, GestureEventArgs e)
{
RangeIncrementUp(sender, e);
}
private readonly TimeSpan _rangeIncrementTimeSpan = new TimeSpan(1500000);
private readonly DispatcherTimer _rangeIncrementTimer = new DispatcherTimer();
private void RangeUpHold(object sender, GestureEventArgs e)
{
_rangeIncrementTimer.Interval = _rangeIncrementTimeSpan;
_rangeIncrementTimer.Tick += RangeIncrementUp;
_rangeIncrementTimer.Start();
}
private void RangeUpCancel(object sender, GestureEventArgs e)
{
_rangeIncrementTimer.Stop();
_rangeIncrementTimer.Tick -= RangeIncrementUp;
}
private void RangeIncrementUp(object sender, EventArgs e)
{
int range = Convert.ToInt32(tBoxRange.Text);
if (range < 1000)
{
range += 10;
}
tBoxRange.Text = range.ToString();
}

Resources