How to add background task to Windows Store App - c#

using c#, VS 2013
Try to add some background task to my Store app (idea to show toast if some data in my Json file contains current date and time).
What was done:
1.Create Windows Runtime Component, that implement IBackgroundTask , add reference to my Windows Store App. Inside WRC create class that contains next code :
namespace BackgroundTask
{
public sealed class EventChecker: IBackgroundTask
{
ThreadPoolTimer _periodicTimer = null;
public void Run(IBackgroundTaskInstance taskInstance)
{
_periodicTimer
= ThreadPoolTimer.CreatePeriodicTimer(new TimerElapsedHandler(PeriodicTimerCallback), TimeSpan.FromSeconds(30));
}
private void PeriodicTimerCallback(ThreadPoolTimer timer)
{
CheckEventAndShowToast();
}
....
}
2.Register task : In MainPage.xaml.cs add in method OnNavigatedTo registering of this background task. Code:
protected override void OnNavigatedTo(NavigationEventArgs e)
{
navigationHelper.OnNavigatedTo(e);
var taskName = "EventCheckerTask";
if (BackgroundTaskRegistration.AllTasks.Count > 0)
{
foreach (var cur in BackgroundTaskRegistration.AllTasks)
{
if (cur.Value.Name != taskName)
{
BackgroundTaskBuilder builder = new BackgroundTaskBuilder();
builder.Name = taskName;
builder.TaskEntryPoint = "BackgroundTask.EventChecker";
BackgroundTaskRegistration taskToRegister = builder.Register();
}
}
}
else
{
BackgroundTaskBuilder builder = new BackgroundTaskBuilder();
builder.Name = taskName;
builder.TaskEntryPoint = "BackgroundTask.EventChecker";
BackgroundTaskRegistration taskToRegister = builder.Register();
}
}
Use MSDN1, MSDN2, MSDN3 links.
Also OnComplete not implement - because i don't need it (or I must to implement it anyway?)
3.Declare in manifest.
Set toast capable to "YES":
Declare background Task:
4.Check functionality of all method that i want to use for background - all Ok and work
Durring debugging all it's ok, no errors/ exceptions, but nothing happend. Try to debug step by step - looks like all it's ok, think i make some mistake in code.
So question: where i'm wrong, why i cant launch my background task that must to check data and do required action if some conditions are as required?
EDIT
Part 2 - Try to realize background task in new solution.
What was done:
Create new simple CRC :
namespace Tasks
{
public sealed class Tasks : IBackgroundTask
{
public void Run(IBackgroundTaskInstance taskInstance)
{
//for checking place debug point
//TODO something
}
}
}
Also in main.xaml.cs placed next code:
protected override void OnNavigatedTo(NavigationEventArgs e)
{
CheckTaskRegistration();
}
private void CheckTaskRegistration()
{
foreach (var task in BackgroundTaskRegistration.AllTasks)
{
if (task.Value.Name == "Tasks")
{
isTaskRegistered = true;
break;
}
}
if (!isTaskRegistered)
{
RegisterBackgroundTask2("Tasks", "Tasks.Tasks");
}
}
private void RegisterBackgroundTask2(string name, string entrypoint)
{
BackgroundTaskBuilder btb = new BackgroundTaskBuilder();
btb.Name = name;
btb.TaskEntryPoint = entrypoint;
//IBackgroundTrigger everyMinuteTrigger = new TimeTrigger(1, false);
// btb.SetTrigger(everyMinuteTrigger);
btb.SetTrigger(new SystemTrigger(SystemTriggerType.InternetAvailable, false));
BackgroundTaskRegistration task = btb.Register();
}
As result got, that with this trigger btb.SetTrigger(new SystemTrigger(SystemTriggerType.InternetAvailable, false)); all works - i can go inside Run method, but if I try to use TimeTrigger like
//IBackgroundTrigger everyMinuteTrigger = new TimeTrigger(1, false);
// btb.SetTrigger(everyMinuteTrigger);
nothing happend - wait few minutes try few times (placed instead prev trigger registration).
Question - Why? Do i must to do something more?
Also old questions are without answers too...
Also try to use it with my App - all worksperfect, but only if i connect to lan... But why it's not work with time trigger?

A spend a little bit more time and found few root causes for my problem:
I must to use some trigger with my BackgroundTask if I want to use it and launch. problem here that ther is not exactly what i need exist (or maybe i need to read a little bit more about triggers).
So if I add some trigger, BackgroundTask can be launched after such event happend. Example:
//Time trigger
IBackgroundTrigger everyMinuteTrigger = new TimeTrigger(15, false);
btb.SetTrigger(everyMinuteTrigger);
//one of the standart tirgger
btb.SetTrigger(new SystemTrigger(SystemTriggerType.InternetAvailable, false));
If I want to use TimeTrigger, in EDIT i write code with TimeTrigger(1, false);, but after reading some more detailed documentation found "that the time trigger only accepts values greater than or equal to 15; smaller values fail during Register." MSDN
Also if I want to use TimeTrigger i must to add LockScreenNotification support to my App
Currently I can launch backgroundTask every 15 min, but it's not exactly what i want...
So, regarding this post quation - i found answer, but still need to read more deeply about BackgroundTask

Is your CheckEventAndShowToast(); calling anything asynchronously?
If that is the case then you should follow step 4 from the MSDN2 link you posted.
"4. If you run any asynchronous code in your background task, then your background task needs to use a deferral. If you don't use a deferral, then the background task process can terminate unexpectedly if the Run method completes before your asynchronous method call has completed."

Related

How To Cancel a Notification When Android App is Closed By Swipe?

When you close the app by swiping it in recent apps, it will cancel any services and terminate most aspects of the app gracefully. However, if there are any notifications that were SetOngoing(true), then these will remain if the app suddenly is closed, and there aren't any services that listen for the app's termination.
What is the right way to deal with this problem?
Recently, I coded a music player, and I arranged it such that in the OnStop for my activities, the notification is canceled (and so is the thread updating the progress bar within it). Then, OnResume, I trigger the notification again.
If they "recent apps swipe" it away, or click away, the notification goes away now, as long as the music isn't playing. So to get rid of the notification, you have to pause it, and then swipe away. Otherwise, there is a leak memory if the app is closed by swipe, where the notification remains open and is buggy afterwards if the app is reopened, and the app crashes if you click the notification (though maybe that's because I can't figure out how to get started with saved state bundles). Likewise, there is a problem if you let the app close the notification every OnStop, as then it will be closed as the user does other things with their phone, even though the music is playing (which sort of defeats the point of it right?)
Are there other better ways to handle this? Who has a good saved state bundle if that is indeed relevant to my issue?
Thanks for the discussion
You can cancel the notification when android App is closed by swipe with the following code:
[Service]
      public class ForegroundServiceDemo : Service
      {
           
            public override void OnTaskRemoved(Intent rootIntent)
            {
                  //this.StopSelf();
                  //this.StopForeground(StopForegroundFlags.Remove);
                  this.StopService(new Intent(this,typeof(ForegroundServiceDemo)));
                  base.OnTaskRemoved(rootIntent);     
            }
           
      }
By overriding the OnTaskRemoved method of the service, the system will call this method when user closes the app by swipe. And each of the three lines code can cancel the notification and stop the service when the app is closed by swipe.
I found this, finally, after trying every search terms imaginable, and wow there is a whole section on this. I do not have it working yet, but I can report back with code when I do. Here is the solution: https://developer.android.com/guide/topics/media-apps/media-apps-overview
Seems you have to implement the media player service as a specific kind of service that registers to the notification. I am in the process of refactoring the heart of my code, which perhaps should be terrifying, but feels more like the final algorithm on a Rubix's cube... I will report back in like 10 work hours with some working code (I hope).
Thanks to everyone contributing on this discussion!
OK, so, after much dabbling and dozens of work hours... I have found the best way to handle this issue is to create a MediaBrowserService with a MediaSession. In the notification creation code, it is very particular about how you start that notification (which has to be in the Foreground and bound to the MediaSession). Once this is done, the notification will stay open, even if you close the app, and clicking it will always bring you back to the activity bound to the service (see the supplied code below). Then, you just have a button on the notification to close itself and the app. Voila, a notification that does NOT remain open if the app is closed from the recent apps, etc.
public static void CancelNotificationBreadCrumb()
{
if (cts != null)
{
cts.Cancel();
Thread.Sleep(250);
// Cancellation should have happened, so call Dispose
cts.Dispose();
MyLogger.Debug("MyMediaPlayer: CloseEntireApp: Notification should have been disposed.");
}
}
public static void NotificationNowPlayingBreadCrumb()
{
try
{
Intent intent = MenuManager.GetGoToNowPlayingIntent(context, GetCurrentlyPlaying());
manager = (NotificationManager)context.GetSystemService(NotificationService);
PendingIntent pendingIntent = PendingIntent.GetActivity(context, 1, intent, PendingIntentFlags.Immutable);
NotificationChannel notificationChannel = new NotificationChannel(ChannelId, ChannelId, NotificationImportance.Low);
notificationChannel.EnableLights(false);
notificationChannel.EnableVibration(false);
notificationChannel.SetSound(null, null);
//notificationChannel.SetVibrationPattern(new long[] { 10, 20 });
manager.CreateNotificationChannel(notificationChannel);
Notification notification = NowPlayingAdapter.InflateNotification(context, currentFile, ChannelId, pendingIntent);
service.StartForeground(MY_MEDIA_NOTIFICATION_ID, notification);
manager.Notify(MY_MEDIA_NOTIFICATION_ID, notification);
// Then trigger the thread to update the real-time features
if (cts == null || cts.IsCancellationRequested)
cts = new CancellationTokenSource();
ThreadPool.QueueUserWorkItem(new WaitCallback(RunInBackground), cts.Token);
} catch(Exception e)
{
string message = "MyMediaPlayer: NotificationNowPlayingBreadCrumb: Could not create now playing breadcrumb notification; message: " + e.Message;
MyLogger.Error(message);
}
}
public static void CloseEntireApp()
{
MyLogger.Trace("MyMediaPlayer: Entering CloseEntireApp...");
if (player != null)
player.Release();
CancelNotificationBreadCrumb();
MediaReceiver.Dispose();
MediaSession.Dispose();
MyLogger.Trace("MyMediaPlayer: CloseEntireApp is Killing App. Good bye!");
service.StopSelf();
Android.OS.Process.KillProcess(Android.OS.Process.MyPid());
}
Here is the OnCreate method for my service:
public class MyMediaPlayer : MediaBrowserServiceCompat
{
private static MediaPlayer? player;
private static MusicAppFile? currentFile;
private static List<MusicAppFile>? allFilesInCurrentContext;
private static Context? context;
private static List<int> recentIndexes = new List<int>();
private static int maxRecentIndexes = 30;
private static bool shuffleMode = false;
private static ViewGroup? Parent;
private static NotificationManager? manager;
private static CancellationTokenSource? cts;
public static MediaButtonReceiver? MediaReceiver;
public static MediaSessionCompat? MediaSession;
private static PlaybackStateCompat.Builder stateBuilder;
private static MediaBrowserServiceCompat service;
public IBinder Binder { get; private set; }
public const string ActionPlay = "com.xamarin.action.PLAY";
public const string ActionPause = "com.xamarin.action.PAUSE";
public const string ActionNext = "com.xamarin.action.NEXT";
public const string ActionStop = "com.xamarin.action.STOP";
public const string ActionBack = "com.xamarin.action.BACK";
public const string ActionCloseApp = "com.xamarin.action.CLOSEAPP";
public static string ChannelId = "NowPlayingNote";
public static string MY_MEDIA_ROOT_ID = "media_root_id";
public static int MY_MEDIA_NOTIFICATION_ID = 1111111;
public static string MY_MEDIA_TAG = "media_tag";
public override void OnCreate()
{
base.OnCreate();
// Create a MediaSessionCompat
MediaSession = new MediaSessionCompat(context, MY_MEDIA_TAG);
// Enable callbacks from MediaButtons and TransportControls
MediaSession.SetFlags(
MediaSessionCompat.FlagHandlesMediaButtons |
MediaSessionCompat.FlagHandlesTransportControls);
// Set an initial PlaybackState with ACTION_PLAY, so media buttons can start the player
stateBuilder = new PlaybackStateCompat.Builder()
.SetActions(
PlaybackStateCompat.ActionPlay |
PlaybackStateCompat.ActionPlayPause |
PlaybackStateCompat.ActionSkipToNext |
PlaybackStateCompat.ActionSkipToPrevious |
PlaybackStateCompat.ActionStop);
MediaSession.SetPlaybackState(stateBuilder.Build());
// MySessionCallback() don't do this. C# isn't as good at doing callbacks because you can't define them inline
// MediaSession.SetCallback(new MediaSessionCallback(this));
service = this;
// Set the session's token so that client activities can communicate with it.
SessionToken = MediaSession.SessionToken;
}
...
I create this service when they click to select a file in one of the menu activities (so in a method called by a method called by an OnClick delegate):
if (musicMenu != null)
{
bool stillPlayingSameFile = MyMediaPlayer.UpdateCurrentContext(c, musicMenu, mf);
if (cts == null)
{
// Start the service and tell it to call play
InitiateMediaBrowserService(c);
} else
{
MyMediaPlayer.Play(stillPlayingSameFile);
}
}
GoToNowPlaying(c, mf);
and the inner service there:
public static void InitiateMediaBrowserService(Context c)
{
// Start the service and tell it to call play
Intent intent = new Intent(c, typeof(MyMediaPlayer));
intent.SetAction(MyMediaPlayer.ActionPlay);
cts = new CancellationTokenSource();
Platform.AppContext.StartForegroundService(intent);
}
Ok, so now the play service, which is triggered from the action play intent here, and makes the call to start the notification, which is where the StartForeground call is made (see the first snippet at the top):
public static void Play(bool stillPlayingSameFile)
{
// If the player has not been created before, or it is a new track, then it needs to be recreated
if (player == null || !stillPlayingSameFile)
{
// If we're here to recreate the player, destroy the old one in memory first
if (player != null)
player.Release();
// Then add the new player
if (currentFile != null)
{
Uri uri = Android.Net.Uri.Parse(currentFile.FilePath);
MediaPlayer media = MediaPlayer.Create(context, uri);
media.Completion += OnCompletion;
if (MediaReceiver == null)
MediaReceiver = new MediaButtonReceiver(context);
media.RoutingChanged += MediaReceiver.OnRoutingChanged;
player = media;
player.SetWakeMode(context, WakeLockFlags.Partial);
}
// Finally, add this file to the list of those recently played
int indexToPlay = allFilesInCurrentContext.IndexOf(currentFile);
if (indexToPlay >= 0)
recentIndexes.Add(indexToPlay);
if (recentIndexes.Count > maxRecentIndexes)
recentIndexes.RemoveAt(0);
}
// Finally start the player, which picks up where left off if this is the same track
if (!IsPlaying() || !stillPlayingSameFile)
{
player.Start();
NotificationNowPlayingBreadCrumb();
}
}
The MediaButtonReceiver and MediaBroadcastReceiver classes are pretty straightforward, so comment if you really need that code. One other thing to note is that you do have to bind the service to an activity (I suggest the now playing activity):
protected override void OnStart()
{
base.OnStart();
//Config.ConfigureBluetoothIntegration(this); TODO remove this
Intent serviceToStart = new Intent(this, typeof(MyMediaPlayer));
//serviceToStart.SetAction(MyMediaPlayer.ActionPlay);
BindService(serviceToStart, new ServiceConnection(this), Bind.AutoCreate);
}
So there, now there IS an example of how to use the MediaSession and MediaSessionCompat and MediaBrowserServiceCompat online somewhere. Even ChatGPT could not find an example or tell me how to do this. You are welcome, internet. Enjoy your coding!

How to show a WPF window from Class Library (dll) project?

I have recently made a Class Library (dll) for my other project to program a Bluetooth device via serial port (COM). The library is used to transfer firmware via COM port. It works fine until the requirement comes, which requires a WPF window to show the progress of programming. I have successfully created the progress bar using standard WPF app template. However, the standard WPF does not allow me to generate dll. After searching here, I found this link that teaches you how to add a WPF window to existing Class Library project. Also, someone teaches you how to show the window from here. Everything look good until I tried, there is nothing shows up when I call the method ProgrammBluetooth() from LabVIEW.
My main method, which is in a separate .cs file:
namespace BTMProg
{
public class BTMProgrammer
{
private bool _uut1Status = false;
private string _uut1Message = "";
public bool UUT1Status
{
get { return _uut1Status; }
set { _uut1Status = value; }
}
public string UUT1Message
{
get { return _uut1Message; }
set { _uut1Message = value; }
}
public void ProgramBluetooth (string ioPort, string firmwareFile)
{
List<UUT> uutList = new List<UUT>();
uutList.Add(new UUT(ioPort, "UUT1", 1));
Thread thread = new Thread(() =>
{
var wn = new MainWindow(uutList, firmwareFile);
wn.ShowDialog();
wn.Closed += (s, e) => wn.Dispatcher.InvokeShutdown();
Dispatcher.Run();
if (wn.TaskList[0].Result.ToUpper().Contains("SUCCESS"))
{
_uut1Status = true;
_uut1Message = wn.TaskList[0].Result;
}
else
{
_uut1Status = false;
_uut1Message = wn.TaskList[0].Result;
}
});
thread.SetApartmentState(ApartmentState.STA);
thread.Start();
}
}
}
My WPF code in MainWindow.xaml.cs:
ProgrammingViewModel _pvm = new ProgrammingViewModel();
private List<string> _viewModeList = new List<string>();
private List<Task<string>> _taskList = new List<Task<string>>();
public List<Task<string>> TaskList {
get => _taskList;
set => _taskList = value;
}
public MainWindow(List<UUT> uutList, string firmwareFile)
{
InitializeComponent();
foreach (var uut in uutList)
{
_viewModeList.Add(uut.UutName);
}
_pvm.AddProcessViewModels(_viewModeList);
ProgressBarView.DataContext = _pvm.ProcessModels;
StartProgramming(uutList, firmwareFile);
Application.Current.MainWindow.Close();
}
The issue before was that if I don't use dispatcher to create a new thread, an exception saying "The calling thread must be STA, because many UI components require this...." thrown. After I use the new thread, no error but the window does not show up as expected. What could be the problem? Thanks.
The ShowDialog function will stop execution of the thread until the window closes, meaning the rest of that code may not run and the dispatcher may not be started. You should try the Show method instead, which returns as soon as the window is shown.
Also, what is going on with these lines in the constructor of the window?
StartProgramming(uutList, firmwareFile);
Application.Current.MainWindow.Close();
Whatever that first line does, it needs to return and not do a bunch of work if you want the window to finish getting constructed. The second line makes no sense at all. Why are you closing the main window of the application? Did you even set and open a window associated with that property at some point?
I suspect one or more of these things is preventing the thread from ever reaching the point where it can show the window.

Find out which winforms controls are accessed from a background thread

We have built a huge winforms project, already in progress for multiple years.
Sometimes, our users get an exception which looks like this one.
The resolution of this problem seems to be:
don't acces UI components from a background thread
.
But since our project is a very big project with a lot of different threads, we don't succeed in finding all these.
Is there a way to check (with some tool or debugging option) which components are called from a background thread?
To clarify:
I created a sample winforms project with a single Form, containing two Button
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
button1.Text = "Clicked!";
}
private void button2_Click(object sender, EventArgs e)
{
Task.Run(() =>
{
button2.BackColor = Color.Red; //this does not throw an exception
//button2.Text = "Clicked"; //this throws an exception when uncommented
});
}
}
The background color of button2 is set to red when the button is clicked. This happens in a background thread (which is considered bad behavior). However, it doesn't (immediately) throw an exception. I would like a way to detect this as 'bad behavior'. Preferably by scanning my code, but if it's only possible by debugging, (so pausing as soon as a UI component is accessed from a background thread) it's also fine.
I've got 2 recommendations to use together, the first is a Visual Studio Plugin called DebugSingleThread.
You can freeze all the threads and work on one at a time (obviously the non-main-UI threads) and see each threads access to controls. Tedious I know but not so bad with the second method.
The second method is to get the steps in order to reproduce the problem. If you know the steps to reproduce it, it will be easier to see whats causing it. To do this I made this User Action Log project on Github.
It will record every action a user makes, you can read about it here on SO: User Activity Logging, Telemetry (and Variables in Global Exception Handlers).
I'd recommend you also log the Thread ID, then when you have been able to reproduce the problem, go to the end of the log and work out the exact steps. Its not as painful as it seems and its great for getting application telemetry.
You might be able to customise this project, eg trap a DataSource_Completed event or add a dummy DataSource property that sets the real Grids DataSource property and raises an INotifyPropertyChanged event - and if its a non-main thread ID then Debugger.Break();.
My gut feeling is you're changing a control's (eg a grid) data source in a background thread (for that non-freeze feel) and thats causing a problem with synchronisation. This is what happened to the other DevExpress customer who experienced this. Its discussed here in a different thread to the one you referenced.
Is your app set to ignore cross threading intentionally?
Cross-thread operations should be blowing up all the time in winforms. It checks for them like crazy in just about every method. for a starting point check out https://referencesource.microsoft.com/#System.Windows.Forms/winforms/Managed/System/WinForms/Control.cs.
Somewhere in your app, somebody might have put this line of code:
Control.CheckForIllegalCrossThreadCalls = False;
Comment that out and run the app, then follow the exceptions.
(Usually you can fix the problem by wrapping the update in an invoke, e.g., in a worker thread if you see textbox1.text=SomeString; change it to `textbox.invoke(()=>{textbox1.text=SomeString;});.
You may also have to add checking for InvokeRequired, use BeginInvoke to avoid deadlocks, and return values from invoke, those are all separate topics.
this is assuming even a moderate refactor is out of the question which for even a medium sized enterprise app is almost always the case.
Note: it's not possible to guarantee successful discovery of this case thru static analysis (that is, without running the app). unless you can solve the halting problem ... https://cs.stackexchange.com/questions/63403/is-the-halting-problem-decidable-for-pure-programs-on-an-ideal-computer etc...
I did this to search for that specific situation but of course, need to adjust it to your needs, but the purpose of this is to give you at least a possibility.
I called this method SearchForThreads but since it's just an example, you can call it whatever you want.
The main idea here is perhaps adding this Method call to a base class and call it on the constructor, makes it somewhat more flexible.
Then use reflection to invoke this method on all classes deriving from this base, and throw an exception or something if it finds this situation in any class.
There's one pre req, that is the usage of Framework 4.5.
This version of the framework added the CompilerServices attribute that gives us details about the Method's caller.
The documentation for this is here
With it we can open up the source file and dig into it.
What i did was just search for the situation you specified in your question, using rudimentary text search.
But it can give you an insight about how to do this on your solution, since i know very little about your solution, i can only work with the code you put on your post.
public static void SearchForThreads(
[System.Runtime.CompilerServices.CallerMemberName] string memberName = "",
[System.Runtime.CompilerServices.CallerFilePath] string sourceFilePath = "",
[System.Runtime.CompilerServices.CallerLineNumber] int sourceLineNumber = 0)
{
var startKey = "this.Controls.Add(";
var endKey = ")";
List<string> components = new List<string>();
var designerPath = sourceFilePath.Replace(".cs", ".Designer.cs");
if (File.Exists(designerPath))
{
var designerText = File.ReadAllText(designerPath);
var initSearchPos = designerText.IndexOf(startKey) + startKey.Length;
do
{
var endSearchPos = designerText.IndexOf(endKey, initSearchPos);
var componentName = designerText.Substring(initSearchPos, (endSearchPos - initSearchPos));
componentName = componentName.Replace("this.", "");
if (!components.Contains(componentName))
components.Add(componentName);
} while ((initSearchPos = designerText.IndexOf(startKey, initSearchPos) + startKey.Length) > startKey.Length);
}
if (components.Any())
{
var classText = File.ReadAllText(sourceFilePath);
var ThreadPos = classText.IndexOf("Task.Run");
if (ThreadPos > -1)
{
do
{
var endThreadPos = classText.IndexOf("}", ThreadPos);
if (endThreadPos > -1)
{
foreach (var component in components)
{
var search = classText.IndexOf(component, ThreadPos);
if (search > -1 && search < endThreadPos)
{
Console.WriteLine($"Found a call to UI thread component at pos: {search}");
}
}
}
}
while ((ThreadPos = classText.IndexOf("Task.Run", ++ThreadPos)) < classText.Length && ThreadPos > 0);
}
}
}
I hope it helps you out.
You can get the Line number if you split the text so you can output it, but i didn't want to go through the trouble, since i don't know what would work for you.
string[] lines = classText.Replace("\r","").Split('\n');
Try that:
public static void Main(string[] args)
{
// Add the event handler for handling UI thread exceptions to the event.
Application.ThreadException += new ThreadExceptionEventHandler(exception handler);
// Set the unhandled exception mode to force all Windows Forms errors to go through the handler.
Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException);
// Add the event handler for handling non-UI thread exceptions to the event.
AppDomain.CurrentDomain.UnhandledException += // add the handler here
// Runs the application.
Application.Run(new ......);
}
Then you can log the message and the call stack and that should give you enough information to fix the issue.
I recommend you update your GUI to handle this situation automatically for your convenience. You instead use a set of inherited controls.
The general principle here is to override the property Set methods in a way to make them Thread Safe. So, in each overridden property, instead of a straight update of the base control, there's a check to see if an invoke is required (meaning we're on a separate thread the the GUI). Then, the Invoke call updates the property on the GUI thread, instead of the secondary thread.
So, if the inherited controls are used, the form code that is trying to update GUI elements from a secondary thread can be left as is.
Here is the textbox and button ones. You would add more of them as needed and add other properties as needed. Rather than putting code on individual forms.
You don't need to go into the designer, you can instead do a find/replace on the designer files only. For example, in ALL designer.cs files, you would replace System.Windows.Forms.TextBox with ThreadSafeControls.TextBoxBackgroundThread and System.Windows.Forms.Button with ThreadSafeControls.ButtonBackgroundThread.
Other controls can be created with the same principle, based on which control types & properties are being updated from the background thread.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace ThreadSafeControls
{
class TextBoxBackgroundThread : System.Windows.Forms.TextBox
{
public override string Text
{
get
{
return base.Text;
}
set
{
if (this.InvokeRequired)
this.Invoke((MethodInvoker)delegate { base.Text = value; });
else
base.Text = value;
}
}
public override System.Drawing.Color ForeColor
{
get
{
return base.ForeColor;
}
set
{
if (this.InvokeRequired)
this.Invoke((MethodInvoker)delegate { base.ForeColor = value; });
else
base.ForeColor = value;
}
}
public override System.Drawing.Color BackColor
{
get
{
return base.BackColor;
}
set
{
if (this.InvokeRequired)
this.Invoke((MethodInvoker)delegate { base.BackColor = value; });
else
base.BackColor = value;
}
}
}
class ButtonBackgroundThread : System.Windows.Forms.Button
{
public override string Text
{
get
{
return base.Text;
}
set
{
if (this.InvokeRequired)
this.Invoke((MethodInvoker)delegate { base.Text = value; });
else
base.Text = value;
}
}
public override System.Drawing.Color ForeColor
{
get
{
return base.ForeColor;
}
set
{
if (this.InvokeRequired)
this.Invoke((MethodInvoker)delegate { base.ForeColor = value; });
else
base.ForeColor = value;
}
}
public override System.Drawing.Color BackColor
{
get
{
return base.BackColor;
}
set
{
if (this.InvokeRequired)
this.Invoke((MethodInvoker)delegate { base.BackColor = value; });
else
base.BackColor = value;
}
}
}
}

Implement user interface command with background computing in wpf & mvvm

I have some difficulty with implementation user interface commands.
I use wpf, prism and mvvm. My app has two region - main and menu.
While app is loading in the menu region (NavBarControl, Devexpress) registering menu items (NavBarGroup). Each NavBarGroup has some NavBarItem. When a NavBarItem is selected the command which is bound executing. Some commands allow to create an entity. But for that app has to load some data from server and in this time the user interfacу should be responsive. I tried to reach that use next way:
this.createAccount.Command = (ICommand)new DelegateCommand(this.ExecuteCreateAccount);
private void ExecuteCreateAccount()
{
AppEvent.OnShowNotificationEvent(UTNotificationType.ChangeMainLoaderStatus, "show", null);
if (this.isCreateAccountProcessing)
{
return;
}
this.isCreateAccountProcessing = true;
Task.Factory.StartNew(() => this.AccountListViewModel.LoadUsersCollection()).GetAwaiter().OnCompleted(this.ShowAccountEditor);
}
private void ShowAccountEditor()
{
AppEvent.OnShowNotificationEvent(UTNotificationType.ChangeMainLoaderStatus, null, null);
this.isCreateAccountProcessing = false;
if (this.createAccount.IsSelected)
{
this.AccountListViewModel.CreateNewItem();
}
}
But maybe there is a better way to rich this goal?
While background computing takes place the app shows loader (AppEvent.OnShowNotificationEvent). If an user select another menu item the command is considered cancelled and account editor shouldn't be shown.
Since you are using the DevExpress framework, I suggest you to use the AsyncCommand. According to documentation, it designed for the scenarios like you've described.
Prism's DelegateCommand can handle async tasks. What about this:
this.createAccount.Command = (ICommand)new DelegateCommand(this.ExecuteCreateAccount);
private async Task ExecuteCreateAccount()
{
AppEvent.OnShowNotificationEvent(UTNotificationType.ChangeMainLoaderStatus, "show", null);
if (this.isCreateAccountProcessing)
{
return;
}
this.isCreateAccountProcessing = true;
await this.AccountListViewModel.LoadUsersCollection());
AppEvent.OnShowNotificationEvent(UTNotificationType.ChangeMainLoaderStatus, null, null);
this.isCreateAccountProcessing = false;
if (this.createAccount.IsSelected)
{
this.AccountListViewModel.CreateNewItem();
}
}
That is, if AccountListViewModel.LoadUsersCollection() can be made async. Otherwise you should wrap it in a Task.Run like this
await Task.Run( () => this.AccountListViewModel.LoadUsersCollection() );

Running Background Audio in different pages of UWP App

I am making a UWP App where I run Background Audio in the MainPage on a Button Click event. When I move to another page, there's also a different Media to play in Background Audio Task there.
How can I stop the currently playing Task to run the other? Should I define something globally? Any help regarding this issue?
Edit
I am using this sample: https://github.com/Microsoft/Windows-universal-samples/tree/master/Samples/BackgroundAudio While the backgroundAudio of the first Page is running, I go to the second page and on a click event I set a new List with the following code:
// First update the persisted start track
ApplicationSettingsHelper.SaveSettingsValue(ApplicationSettingsConstants.TrackId, RadioFacade.mySongs[0].MediaUri.ToString()); //here
ApplicationSettingsHelper.SaveSettingsValue(ApplicationSettingsConstants.Position, new TimeSpan().ToString());
// Start task
StartBackgroundAudioTask();
But the new song takes more than the estimated time to run and enter the else of this method:
private void StartBackgroundAudioTask()
{
AddMediaPlayerEventHandlers();
var startResult = this.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
bool result = backgroundAudioTaskStarted.WaitOne(10000);
//Send message to initiate playback
if (result == true)
{
MessageService.SendMessageToBackground(new UpdatePlaylistMessage(RadioFacade.mySongs));
MessageService.SendMessageToBackground(new StartPlaybackMessage());
}
else
{
throw new Exception("Background Audio Task didn't start in expected time");
}
});
startResult.Completed = new AsyncActionCompletedHandler(BackgroundTaskInitializationCompleted);
}
and the old (first playing) song keeps playing.
I tried to Stop the current BackgroundMediaPlayer using BackgroundMediaPLayer.Shutdown() but it didn't work.
Any idea how to let the old song stop and the current song play?
You can control the background media player by sending messages to it from the foreground. For example,
From the foreground app:
BackgroundMediaPlayer.SendMessageToBackground(new ValueSet
{
{"playnew", "some value"}
});
In your background task:
public sealed class AudioPlayer : IBackgroundTask
{
public void Run(IBackgroundTaskInstance taskInstance)
{
BackgroundMediaPlayer.MessageReceivedFromForeground += BackgroundMediaPlayer_MessageReceivedFromForeground;
...
...
}
private async void BackgroundMediaPlayer_MessageReceivedFromForeground(object sender, MediaPlayerDataReceivedEventArgs e)
{
object value;
if (e.Data.TryGetValue("playnew", out value) && value != null)
{
// value will be whatever you pass from the foreground.
BackgroundMediaPlayer.Current.Pause();
BackgroundMediaPlayer.Current.Source = stream source;
BackgroundMediaPlayer.Current.Play();
}
}
...
}
"playnew" can be a global constant in your application. You can use the ValueSet to pass the live stream url to the background task as the value.

Categories

Resources