PROBLEM
I am using a secondary view to run my media files, but When I close my secondary view with close button on it ( while media is still playing ) the secondary view/window closes but the media somehow keeps playing because I can hear the sound and source of sound seems to be the primary view ( main app window ). how can I completely terminate the secondary window when I close it?
TRIED
I followed windows samples multiple views and was able to complete all steps, I copied the ViewLifetimeControl.cs file from the sample and used it in my project. the code runs fine until it reaches Windows.Current.Close() in released event of the secondary view.
Then it gives an exception when it tries "Window.Current.Close()" with in the released event. according to documentation exception occurs due to any on going changes ( which might be because of media file playing ), but I need to force close the window even when media file is playing how can I do that? btw here is the exception :
Message = "COM object that has been separated from its underlying RCW cannot be used."
Code to Create and Show secondary view
internal static async Task CompactOpen(string Title, string caption)
{
ViewLifetimeControl viewControl = null;
await CoreApplication.CreateNewView().Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
viewControl = ViewLifetimeControl.CreateForCurrentView();
viewControl.Title = Title;
viewControl.StartViewInUse();
var frame = new Frame();
frame.MinHeight = 200;
frame.MinWidth = 200;
frame.Navigate(typeof(CompactNowPlayingPage), new object[] { viewControl,caption});
Window.Current.Content = frame;
Window.Current.Activate();
ApplicationView.GetForCurrentView().Title = viewControl.Title;
});
((App)App.Current).SecondaryViews.Add(viewControl);
var selectedView = viewControl;
var sizePreference = new SizePreferenceString() { Title = "SizePreference", Preference = ViewSizePreference.Default };
var anchorSizePreference = new SizePreferenceString() { Title = "AnchorSizePreference", Preference = ViewSizePreference.Default };
if (selectedView != null && sizePreference != null && anchorSizePreference != null)
{
try
{
selectedView.StartViewInUse();
var viewShown = await ApplicationViewSwitcher.TryShowAsStandaloneAsync(
selectedView.Id,
sizePreference.Preference,
ApplicationView.GetForCurrentView().Id,
anchorSizePreference.Preference);
if (!viewShown)
{
// The window wasn't actually shown, so release the reference to it
// This may trigger the window to be destroyed
}
// Signal that switching has completed and let the view close
selectedView.StopViewInUse();
}
catch (InvalidOperationException)
{
// The view could be in the process of closing, and
// this thread just hasn't updated. As part of being closed,
// this thread will be informed to clean up its list of
// views (see SecondaryViewPage.xaml.cs)
}
}
}
Released Event
private async void ViewLifetimeControl_Released(Object sender, EventArgs e)
{
((ViewLifetimeControl)sender).Released -= ViewLifetimeControl_Released;
// The ViewLifetimeControl object is bound to UI elements on the main thread
// So, the object must be removed from that thread
await mainDispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
((App)App.Current).SecondaryViews.Remove(thisViewControl);
});
// The released event is fired on the thread of the window
// it pertains to.
//
// It's important to make sure no work is scheduled on this thread
// after it starts to close (no data binding changes, no changes to
// XAML, creating new objects in destructors, etc.) since
// that will throw exceptions
Window.Current.Close(); //this is where that exception occurs
}
Note : both of above methods and even all the related variables, all of them I have followed the guidelines within the uwp sample for multiple views.
Thanks in advance, any help would be really appreciated, I only want to force close the secondary view ( If that's possible )
Is this in the editor or the app? If it's in your debug or build of the app, the secondary view is most likely still open but hidden. You may be using a custom close button which doesn't perform its job well enough. Instead of putting down SecondaryViews.Remove you should do what you had originally written and try StopViewInUse. It may not work, I'm not used to this kind of thing.
Related
so after more than a week of trying to solve it on my own I officially give up and turn to your help. Basically, it should not be so complicated so I have no idea why it does not work. I have a WPF app which contains a Main Window called surprise surpise...: Main_Window.
That window contain a user control called 'pageTransitionControl' that change its content according to what the client want to see. the 'pageTransitionControl' is there to support multiple animations and so on... Anyway, among all of the user controls, i have a preety havy uc called ucBanks. before it shows, the ucBanks load a lot of data, manipulating it and display it on a very beautiful and smart charts. the problem is it takes some time to load it, approximately 6-7 seconds so i need the UI to show 'Loading' animation during that time (another user control called 'ucSpinner').
I'm Trying to load the ucBanks on a different thread to avoid freezing the application and it works great: the ucSpinner is showed immidiatlly and the ucBanks is loading on the background but when i change the content of the 'pageTransitionControl' i get this error:
"The calling thread cannot access this object because a different thread owns it".
I think i tried basically everything but i must missing somthing or doing somthing wrong.
This is where it all start, the btn_click event that load ucBanks:
ShowSpinner();
Thread.Sleep(100);
Thread newThread = new Thread(new ThreadStart(LoadUc));
newThread.SetApartmentState(ApartmentState.STA);
newThread.IsBackground = true;
newThread.Start();
This is the ShowSpinner method:
private void ShowSpinner()
{
ucSpinner.Opacity = 1;
}
and this is the LoadUc method:
private void LoadUc()
{
ucOsh ucOshx = new ucOsh();
Utils.LoadUc(ucOshx, null, PageTransitions.PageTransitionType.GrowAndFade, true, this, null, true);
}
With the LoadUc i called static class called 'Utils' holding the 'LoadUc' method:
public static void LoadUc(System.Windows.Controls.UserControl ucParent, System.Windows.Controls.UserControl ucChild, PageTransitions.PageTransitionType tranType, bool removeChildrens = true, System.Windows.Window w = null, List<Plist.Plist> lst = null, bool hideMenu = false)
{
MainWindow win = null;
if (w != null) { win = (MainWindow)w; }
else { win = (MainWindow)System.Windows.Window.GetWindow(ucChild); }
win.Dispatcher.Invoke(
System.Windows.Threading.DispatcherPriority.ContextIdle, (System.Action)delegate
{
win.pageTransitionControl.TransitionType = tranType;
win.pageTransitionControl.PARAMS = lst;
win.pageTransitionControl.Tag = ucParent.ToString();
win.pageTransitionControl.pages.Push(ucParent);
win.pageTransitionControl.Content = ucParent; ----------->>>>This is where i get the error!!!
});
}
I understand that the main window is locked inside another thread but i cant see any other option to load it without freezing the entire app.
Does anyone have a suloution to my problem? SA :-) ?
What I have tried:
i tried working with background-worker, i chaned all of the settings of the dispatcher, loaded the user control inside and outside the threads...
I am looking for some help with my MultiSourceFrameArrived event in the Kinect v2 SDK.
The following is the method in question:
private async void _reader_MultiSourceFrameArrived(object sender, MultiSourceFrameArrivedEventArgs e)
{
MultiSourceFrame multiSourceFrame = e.FrameReference.AcquireFrame();
using (var colorFrame = multiSourceFrame.ColorFrameReference.AcquireFrame())
{
if (colorFrame != null)
{
_writeableBitmap.Lock();
colorFrame.CopyConvertedFrameDataToIntPtr(
_writeableBitmap.BackBuffer,
(uint)(_colorFrameDescription.Width * _colorFrameDescription.Height * _colorFrameDescription.BytesPerPixel),
ColorImageFormat.Bgra);
_writeableBitmap.AddDirtyRect(new Int32Rect(0, 0, _writeableBitmap.PixelWidth, _writeableBitmap.PixelHeight));
_writeableBitmap.Unlock();
reflectionOverlayControl.ReflectionImageSource = _writeableBitmap;
}
}
using (var bodyFrame = multiSourceFrame.BodyFrameReference.AcquireFrame())
{
if (bodyFrame != null)
{
Body body = JointHelpers.FindClosestBody(bodyFrame);
if (body != null)
{
if (body.IsTracked)
{
Dictionary<BodyComponentType, BodyComponent> bodyComponentDictionary = BuildBodyComponentDictionary(body);
foreach (BodyComponent bodyComponent in bodyComponentDictionary.Values.OrderBy(x => x.BodyComponentType))
{
bodyComponent.Generate(_writeableBitmap, _coordinateMapper, FrameType.Color, 25);
if (!_isRunningFiltering)
{
_isRunningFiltering = true;
try
{
await Task.Run(() =>
{
bodyComponent.RunFunFiltering();
});
}
finally
{
_isRunningFiltering = false;
}
}
}
reflectionOverlayControl.UpdateValues(
bodyComponentDictionary,
GetFullBodyComponent(body));
}
}
}
}
}
Now, allow me to explain:
The method runs when a particular kind of frame arrives from the Kinect, this is acquired and I can extract the ColorFrame and BodyFrame out of it in the using blocks.
The first "using" block turns the ColorFrame into a WPF WriteableBitmap (declared in the constructor) and sets a user control's ReflectionImageSource set equal to this WriteableBitmap. If this were the only using block, I would see a very smooth feed on the screen!
The second BodyFrame using determines the closest body, if it is tracked and then creates a Dictionary populated with a persons BodyComponents (hands, feet, head etc.)
The foreach loop here runs the "Generate" function on each BodyComponent, which sets a few of it's properties. For example, it sets an EncompassingRectangle property which is an Int32Rect object designed to encompass the component.
The next bit is where I need help!
The method RunFunFiltering is a heavily intensive processing method which, when run, would create a blocking statement that freezes up my UI. This would have the effect of making my color frame video feed very jumpy! This RunFunFiltering method needs to set some of the BodyComponent class's properties, such as the colour that the rectangle should be displayed, the number of white pixels in it's ReflectionImageSource and to set another writeable bitmap with the part of the first ReflectionImageSource which is contained in the rectangle.
Since this object is now complete, with all properties set (and this has been done for each of the BodyComponent's in the dictionary) I run an UpdateValues method on the view, which displays the interesting stuff in the BodyComponent class on the screen for me.
Following some advice from #sstan in this post: Async Await to Keep Event Firing
I threw in a Task.Run() block. However, this doesn't seem to be releasing my UI and I still see a jumpy image. The weird thing is in that timer example, that it works perfectly! I'm at a bit of a loss here to know what to do.
I'm a bit of a beginner with asynchronous functions but I would really like to understand your solutions. If you can provide an explanation with your code I'd be extremely grateful!
Update
I have been able to identify that the using statement which acquires the frame blocks the UI when it is placed outside of the Task.Run call.
I can't just make the whole BodyFrame using block run asynchronously because I need the first "Generate" function to always happen and not be part of the heavy processing thread. Two using blocks seems inelegant and is rather pushing my question under the carpet...
From your comment I understand the following:
You have an async function that is called when a frame arrives
If no RunFunFiltering task is running start one
If such a task is running, don't start a new one
If RunFunFiltering is finished Process the result
.
Task taskFunFiltering = null;
private async Task ProcessFrame(...)
{ // a new frame is arrived
DoSomeProcessing(...);
// only start a new run fun filtering if previous one is finished
if (taskFunFiltering == null || taskFunFiltering.IsCompleted)
{ // start a new fun filtering
// don't wait for the result
taskFunFiltering = Task.Run( () => ...);
}
}
private async Task RunFunFiltering(...)
{
// do the filtering and wait until finished
var filterResult = await DoFiltering(...);
DisplayResult(filterResult);
}
I'm having the same problem posed here:
http://social.msdn.microsoft.com/Forums/wpapps/en-us/af8615e7-8e90-4069-aa4d-3c4a84a6a3d0/windows-phone-8-fast-app-resume-with-deeplinks?forum=wpdevelop
I'm no C# or WP expert, so please bear with me.
I have secondary tiles which link to "/MainPage.xaml?id=XX".
I have fast app resume enabled. (ActivationPolicy="Resume" in the app manifest)
I only have one page in my app: MainPage.xaml.
Problem: When I resume the app using a secondary tile ("/MainPage.xaml?id=XX"), I get a brief view of the previous instance (that would have resumed) and then the MainPage initializes again, creating a new instance. In effect, the app is loading from scratch after giving me a peek of what was previously open.
That is obviously undesired behavior. I want to use the existing instance to perform my task.
Attempt 1:
Use e.Cancel = true; to cancel the navigation to the MainPage.xaml:
(using the App.xaml.cs code from the official Fast App Resume sample to identify how the app was launched)
...
else if (e.NavigationMode == NavigationMode.New && wasRelaunched)
{
// This block will run if the previous navigation was a relaunch
wasRelaunched = false;
if (e.Uri.ToString().Contains("="))
{
// This block will run if the launch Uri contains "=" (ex: "id=XX") which
// was specified when the secondary tile was created in MainPage.xaml.cs
sessionType = SessionType.DeepLink;
e.Cancel = true; // <======================== Here
// The app was relaunched via a Deep Link.
// The page stack will be cleared.
}
}
...
Problem: In doing so, my OnNavigatedTo event handlers never fire, so my query string is never parsed.
protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
{
String navId;
if (e.NavigationMode != System.Windows.Navigation.NavigationMode.Back)
{
if (NavigationContext.QueryString.TryGetValue("id", out navId))
{
MessageBox.Show(navId.ToString()); // Not reached
}
}
...
Attempt 2:
Use e.Cancel = true; to cancel the navigation to the MainPage.xaml, AND pass the Uri to a method in MainPage:
// App.xaml.cs
...
else if (e.NavigationMode == NavigationMode.New && wasRelaunched)
{
// This block will run if the previous navigation was a relaunch
wasRelaunched = false;
if (e.Uri.ToString().Contains("="))
{
// This block will run if the launch Uri contains "=" (ex: "id=XX") which
// was specified when the secondary tile was created in MainPage.xaml.cs
sessionType = SessionType.DeepLink;
e.Cancel = true;
MainPage.GoToDeepLink(e.Uri); // <======================== Here
// The app was relaunched via a Deep Link.
// The page stack will be cleared.
}
}
...
// MainPage.xaml.cs
public static void GoToDeepLink(Uri uri) // <======================== Here
{
// Convert the uri into a list and navigate to it.
string path = uri.ToString();
string id = path.Substring(path.LastIndexOf('=') + 1);
MyList list = App.ViewModel.ListFromId(Convert.ToInt32(id));
pivotLists.SelectedItem = list;
}
Problem: I get an error that pivotLists is non-static and thus requires an object reference. I think that in order to get this to work I'd need to create a new instance of MainPage (MainPage newMainPage = new MainPage();) and call newMainPage.pivotLists.SelectedItem = list; -- BUT I don't know how to use newMainPage instead of the existing one/replace it... or if that's something I want/won't cause further problems/complications.
I don't know what the solution is to this problem, and I may be going in the completely wrong direction. Please keep all suggestions in simple terms with code examples if you can, I'm still learning.
Thanks for any help.
It seems that when you reopen your App from secondary tile, then it's reactivated and new instance of MainPage is created (even if there is one from previous run). If I understood you correctly, I've managed to do such a thing:
In app.xaml.cs:
I've added a variable which indicates if I should return to previous MainPage after Navigating from secondary tile - it needs to be static as I want to have access to it from MainPage
public static bool returnPage = false;
In RootFrame_Navigating I'm setting this variable to true in:
// ...
else if (e.NavigationMode == NavigationMode.New && wasRelaunched)
{
// This block will run if the previous navigation was a relaunch
wasRelaunched = false;
returnPage = true;
// ...
In ClearBackStackAfterReset - prevent from deleting the old Page, when returning:
// ...
if (e.NavigationMode != NavigationMode.New || returnPage)
return;
// ...
In MainPage.cs:
I've changed a little constructor, as I don't want to see a blink of a new Page:
public MainPage()
{
if (!App.returnPage)
InitializeComponent();
}
In MainPage I've also variable which is passed from secondary tile - it's also static, as I need only one instance of it:
private static string navId = "";
And the core of the trick - OnNavigatedTo:
protected override void OnNavigatedTo(NavigationEventArgs e)
{
if (App.returnPage)
{
App.returnPage = false;
NavigationContext.QueryString.TryGetValue("id", out navId);
NavigationService.GoBack();
}
else if (e.NavigationMode != NavigationMode.Reset)
{
// normal navigation
}
}
It works like this:
when you launch normally your App, returnPage is false, everything goes normal
when you activate it from secondary tile few things happen:
1. first goes navigation to your previous page with NavigationMode.Reset - we are not interested in it, so I switched it off - nothing should happen
2. then program tries to create new instance of MainPage, but returnPage is true, and because of the if statement, InitializeComponent won't run. Just after this, in OnNavigatedTo, program saves passed querystring and Navigates Back to previous instance of MainPage - from previous run
3. at last we are navigating to right MainPage with NavigationMode.Back and we have our querystring saved in static variable.
You must be aware of two things: first - probably it can be little rebuild (I'm not sure if wasRelaunched is needed and so on) - you need to debug it and see of what you can get rid off. Second - you will probably need to test your App with Tombstone case.
Hope this helps.
I have an Internet radio app that uses BackgroundAudioPlayer.
I need a timer in the Audio Playback Agent that will update the track title of the currently playing track of the BAP that is pulled from the Internet radio station's API.
Adding a DispatcherTimer into the Audio Playback Agent gives me a cross-thread exception, and using:
Deployment.Current.Dispatcher.BeginInvoke(() =>
{
// Code
});
Didn't work.
I need the code in here because if I put the update code in the app itself, when the user navigates away from the app the updates stop (much unlike Windows 8's behavior).
I can't use Scheduled Agents since they only run once every 30 minutes (IIRC).
Is this possible or can this not be done on Windows Phone?
Below is an excerpt from the MSDN documentation for Background Audio Player:
Sending messages between tasks:
There are times when you will want to communicate between the two processes of a background audio app. For example, you might want the background task to notify the foreground task when a new track starts playing, and then send the new song title to the foreground task to display on the screen. A simple communication mechanism raises events in both the foreground and background processes. The SendMessageToForeground and SendMessageToBackground methods each invoke events in the corresponding task. Data can be passed as an argument to the event handler in the receiving task. Pass data using a new class called ValueSet. This class is a dictionary that contains a string as a key and other value types as values. You can pass simple value types such as int, string, bool, and so on.
https://msdn.microsoft.com/en-US/library/windows/apps/xaml/dn642090
Hope this helps!
I found a question which could help you : How to run a timer on background in windows phone 8?
when you set a timer which is checking every x seconds if the "title" differs from last known title then you could send this info back to it.
This could be the Code for the Timer:
Declare these:
string _newValue = string.Empty;
string _currentValue = string.Empty;
AudioTrack _tempTrack = null;
and set this as Tick for the Timer
if (this.BackgroundAudioPlayer != null)
{
if (this.BackgroundAudioPlayer.Instance != null)
{
if (this.BackgroundAudioPlayer.Instance.Track != null)
{
this._newValue= yourAPI.GetTitleOfTrack();
try
{
/* First try to get the current Track as own Var */
this._tempTrack = this.BackgroundAudioPlayer.Instance.Track;
if (this._tempTrack != null)
{
/* Then Read the .Tag Value from it, save to _currentValue */
if (this._tempTrack.Tag != null)
{ this._currentValue = this._tempTrack.Tag.ToString(); }
else
{ this._currentValue = string.Empty; }
/* Compare */
if (this._currentValue != this._newValue)
{
/* Edit the Track Tag from your original BAP */
this.BackgroundAudioPlayer.Instance.Track.Tag = this._newValue;
}
}
}
catch(Exception ex)
{
/* if something Crashes you can save the exception error for protocol */
}
}
}
}
Remember: Change "yourAPI.GetTitleOfTrack()"-Function from this with real Function Call of your API.
Have you considered updating the information in the background audio player agent as below in the track tag.
string newTag = "whatever you need to show";
AudioTrack track = BackgroundAudioPlayer.Instance.Track;
track.BeginEdit();
track.Tag = newTag;
track.EndEdit();
and then reading that tag in the front end by your application when needed?
I have the following code:
ShowPoup();
if (_watcher == null)
{
_watcher = new GeoCoordinateWatcher(GeoPositionAccuracy.High);
_watcher.MovementThreshold = 15; // use MovementThreshold to ignore noise in the signal
_watcher.StatusChanged += new EventHandler<GeoPositionStatusChangedEventArgs>(watcher_StatusChanged);
}
if (!_watcher.TryStart(true, TimeSpan.FromSeconds(3)))
{
MessageBox.Show("Please turn on location services on device under Settings.");
//HidePopup();
}
My problem is that the popup doesn't appear until after the _watcher.TryStart() method returns. The point of the popup is to show a loading overlay to tell the user the app is doing something. It's pointless to have it show after the work is done, at which point I hide the popup, so the user never sees anything.
I have this popup code throughout the app and this is the first time I've encountered this issue. Even if I call ShowPopup() in a separate method before calling the current method, it still doesn't show until after _watcher starts. I'm not sure why this is happening.
It looks like you are blocking the UI thread during the TryStart. If you can move the watcher initialization to a background thread (e.g. to the threadpool) then you can keep the display "alive".
Something like:
ShowPoup();
if (_watcher == null)
{
_watcher = new GeoCoordinateWatcher(GeoPositionAccuracy.High);
_watcher.MovementThreshold = 15; // use MovementThreshold to ignore noise in the signal
_watcher.StatusChanged += new EventHandler<GeoPositionStatusChangedEventArgs>(watcher_StatusChanged);
}
System.Threading.ThreadPool.QueueUserWorkItem((ignored) =>
{
if (!_watcher.TryStart(true, TimeSpan.FromSeconds(3)))
{
Dispatcher.BeginInvoke(() =>
{
HidePopup();
MessageBox.Show("Please turn on location services on device under Settings.");
}
});
});