Close secondary view completely in UWP - c#

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?
Here is my code to create the secondary view.
await CoreApplication.CreateNewView().Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
var frame = new Frame();
frame.MinHeight = 200;
frame.MinWidth = 200;
compactViewId = ApplicationView.GetForCurrentView().Id;
frame.Navigate(typeof(CompactNowPlayingPage), caption);
Window.Current.Content = frame;
Window.Current.Activate();
ApplicationView.GetForCurrentView().Title = Title;
});
bool viewShown = await ApplicationViewSwitcher.TryShowAsViewModeAsync(compactViewId, ApplicationViewMode.Default);
Update
After some debugging I've come to know that close button pressed on the secondary view only hides the view but it keeps on running on its thread, I just want that close button to completely close the secondary view, close its thread and destroy the window as a whole.
Update 2
I followed windows samples multiple views and was able to complete all steps, the code runs fine until it reaches Windows.Current.Close() in released event.
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? Here is the exception:
Message = "COM object that has been separated from its underlying RCW cannot be used."
Update 3
This is the latest updated, I am not following official sample now, just following simpler approach now.
Code to open secondary view:
await Helpers.DeviceTypeHelper.CompactOpen(e.ClickedItem as Video, identifier); //where identified is just a string for some custom logic in the secondary view.
//following method is located in a helper class within the project
internal static async Task CompactOpen(Video PlayingVideo, string caption)
{
ApplicationView newView = null;
await CoreApplication.CreateNewView().Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
var frame = new Frame();
frame.Navigate(typeof(CompactNowPlayingPage),new object[] { PlayingVideo,caption});
Window.Current.Content = frame;
Window.Current.Activate();
newView = ApplicationView.GetForCurrentView();
newView.Title = PlayingVideo.MyVideoFile.DisplayName;
});
await ApplicationViewSwitcher.TryShowAsStandaloneAsync(newView.Id);
}
Secondary View:
public sealed partial class CompactNowPlayingPage : Page
{
public CompactNowPlayingViewModel ViewModel { get; } = new CompactNowPlayingViewModel();
private CustomMediaTransportControls controls;
public CompactNowPlayingPage()
{
InitializeComponent();
this.Loaded += MediaPage_Loaded;
this.Unloaded += MediaPage_Unloaded;
Microsoft.Toolkit.Uwp.UI.Extensions.ApplicationView.SetExtendViewIntoTitleBar(this, true);
Microsoft.Toolkit.Uwp.UI.Extensions.TitleBar.SetButtonBackgroundColor(this, Colors.Transparent);
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
string chk = "";
var paramm = e.Parameter as object[];
NowPlayingVideo = paramm[0] as Video;
var vis = Visibility.Collapsed;
chk = paramm[1].ToString();
switch (chk)
{
case "library":
vis = Visibility.Visible;
break;
case "playlist":
vis = Visibility.Visible;
break;
case "history":
vis = Visibility.Collapsed;
break;
case "directplay":
vis = Visibility.Collapsed;
break;
default:
break;
}
controls = new CustomMediaTransportControls(NowPlayingVideo,vis);
Media.TransportControls = controls;
PlayVideo();
}
private Video NowPlayingVideo { get; set; }
private void PlayVideo()
{
if (NowPlayingVideo != null)
{
string token = "";
if (StorageApplicationPermissions.FutureAccessList.Entries.Count == 800)
{
var en = StorageApplicationPermissions.FutureAccessList.Entries;
StorageApplicationPermissions.FutureAccessList.Remove(en.Last().Token);
}
token = StorageApplicationPermissions.FutureAccessList.Add(NowPlayingVideo.MyVideoFile);
Media.Source = null;
Media.Source = $"winrt://{token}";
SetViews();
}
}
private void SetViews()
{
NowPlayingVideo.Views++;
Database.DbHelper.UpdateViews(NowPlayingVideo.MyVideoFile.Path);
}
private void MediaPage_Loaded(object sender, RoutedEventArgs e)
{
Windows.UI.ViewManagement.ApplicationView.GetForCurrentView().Consolidated += MediaPage_Consolidated;
}
private void MediaPage_Unloaded(object sender, RoutedEventArgs e)
{
Windows.UI.ViewManagement.ApplicationView.GetForCurrentView().Consolidated -= MediaPage_Consolidated;
}
private void MediaPage_Consolidated(Windows.UI.ViewManagement.ApplicationView sender, Windows.UI.ViewManagement.ApplicationViewConsolidatedEventArgs args)
{
Window.Current.Close();
}
}
Secondary View XAML:
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<vlc:MediaElement AreTransportControlsEnabled="True"
Name="Media"
HardwareAcceleration="True"
AutoPlay="True">
</vlc:MediaElement>
</Grid>
Case 1 : Everything runs perfect if I place a video file in Assets folder and give it as a source to the media element and comment the whole OnanvigatdTo method on secondary page. And I am able to successfully close the window as well.
...
Case 2 : But when I try to set the media through the NowPlayingVideo object as shown in the code above and I also use default Transport Controls, so I don't comment the lines used to assign custom transport controls in the above code it runs fine, but when I then try to close the window I get following exception in App.i.g.cs file but stacktrace doesn't exist:
Message = "Attempt has been made to use a COM object that does not have a backing class factory." Message = "COM object that has been separated from its underlying RCW cannot be used.
Case 3 : Exactly like case 2 but here I uncomment Custom transport controls lines so now I am assigning custom transport controls to my media element, this time exception is a bit different with some stacktrace as well
StackTrace = " at System.StubHelpers.StubHelpers.GetCOMIPFromRCW_WinRT(Object objSrc, IntPtr pCPCMD, IntPtr& ppTarget)\r\n at Windows.UI.Xaml.DependencyObject.get_Dispatcher()\r\n at VLC.MediaElement.d__160.MoveNext()\r\n--- End of stack trace ...
Message = "Attempt has been made to use a COM object that does not have a backing class factory."

The short answer is: you need to make sure nothings holds on to your view instance, and you call Window.Close in the view's Consolidated event. The longer answer with code is here in the official sample. Take a look at the ViewLifetimeControl.cs source file: https://github.com/Microsoft/Windows-universal-samples/tree/master/Samples/MultipleViews/cs

Related

How can I pop a navigation page when a value changes in a static class?

So I have a bit of a weird problem.
I've implemented a camera preview class (largely following this code here: https://learn.microsoft.com/en-us/samples/xamarin/xamarin-forms-samples/customrenderers-view/) but have added a button at the bottom to take a picture. This involves the use of both some xamarin forms code and some xamarin android code.
However, the CapturePage is only put on the stack when the user announces that they want to take a photo, and after the photo has been taken, I want to pop the Capture page to go back to the main screen. Currently, I have a static boolean value in the overall project that is changed from false to true when a capture has occurred. Is there some way to get my code in Main.xaml.cs to wait on this value changing, then pop whatever is on top of the navigation stack? Is this a use for a property changed? See code below:
The code in Project.Droid that handles the capturing and saving of the image:
void OnCapButtonClicked(object sender, EventArgs e)
{
// capButton.capture is an instance of Android.Hardware.Camera
capButton.capture.TakePicture(null, null, this);
// stop the preview when the capture happens
CameraInfoContainer.isPreviewing = false;
}
public void OnPictureTaken(byte[] data, Camera camera)
{
var filepath = string.Empty;
var clientInstanceId = Guid.NewGuid().ToString();
var saveLoc = Android.OS.Environment.GetExternalStoragePublicDirectory(Android.OS.Environment.DirectoryPictures);
filepath = System.IO.Path.Combine(saveLoc.AbsolutePath, clientInstanceId + ".jpg");
try
{
System.IO.File.WriteAllBytes(filepath, data);
//mediascan adds the saved image into the gallery
var mediaScanIntent = new Intent(Intent.ActionMediaScannerScanFile);
mediaScanIntent.SetData(Android.Net.Uri.FromFile(new File(filepath)));
Forms.Context.SendBroadcast(mediaScanIntent);
}
catch (Exception e)
{
System.Console.WriteLine(e.ToString());
}
// CameraInfoContainer is a static class in Project NOT in Project.Droid
CameraInfoContainer.savedCapture = filepath;
CameraInfoContainer.capType = CaptureType.Photo;
CameraInfoContainer.captureComplete = true; // here is where I set the value (in Project)
}
Now the code in Project that pushes the capture page on the stack and that I want to trigger when the capture has happened:
// this method puts the capture page on the stack and starts the whole process
private async Task ExecuteNewCapture()
{
var cp = new CapturePage();
var np = new NavigationPage(cp);
await Navigation.PushModalAsync(np, true);
}
// this is the method that I want to trigger when a photo is taken (in Project/Main.xaml.cs)
private async Task Complete(string fileLoc)
{
await Navigation.PopModalAsync();
}
Answer is in a comment from Junior Jiang. Ended up using Xamarin.Forms.MessagingCenter to get done what I needed.

How to add multiple instances of the native add UWP?

I have an UWP app which displays several catalogs of videos. They are located in separate pages. For this purpose I've created an NativeAdV2 control:
public sealed partial class CardAdvert : UserControl
{
NativeAdsManagerV2 manager = new NativeAdsManagerV2("d25517cb-12d4-4699-8bdc-52040c712cab", "test");
NativeAdV2 advert;
public CardAdvert()
{
InitializeComponent();
manager.AdReady += AdReady;
manager.RequestAd();
}
private void AdReady(object sender, NativeAdReadyEventArgs e)
{
advert = e.NativeAd;
Initialize();
e.NativeAd.RegisterAdContainer(this); //Exception is here
}
public void Initialize()
{
title.Text = advert.Title;
image.Source = new BitmapImage(advert.MainImages.First().Url.ToUri());
if (advert.AdIcon == null)
contentGrid.ColumnDefinitions[0].Width = new GridLength(0);
else
icon.ProfilePicture = advert.AdIcon.Source;
if (string.IsNullOrWhiteSpace(advert.SponsoredBy))
sponsor.Visibility = Visibility.Collapsed;
else
sponsor.Text = advert.SponsoredBy;
if (!string.IsNullOrWhiteSpace(advert.Rating))
info.Text += $" {advert.Rating}";
if (string.IsNullOrWhiteSpace(advert.CallToActionText) && string.IsNullOrWhiteSpace(advert.Price))
desc.Visibility = Visibility.Collapsed;
else if (!string.IsNullOrWhiteSpace(advert.CallToActionText))
desc.Text = advert.CallToActionText;
else
desc.Text = advert.Price;
}
}
But no matter where I create it (even on different page) on the second or third time it throws me an exception on e.NativeAd.RegisterAdContainer(this):
Unhandled exception at 0x082A1330 (Windows.UI.Xaml.dll) in FoxTube.exe:
0xC000027B: An application-internal exception has occurred (parameters: 0x1E9F4608, 0x00000003)
There is no anything similar neither on MSDN forum or elsewhere nor in NativeAdV2 class documentation
Perfectly, I'd like to insert this control every 10 videos. Or at least on every page. Is there any solutions?
Actually, I don't know why Microsoft doesn't want to fix this but as they said it works on earlier builds. You just need to downgrade your target and minimal versions to 17134 and it will work. You can also use Windows UI Library to get latest XAML controls

UWP StoreContext provides inaccurate information in RELEASE version

I have an app, published in Windows Store and the app has a trial version. But recently the trial version provides the full version somehow.
When I debugged the code, it shows that the following piece of code gets called twice in the release version and once it says that Trial is true and secondly it says Trial is false.
if (storeContext == null)
{
storeContext = StoreContext.GetDefault();
}
appLicense = await storeContext.GetAppLicenseAsync();
if (appLicense.IsActive)
{
if (appLicense.IsTrial)
{
isTrial = true;
}
else
{
isTrial = false;
}
}
When I debug the code in Release version it appLicense.IsTrial gets evaluated two times and that's how it produces two different results. Is it a bug? Should I tell Microsoft about this? Or is it just related to me somehow? If you try my app from store here you can probably see that it provides full access to the application and that the search is available to use while it's disabled in Trial version. Please help me with this.
Update:
The ExtendedSplash code:
public sealed partial class ExtendedSplash : Page
{
internal Rect splashImageRect; // Rect to store splash screen image coordinates.
private SplashScreen splash; // Variable to hold the splash screen object.
internal bool dismissed = false; // Variable to track splash screen dismissal status.
internal Frame rootFrame;
// Define methods and constructor
public ExtendedSplash(SplashScreen splashscreen, bool loadState)
{
InitializeComponent();
// Listen for window resize events to reposition the extended splash screen image accordingly.
// This ensures that the extended splash screen formats properly in response to window resizing.
Window.Current.SizeChanged += new WindowSizeChangedEventHandler(ExtendedSplash_OnResize);
splash = splashscreen;
if (splash != null)
{
// Register an event handler to be executed when the splash screen has been dismissed.
splash.Dismissed += new TypedEventHandler<SplashScreen, Object>(DismissedEventHandler);
// Retrieve the window coordinates of the splash screen image.
splashImageRect = splash.ImageLocation;
//PositionImage();
// If applicable, include a method for positioning a progress control.
//PositionRing();
}
// Create a Frame to act as the navigation context
rootFrame = new Frame();
}
void PositionImage()
{
//extendedSplashImage.SetValue(Canvas.LeftProperty, splashImageRect.X);
//extendedSplashImage.SetValue(Canvas.TopProperty, splashImageRect.Y);
//extendedSplashImage.Height = splashImageRect.Height;
//extendedSplashImage.Width = splashImageRect.Width;
}
void PositionRing()
{
splashProgressRing.SetValue(Canvas.LeftProperty, splashImageRect.X);// + (splashImageRect.Width * 0.5) - (splashProgressRing.Width * 0.5));
splashProgressRing.SetValue(Canvas.TopProperty, (splashImageRect.Y + splashImageRect.Height));// + splashImageRect.Height * 0.1));
}
// Include code to be executed when the system has transitioned from the splash screen to the extended splash screen (application's first view).
async void DismissedEventHandler(SplashScreen sender, object e)
{
dismissed = true;
// Complete app setup operations here...
await TrialManager.IsTrialLicense();
// this is the code that gets called to see the Trial / Purchase
DismissExtendedSplash();
}
async void DismissExtendedSplash()
{
await Windows.ApplicationModel.Core.CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(CoreDispatcherPriority.Normal,
() =>
{
// Navigate to mainpage
rootFrame.Navigate(typeof(IndexPage));
// Place the frame in the current Window
Window.Current.Content = rootFrame;
});
}
void ExtendedSplash_OnResize(Object sender, WindowSizeChangedEventArgs e)
{
// Safely update the extended splash screen image coordinates. This function will be executed when a user resizes the window.
if (splash != null)
{
// Update the coordinates of the splash screen image.
splashImageRect = splash.ImageLocation;
//PositionImage();
// If applicable, include a method for positioning a progress control.
//PositionRing();
}
}
void RestoreStateAsync(bool loadState)
{
if (loadState)
{
// code to load your app's state here
}
}
}

How to set the navigation URI while creating a secondary tile? (Windows Phone 8.1 - Universal)

Let's say I have a specific page, SecondaryTile.xaml. From this page I pin a secondary tile to the startscreen. Now if I tap on the secondary tile I want it to open the SecondaryTile.xaml.
In WP8.0 this was possible by setting the URI of Shell.Create. E.g.:
ShellTile.Create(new Uri("/SecondaryTile.xaml?Parameter=FromTile", UriKind.Relative), NewTileData);
}
But it looks like this is not supported anymore in WinRT.
I saw a sample that uses the parameter to launch (it fetches the parameter in the OnNavigatedTo on the Mainpage.xaml.cs), but with the new app behaviour the app is being suspended so OnNavigatedTo does not always trigger.
Hope someone can help.
Kind regards,
Niels
Shouldn't this be done as application logic?
Examine the LaunchActivatedEventArgs parameter within the OnLaunched method of your app's code-behind:
protected override async void OnLaunched(LaunchActivatedEventArgs args)
{
ApplicationData.Current.LocalSettings.Values[Constants.APP_PARAMETERS] = args.Arguments;
// Do not repeat app initialization when already running, just ensure that
// the window is active
if (args.PreviousExecutionState == ApplicationExecutionState.Running)
{
Window.Current.Activate();
await ViewManager.Instance.LaunchView();
return;
}
Consider implementing some type of ViewManager for managing the startup view:
public class ViewManager
{
#region Singleton
private ViewManager()
{
}
static ViewManager _viewManager = null;
public static ViewManager Instance
{
get
{
if (_viewManager == null)
{
_viewManager = new ViewManager();
}
return _viewManager;
}
}
#endregion
public async Task LaunchView()
{
bool displaySubheader = false;
var displayBackbutton = false;
var arguments = ApplicationData.Current.LocalSettings.Values[Constants.APP_PARAMETERS] as string;
var argumentsExist = !string.IsNullOrEmpty(arguments);
if (!argumentsExist)
{
await UIServices.Instance.Load(typeof(HomePage), null, displaySubheader, displayBackbutton);
}
else
{
displaySubheader = true;
displayBackbutton = false;
await UIServices.Instance.Load(typeof(GroupPage), arguments, displaySubheader, displayBackbutton);
var groupId = new Guid(arguments);
await ReadPost(groupId);
}
}
.
.
.
Here's how I create secondary tiles:
SecondaryTile secondaryTile =
new SecondaryTile(group.GroupId.ToString(),
group.Name,
group.Name,
group.GroupId.ToString(),
TileOptions.ShowNameOnWideLogo,
new Uri("ms-appx:///Assets/Logo.png"),
new Uri("ms-appx:///Assets/WideLogo.scale-100.png"));
var successful = await secondaryTile.RequestCreateAsync();
You are not able to navigate directly to another page from the OnNavigatedTo event. However you can add the navigation as a queued event on your UI thread.
In your OnNavigatedTo eventhandler, check for the following (Your test might need to be a bit more sophisticated as not all eventualities are accounted for in this example, such as for example e.Parameters beeing null).
if (e.Parameter.ToString().Contains("something_from_secondary_tile_arguments") && (e.NavigationMode == NavigationMode.New))
{
await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => { Current.Frame.Navigate(typeof(Transactions), "data_for_your_sub_page"); });
}
Also, you need to specify the Arguments property when you create your SecondaryTile.
More info here: Not able to navigate to pages on Windows Metro App using c#

Windows Phone 8 canĀ“t call AudioVideoCaptureDevice second time

In my App you can open a Site where you can switch on and off the Flashlight.
The first time it works, but if I try to switch the flashlight on a second time the App crashes.
I think this is a Problem with AudioVideoCaptureDevice.OpenAsync. If I call it a second time the App crashes with a System.Reflection.TargetInvocationException WinRT-Informationen: Unable to acquire the camera. You can only use this class while in the foreground.
Someone know this Problem?
protected AudioVideoCaptureDevice Device { get; set; }
public Page10()
{
InitializeComponent();
}
async void tglSwitch_Checked(object sender, RoutedEventArgs e)
{
var sensorLocation = CameraSensorLocation.Back;
if (this.Device == null)
{
// get the AudioVideoCaptureDevice
this.Device = await AudioVideoCaptureDevice.OpenAsync(sensorLocation,
AudioVideoCaptureDevice.GetAvailableCaptureResolutions(sensorLocation).First());
}
var supportedCameraModes = AudioVideoCaptureDevice
.GetSupportedPropertyValues(sensorLocation, KnownCameraAudioVideoProperties.VideoTorchMode);
if (supportedCameraModes.ToList().Contains((UInt32)VideoTorchMode.On))
{
this.Device.SetProperty(KnownCameraAudioVideoProperties.VideoTorchMode, VideoTorchMode.On);
// set flash power to maxinum
this.Device.SetProperty(KnownCameraAudioVideoProperties.VideoTorchPower,
AudioVideoCaptureDevice.GetSupportedPropertyRange(sensorLocation, KnownCameraAudioVideoProperties.VideoTorchPower).Max);
this.tglSwitch.Content = "Light on";
this.tglSwitch.SwitchForeground = new SolidColorBrush(Colors.Green);
}
}
void tglSwitch_Unchecked(object sender, RoutedEventArgs e)
{
var sensorLocation = CameraSensorLocation.Back;
sensorLocation = CameraSensorLocation.Back;
var supportedCameraModes = AudioVideoCaptureDevice
.GetSupportedPropertyValues(sensorLocation, KnownCameraAudioVideoProperties.VideoTorchMode);
if (this.Device != null && supportedCameraModes.ToList().Contains((UInt32)VideoTorchMode.Off))
{
this.Device.SetProperty(KnownCameraAudioVideoProperties.VideoTorchMode, VideoTorchMode.Off);
this.tglSwitch.Content = "Light off";
}
}
I would recommend to initialize the camera with OpenAsync ONE TIME in page lifecycle, for example in OnNavigatedTo event. And only makeSetProperty() methods calls code in your checkbox events to control light. It is also very important to dispose camera correctly then leaving the page, for example in OnNavigatedFrom event, by calling device.Dispose(). This option also make your flashlight to work faster.
Keep in mind that Windows Phone 8.1 now has dedicated API for torch, which works great and the code is more beautiful. You can use in Silverlight project as well, but you have to migrate your project. Here is more about this http://developer.nokia.com/community/wiki/Using_the_camera_light_in_Windows_Phone_7,_8_and_8.1 and https://msdn.microsoft.com/en-us/library/windows/apps/windows.media.devices.torchcontrol.

Categories