I am writing an app which should be able to run multiple views to edit different documents each in their own window. I've wrote some code which works, but I'm having some problems with it. The code I wrote is based upon the Multiple Views sample provided by Microsoft (https://github.com/Microsoft/Windows-universal-samples/tree/master/Samples/MultipleViews).
I have mainly two problems. The first one is if I close the main view, that is the first window that was open when the application was launched, then I cannot open any new views/windows by clicking in the app tile or opening an associated file type, until I close all views/windows and relaunch the app. The second one, is that when I try to open a new view/window from MainPage.xaml.cs, the app just crashes.
The code that I use to manage the views in App.xaml.cs is the following:
sealed partial class App : Application
{
//I use this boolean to determine if the application has already been launched once
private bool alreadyLaunched = false;
public ObservableCollection<ViewLifetimeControl> SecondaryViews = new ObservableCollection<ViewLifetimeControl>();
private CoreDispatcher mainDispatcher;
public CoreDispatcher MainDispatcher
{
get
{
return mainDispatcher;
}
}
private int mainViewId;
public int MainViewId
{
get
{
return mainViewId;
}
}
public App()
{
this.InitializeComponent();
this.Suspending += OnSuspending;
}
protected override async void OnLaunched(LaunchActivatedEventArgs e)
{
Frame rootFrame = Window.Current.Content as Frame;
if (rootFrame == null)
{
rootFrame = new Frame();
rootFrame.NavigationFailed += OnNavigationFailed;
if (e.PreviousExecutionState == ApplicationExecutionState.Terminated)
{
//TODO: Load state from previously suspended application
}
// Place the frame in the current Window
Window.Current.Content = rootFrame;
}
if (rootFrame.Content == null)
{
alreadyLaunched = true;
rootFrame.Navigate(typeof(MainPage), e.Arguments);
}
else if(alreadyLaunched)
{
var selectedView = await createMainPageAsync();
if (null != selectedView)
{
selectedView.StartViewInUse();
var viewShown = await ApplicationViewSwitcher.TryShowAsStandaloneAsync(
selectedView.Id,
ViewSizePreference.Default,
ApplicationView.GetForCurrentView().Id,
ViewSizePreference.Default
);
await selectedView.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
var currentPage = (MainPage)((Frame)Window.Current.Content).Content;
Window.Current.Activate();
});
selectedView.StopViewInUse();
}
}
// Ensure the current window is active
Window.Current.Activate();
}
protected override async void OnFileActivated(FileActivatedEventArgs args)
{
base.OnFileActivated(args);
if (alreadyLaunched)
{
//Frame rootFrame = Window.Current.Content as Frame;
//((MainPage)rootFrame.Content).OpenFileActivated(args);
var selectedView = await createMainPageAsync();
if (null != selectedView)
{
selectedView.StartViewInUse();
var viewShown = await ApplicationViewSwitcher.TryShowAsStandaloneAsync(
selectedView.Id,
ViewSizePreference.Default,
ApplicationView.GetForCurrentView().Id,
ViewSizePreference.Default
);
await selectedView.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
var currentPage = (MainPage)((Frame)Window.Current.Content).Content;
Window.Current.Activate();
currentPage.OpenFileActivated(args);
});
selectedView.StopViewInUse();
}
}
else
{
Frame rootFrame = new Frame();
rootFrame.Navigate(typeof(MainPage), args);
Window.Current.Content = rootFrame;
Window.Current.Activate();
alreadyLaunched = true;
}
}
partial void Construct();
partial void OverrideOnLaunched(LaunchActivatedEventArgs args, ref bool handled);
partial void InitializeRootFrame(Frame frame);
partial void OverrideOnLaunched(LaunchActivatedEventArgs args, ref bool handled)
{
// Check if a secondary view is supposed to be shown
ViewLifetimeControl ViewLifetimeControl;
handled = TryFindViewLifetimeControlForViewId(args.CurrentlyShownApplicationViewId, out ViewLifetimeControl);
if (handled)
{
var task = ViewLifetimeControl.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
Window.Current.Activate();
});
}
}
partial void InitializeRootFrame(Frame frame)
{
mainDispatcher = Window.Current.Dispatcher;
mainViewId = ApplicationView.GetForCurrentView().Id;
}
bool TryFindViewLifetimeControlForViewId(int viewId, out ViewLifetimeControl foundData)
{
foreach (var ViewLifetimeControl in SecondaryViews)
{
if (ViewLifetimeControl.Id == viewId)
{
foundData = ViewLifetimeControl;
return true;
}
}
foundData = null;
return false;
}
private async Task<ViewLifetimeControl> createMainPageAsync()
{
ViewLifetimeControl viewControl = null;
await CoreApplication.CreateNewView().Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
// This object is used to keep track of the views and important
// details about the contents of those views across threads
// In your app, you would probably want to track information
// like the open document or page inside that window
viewControl = ViewLifetimeControl.CreateForCurrentView();
viewControl.Title = DateTime.Now.ToString();
// Increment the ref count because we just created the view and we have a reference to it
viewControl.StartViewInUse();
var frame = new Frame();
frame.Navigate(typeof(MainPage), viewControl);
Window.Current.Content = frame;
// This is a change from 8.1: In order for the view to be displayed later it needs to be activated.
Window.Current.Activate();
//ApplicationView.GetForCurrentView().Title = viewControl.Title;
});
((App)App.Current).SecondaryViews.Add(viewControl);
return viewControl;
}
void OnNavigationFailed(object sender, NavigationFailedEventArgs e)
{
throw new Exception("Failed to load Page " + e.SourcePageType.FullName);
}
private void OnSuspending(object sender, SuspendingEventArgs e)
{
var deferral = e.SuspendingOperation.GetDeferral();
//TODO: Save application state and stop any background activity
deferral.Complete();
}
//I call this function from MainPage.xaml.cs to try to open a new window
public async void LoadNewView()
{
var selectedView = await createMainPageAsync();
if (null != selectedView)
{
selectedView.StartViewInUse();
var viewShown = await ApplicationViewSwitcher.TryShowAsStandaloneAsync(
selectedView.Id,
ViewSizePreference.Default,
ApplicationView.GetForCurrentView().Id,
ViewSizePreference.Default
);
await selectedView.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
var currentPage = (MainPage)((Frame)Window.Current.Content).Content;
Window.Current.Activate();
currentPage.LoadNewFile();
});
selectedView.StopViewInUse();
}
}
}
The code I use to try to launch a new view/window from MainPage.xaml.cs:
((App)App.Current).LoadNewView();
I've been reading the Microsoft documentation to try and understand what is the issue, but I still don't understand how exactly do Multiple Views work, like if the App class instantiated every time I open a new view/window.
I'd really appreciate the help.
Actually the proper way to still be able to open up new windows after the main one is closed is to use one of the overloads provided by TryShowAsStandaloneAsync.
protected override async void OnLaunched(LaunchActivatedEventArgs e)
{
// Create the newWindowId and stuff...
await ApplicationViewSwitcher.TryShowAsStandaloneAsync(newWindowId,
ViewSizePreference.Default,
e.CurrentlyShownApplicationViewId,
ViewSizePreference.Default);
Basically, you need to specify the third parameter anchorViewId which is
the ID of the calling (anchor) window.
In this case, you just need to pass in e.CurrentlyShownApplicationViewId.
I've found the solution to my problems, and I've actually decided not to use the ViewLifeTime control that comes with the sample.
The problem is that when the Main view is closed you have to use the Dispatcher.RunAsync() method from one of the other views that are still open to run it that thread
Here's the code that I've changed in my App.xaml.cs for anyone that is interested:
public bool isMainViewClosed = false;
public ObservableCollection<CoreApplicationView> secondaryViews = new ObservableCollection<CoreApplicationView>();
//...
protected override async void OnLaunched(LaunchActivatedEventArgs e)
{
Frame rootFrame = Window.Current.Content as Frame;
if (rootFrame == null)
{
rootFrame = new Frame();
rootFrame.NavigationFailed += OnNavigationFailed;
if (e.PreviousExecutionState == ApplicationExecutionState.Terminated)
{
//TODO: Load state from previously suspended application
}
Window.Current.Content = rootFrame;
}
if (rootFrame.Content == null)
{
alreadyLaunched = true;
rootFrame.Navigate(typeof(MainPage), e.Arguments);
}
else if(alreadyLaunched)
{
//If the main view is closed, use the thread of one of the views that are still open
if(isMainViewClosed)
{
int newViewId = 0;
await secondaryViews[0].Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
var currentPage = (MainPage)((Frame)Window.Current.Content).Content;
Window.Current.Activate();
currentPage.NewWindow();
newViewId = ApplicationView.GetForCurrentView().Id;
});
bool viewShown = await ApplicationViewSwitcher.TryShowAsStandaloneAsync(newViewId);
}
else
{
CoreApplicationView newView = CoreApplication.CreateNewView();
int newViewId = 0;
await newView.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
Frame frame = new Frame();
frame.Navigate(typeof(MainPage), null);
Window.Current.Content = frame;
var currentPage = (MainPage)((Frame)Window.Current.Content).Content;
Window.Current.Activate();
secondaryViews.Add(CoreApplication.GetCurrentView());
newViewId = ApplicationView.GetForCurrentView().Id;
});
bool viewShown = await ApplicationViewSwitcher.TryShowAsStandaloneAsync(newViewId);
}
}
Window.Current.Activate();
}
Optionally you can use multiple instances for your application. You can synchronize settings changes as I have described here.
don't View(your)Lifetime away ... cheers,
int idCreate = 0; List<int> idSaved = new List<int>();
protected override async void OnLaunched(LaunchActivatedEventArgs e)
{
Frame rootFrame = Window.Current.Content as Frame;
if (rootFrame == null)
{
rootFrame = new Frame();
rootFrame.NavigationFailed += OnNavigationFailed;
Window.Current.Content = rootFrame;
}
if (rootFrame.Content == null)
{
rootFrame.Navigate(typeof(MainPage), e.Arguments);
idSaved.Add(ApplicationView.GetForCurrentView().Id);
}
else
{
var create = CoreApplication.CreateNewView();
await create.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
var frame = new Frame();
frame.Navigate(typeof(MainPage), e.Arguments);
Window.Current.Content = frame;
Window.Current.Activate();
idCreate = ApplicationView.GetForCurrentView().Id;
});
for(int i = idSaved.Count - 1; i >= 0; i--)
if (await ApplicationViewSwitcher.TryShowAsStandaloneAsync(
idCreate, ViewSizePreference.UseMinimum,
idSaved[i], ViewSizePreference.UseMinimum)
) break;
idSaved.Add(idCreate);
}
Window.Current.Activate();
}
Related
I'm trying to make a tab appear for every file opened via File Explorer.
Expectations: upon double click on the file, it should open up a new tab for that file
Reality: every time I open a file, the frame gets overwritten.
MainPage - contains tabs
TabbedMainPage - the workspace
App.cs:
protected override void OnFileActivated(FileActivatedEventArgs Args)
{
base.OnFileActivated(Args);
Frame RF = Window.Current.Content as Frame;
if (RF != null)
{
var Y = new FrameNavigationOptions();
Y.IsNavigationStackEnabled = false;
RF.NavigateToType(typeof(MainPage), Args, Y);
}
else
{
RF = new Frame();
Window.Current.Content = RF;
RF.Navigate(typeof(MainPage), Args);
Window.Current.Activate();
}
}
MainPage.cs:
protected override void OnNavigatedTo(NavigationEventArgs EvArgs)
{
//Catch file
base.OnNavigatedTo(EvArgs);
var Args = EvArgs.Parameter as IActivatedEventArgs;
var FArgs = Args as FileActivatedEventArgs;
if (Args != null && Args.Kind == ActivationKind.File)
{
//Write file properties
TabViewItem NewTabItem = new TabViewItem();
NewTabItem.Header = "New Tab";
NewTabItem.IconSource = new Microsoft.UI.Xaml.Controls.SymbolIconSource() { Symbol = Symbol.Document };
Frame RF = new Frame();
RF.Navigate(typeof(TabbedMainPage), Args);
NewTabItem.Content = RF;
TabbedView.TabItems.Add(NewTabItem);
TabbedView.UpdateLayout();
}
else { }
}
TabbedMainPage.cs:
protected override async void OnNavigatedTo(NavigationEventArgs EvArgs)
{
//Catch file
base.OnNavigatedTo(EvArgs);
var Args = EvArgs.Parameter as IActivatedEventArgs;
var FArgs = Args as FileActivatedEventArgs;
if (Args != null && Args.Kind == ActivationKind.File)
{
//Write file properties
TXTFile = FArgs.Files[0] as StorageFile;
var Str = await TXTFile.OpenReadAsync();
//Read file
REB.Document.LoadFromStream(TextSetOptions.FormatRtf, Str);
RTB.Document.LoadFromStream(TextSetOptions.FormatRtf, Str);
Str.Dispose();
HomePage.Visibility = Visibility.Collapsed;
CheckForSaving();
}
else { }
}
public async Task<int> CreateNewViewAsync(Type viewModelType, object parameter = null)
{
int viewId = 0;
var newView = CoreApplication.CreateNewView();
await newView.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
viewId = ApplicationView.GetForCurrentView().Id;
var frame = new Frame();
var args = new ShellArgs
{
ViewModel = viewModelType,
Parameter = parameter
};
frame.Navigate(typeof(ShellView), args);
Window.Current.Content = frame;
Window.Current.Activate();
});
if (await ApplicationViewSwitcher.TryShowAsStandaloneAsync(viewId))
{
return viewId;
}
return 0;
}
How can convert this code from UWP to Windows App SDK?
Above code does not work in Windows SDK, If you want to show multiple view in Windows App SDK, it's easy implement than uwp, you just make new Windows instance and then call Activate method like the following.
private void myButton_Click(object sender, RoutedEventArgs e)
{
var window = new MainWindow();
window.Content = new TextBlock() { Text = "Hello World !" };
window.Activate();
}
I need to implement some functionality in a second Window in UWP.
So I want to do following
Start new Window in a Modal state so parent Window cannot be acceced.
On closed event return value to the parent Window.
I use following code to create a new Window please help me to archive these points
private async void Button_Click(object sender, RoutedEventArgs e)
{
var switchToView = true;
var newView = CoreApplication.CreateNewView();
int newViewId = 0;
await newView.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
var frame = new Frame();
frame.Navigate(typeof(MyCam), null);
Window.Current.Content = frame;
newViewId = ApplicationView.GetForCurrentView().Id;
});
var viewShown = await ApplicationViewSwitcher.TryShowAsStandaloneAsync(newViewId);
if (switchToView && viewShown)
{
// Switch to new view
await ApplicationViewSwitcher.SwitchAsync(newViewId);
}
}
I found nice solution here
https://github.com/bkaankose/UWPModalDialogHelper
It is about to use Prism.Core Nuget package to simulate fullscreen popup with custom UserControl.
I am using ZXing.Net camera barcode scanner, xamarin forms, and C# and it appears to be working good. But I have an issue where if I go the the next page via Navigation.PushAsync(), then click the Back Navigation Button, the ZXingScannerPage camera will not reload...(it will only be a still image of the last pic taken)...how do I reload the ZXingScannerPage so that the camera is actively upon pressing the Back Navigation? Is the anyway to refresh the camera view attached to the page?
Use the following code. Stop the scanning as soon as scanning is done. Don't do a manual manuver.
Entry objScanner= new Entry();
objScanner.Placeholder = "Barcode";
objScanner.Keyboard = Keyboard.Numeric;
objScanner.HorizontalOptions = LayoutOptions.StartAndExpand;
objScanner.WidthRequest = Application.Current.MainPage.Width - 40;
objScanner.SetBinding(Entry.TextProperty, "ElementValue", BindingMode.TwoWay);
objScanner.BindingContext = control;
layout.Children.Add(objScanner);
objScanner.Focused += async (s, e) =>
{
var scanPage = new ZXingScannerPage();
await Navigation.PushAsync(scanPage);
scanPage.OnScanResult += (result) =>
{
// Stop scanning
scanPage.IsScanning = false;
// Pop the page and show the result
Device.BeginInvokeOnMainThread(async () =>
{
await Navigation.PopAsync();
objScanner.Text = result.Text;
// await DisplayAlert("Scanned Barcode", result.Text, "OK");
});
};
};
The solution that I found that works for me to allow back navigation using ZXing Scanner page is to remove all instances of ZXing Scanner page before push a new instance of the page to the navigation stack. In your navigation.cs, when you get ready to push the page, use this:
foreach(var x in _navigation.Navigation.NavigationStack.ToList())
{
if((x.GetType() == typeof(/* name of your scanner page */)))
{
_navigation.Navigation.RemovePage(x);
}
}
var page = new /* your scanner page */();
_navigation.PushAsync( /* your scanner page */);
I have found a workaround which may be useful.
On content page, create a local content variable.
If I instantiate the scanner and add it to Content in OnAppearing method, then set Content = null OnDisappearing method. Nulling the Content seems to trigger the necessary cleanups up the stack.
Here's my code:
public class QrCodeScanPage : ZXingScannerPage
{
View _content;
public QrCodeScanPage()
{
InitScanner();
}
void InitScanner()
{
IsAnalyzing = true;
IsScanning = true;
DefaultOverlayTopText = "Align the barcode within the frame";
DefaultOverlayBottomText = string.Empty;
OnScanResult += ScanPage_OnScanResult;
Title = "Scan Code";
var item = new ToolbarItem
{
Text = "Cancel",
Command = new Command(async () =>
{
IsScanning = false;
await Navigation.PopAsync();
})
};
if (Device.RuntimePlatform != Device.iOS)
item.IconImageSource = "toolbar_close.png";
ToolbarItems.Add(item);
}
void ScanPage_OnScanResult(ZXing.Result result)
{
Device.BeginInvokeOnMainThread(async () =>
{
IsScanning = false;
IsAnalyzing = false;
await Navigation.PushAsync(new QrCodeScanResultPage());
});
}
protected override void OnAppearing()
{
IsScanning = true;
IsAnalyzing = true;
base.OnAppearing();
if (Content != null)
{
_content = Content;
}
if (Content == null)
{
Content = _content;
}
}
protected override void OnDisappearing()
{
base.OnDisappearing();
Content = null;
}
}
I am going to write a Video Recorder app in Windows Phone 8.1 (RT).
I used the example provided in Windows Reference.
public sealed partial class CamcorderMainPage : Page
{
StatusBar StatusBarObject = Windows.UI.ViewManagement.StatusBar.GetForCurrentView();
string StatusBarHeader = "Firefly Moments";
MediaCaptureInitializationSettings _captureInitSettings;
List<Windows.Devices.Enumeration.DeviceInformation> _deviceList;
Windows.Media.MediaProperties.MediaEncodingProfile _profile;
Windows.Media.Capture.MediaCapture _mediaCapture;
bool _recording = false;
bool _previewing = false;
string NoCameraError = "No camera device is found ";
public CamcorderMainPage()
{
this.InitializeComponent();
this.Loaded += CamcorderMainPage_Loaded;
}
void CamcorderMainPage_Loaded(object sender, RoutedEventArgs e)
{
//throw new NotImplementedException();
EnumerateCameras();
}
private async void EnumerateCameras()
{
var devices = await Windows.Devices.Enumeration.DeviceInformation.FindAllAsync(
Windows.Devices.Enumeration.DeviceClass.VideoCapture);
_deviceList = new List<Windows.Devices.Enumeration.DeviceInformation>();
// Add the devices to deviceList
if (devices.Count > 0)
{
for (var i = 0; i < devices.Count; i++)
{
_deviceList.Add(devices[i]);
}
InitCaptureSettings();
InitMediaCapture();
// rootPage.NotifyUser("Initialization complete.", NotifyType.StatusMessage);
}
else
{
StatusBarObject.ProgressIndicator.Text = NoCameraError;
//rootPage.NotifyUser("No camera device is found ", NotifyType.ErrorMessage);
}
}
private void InitCaptureSettings()
{
_captureInitSettings = null;
_captureInitSettings = new Windows.Media.Capture.MediaCaptureInitializationSettings();
_captureInitSettings.AudioDeviceId = "";
_captureInitSettings.VideoDeviceId = "";
_captureInitSettings.StreamingCaptureMode = Windows.Media.Capture.StreamingCaptureMode.AudioAndVideo;
_captureInitSettings.PhotoCaptureSource = Windows.Media.Capture.PhotoCaptureSource.VideoPreview;
if (_deviceList.Count > 0)
_captureInitSettings.VideoDeviceId = _deviceList[0].Id;
}
// Create a profile.
private void CreateProfile()
{
_profile = Windows.Media.MediaProperties.MediaEncodingProfile.CreateMp4(
Windows.Media.MediaProperties.VideoEncodingQuality.Qvga);
}
// Create and initialze the MediaCapture object.
public async void InitMediaCapture()
{
_mediaCapture = null;
_mediaCapture = new Windows.Media.Capture.MediaCapture();
// Set the MediaCapture to a variable in App.xaml.cs to handle suspension.
(App.Current as App).MediaCapture = _mediaCapture;
await _mediaCapture.InitializeAsync(_captureInitSettings);
CreateProfile();
}
// Start the video capture.
private async void StartMediaCaptureSession()
{
var storageFile = await Windows.Storage.KnownFolders.VideosLibrary.CreateFileAsync(
"cameraCapture.mp4", Windows.Storage.CreationCollisionOption.GenerateUniqueName);
await _mediaCapture.StartRecordToStorageFileAsync(_profile, storageFile);
_recording = true;
}
// Stop the video capture.
private async void StopMediaCaptureSession()
{
await _mediaCapture.StopRecordAsync();
_recording = false;
(App.Current as App).IsRecording = false;
}
private async void ShowFireFlyStatusBar()
{
//this will show the Status Bar
Windows.UI.ViewManagement.ApplicationView.GetForCurrentView().SetDesiredBoundsMode(ApplicationViewBoundsMode.UseVisible);
StatusBarObject.ProgressIndicator.Text = StatusBarHeader;
StatusBarObject.ProgressIndicator.ProgressValue = 0;
StatusBarObject.ForegroundColor = Colors.MintCream;
StatusBarObject.BackgroundColor = Color.FromArgb(255, 166, 62, 59);
StatusBarObject.BackgroundOpacity = .6;
await StatusBarObject.ProgressIndicator.ShowAsync();
}
/// <summary>
/// Invoked when this page is about to be displayed in a Frame.
/// </summary>
/// <param name="e">Event data that describes how this page was reached.
/// This parameter is typically used to configure the page.</param>
protected override void OnNavigatedTo(NavigationEventArgs e)
{
ShowFireFlyStatusBar();
}
private void Camcorder_StartCapture_Click(object sender, RoutedEventArgs e)
{
StartMediaCaptureSession();
}
private void Camcorder_StopCapture_Click(object sender, RoutedEventArgs e)
{
StopMediaCaptureSession();
}
}
Its working fine as I am getting the video file from Photos App .
Now how to enable live preview while recording in app? Which control to use for it?
You're looking for the CaptureElement. Once you add it to your XAML, connect it to your MediaCapture object and start preview like so:
PreviewControl.Source = _mediaCapture;
await _mediaCapture.StartPreviewAsync();
Have a look at the UniversalCameraSample posted on the Microsoft github page for more information. It targets Windows 10, but most of the patterns should work on 8/8.1 as well.