I'm building an UWP app that spawn second view besides the main view.
I want to close the second view from the main view.
I already try to google it, but most of it only show me how to spawn but not to close.
Below is the code block I use to open a new view, and at its bottom is how I try to close the newly created view that always ends up fail.
public async void OpenNew(Type type, string title, string uniqueId)
{
CoreApplicationView newView = null;
if (!IsExists(uniqueId))
{
newView = CoreApplication.CreateNewView();
int newViewId = 0;
await newView.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
Frame frame = new Frame();
Window.Current.Content = frame;
Window.Current.Activate();
frame.Navigate(type);
newViewId = ApplicationView.GetForCurrentView().Id;
ApplicationView.GetForCurrentView().Title = title;
ApplicationView.GetForCurrentView().Consolidated += OnConsolidated;
});
await ApplicationViewSwitcher.TryShowAsStandaloneAsync(newViewId);
_uniqueIds.Add(uniqueId, newViewId);
}
// Below code testing on how I try to close newly spawned view but always throw error
await Task.Delay(5000).ContinueWith(async _ =>
{
await Window.Current.CoreWindow.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
newView.CoreWindow.Close();
});
});
}
This code always throws
Exception thrown: 'System.NullReferenceException'
Any link or tutorial on how to do it?
Nvm, guys I got the answer. Below is my changes on the part where I try to close the 2nd view from the above code:
await Task.Delay(5000).ContinueWith(async _ =>
{
await newView.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
CoreWindow.GetForCurrentThread().Close();
});
});
Related
When I open the application it works perfect. But when I use my scan button again it's stuck on a white screen and not passing until I press the "Back" Button on my phone.
How can I remove that white screen bug?
Video about my bug: https://www.youtube.com/watch?v=k8-haOmCzm0
QR Code Scanner Code:
public async void Scan()
{
var scan = new ZXingScannerPage();
await Navigation.PushAsync(scan);
scan.OnScanResult += (result) =>
{
Device.BeginInvokeOnMainThread( () =>
{
Navigation.PopAsync();
codes.Text = result.Text;
Application.Current.Properties["codes"] = codes.Text;
Navigation.PushAsync(new MainPage());
});
};
}
My Scan again button Code:
private void Button_Clicked_1(object sender, EventArgs e)
{
Navigation.PushAsync(new ScanPage());
}
From shared code, you have pushed twice to the scan page, however you only use one time PopAsync after get result.
But also used the third push in Device.BeginInvokeOnMainThread:
Navigation.PushAsync(new MainPage());
You could modiy this line with following code to check whether it works.
Device.BeginInvokeOnMainThread( () =>
{
Navigation.PopAsync();
codes.Text = result.Text;
Application.Current.Properties["codes"] = codes.Text;
Navigation.PopAsync();
});
============================Update================================
If you scan code when lauching application, when receiving results, you could use Application.Current.MainPage = new MainPage(); to back to Root Page of Applicatuon.
Code as follows:
Device.BeginInvokeOnMainThread( () =>
{
Navigation.PopAsync();
codes.Text = result.Text;
Application.Current.Properties["codes"] = codes.Text;
Application.Current.MainPage = new MainPage();
});
I trying to understand an example app by Microsoft. Now I can't repeat the next part. The app have classes App and ExtendedSplach. I want to repeat loading by ExtendedSplash. In my case, it's simple switch from splash to main page after some delay.
Introduction
The example to do like this.
If app runs with breakpoints on line .Content = extendedSplash and .Content = rootFrame, then the first will be extendedSplash. But line .Content = extendedSplash the follow after .Content = rootFrame. The constructor ExtendedSplash calls LoadDataAsync that set .Content = rootFram by first.
However, method LoadDataAsync contains await call
await Startup.ConfigureAsync();
I think that thus the first will extendedSplash. And we will see loading page.
class App
...
bool loadState = (e.PreviousExecutionState == ApplicationExecutionState.Terminated);
ExtendedSplash extendedSplash = new ExtendedSplash(e, loadState);
Window.Current.Content = extendedSplash;
Window.Current.Activate();
class ExtendedSplash
public ExtendedSplash(IActivatedEventArgs e, bool loadState)
{
...
LoadDataAsync(this.activatedEventArgs);
}
private async void LoadDataAsync(IActivatedEventArgs e)
{
...
rootFrame.Navigate(typeof(LoginView), shellArgs);
Window.Current.Content = rootFrame;
Window.Current.Activate();
}
Problem
I tried to repeat the same. I want see loading and then swith to other page. But my case with breakpoints looks like the first .Content = rootFrame and the second .Content = extendedSplash. Thus my queue are logo app with delay 5 seconds and then page with extendedSplash. The page rootFrame losing.
I will grateful for any help.
My code
I did the same by App class
bool loadState = (e.PreviousExecutionState == ApplicationExecutionState.Terminated);
ExtendedSplash extendedSplash = new ExtendedSplash(e, loadState);
Window.Current.Content = extendedSplash;
Window.Current.Activate();
And the next by ExtendedSplash
public ExtendedSplash(IActivatedEventArgs e, bool loadState)
{
this.InitializeComponent();
Window.Current.SizeChanged += new WindowSizeChangedEventHandler(ExtendedSplash_OnResize);
this.splashScreen = e.SplashScreen;
this.activatedEventArgs = e;
OnResize();
rootFrame = new Frame();
LoadDataAsync(activatedEventArgs);
}
private async void LoadDataAsync(IActivatedEventArgs e)
{
await Test();
rootFrame.Navigate(typeof(MainPage));
Window.Current.Content = rootFrame;
Window.Current.Activate();
}
private async Task Test()
{
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
while (stopwatch.ElapsedMilliseconds < 5000) ;
}
The problem with your code is in fact in the Test() method. You have marked is async, but that doesn't make the method asynchronous. Instead, your code will actually be stuck for five seconds in blocking manner in the while loop.
Try the following version:
private async Task Test()
{
await Task.Delay(5000);
}
This form of code is in fact asynchronous, so as a result the UI thread will be free to display the splash screen in the meantime.
In general - async methods run on the thread that calls them until they hit an "actual" asynchronous code - for example I/O bound async method or when you run your code with await Task.Run(()=>{...}.
Is there a way to wrap the UWP async function for creating dialog boxes in such a way that they can be called from a normal method without the async keyword? Example:
var msgbox = new ContentDialog
{
Title = "Error",
Content = "Already at the top of the stack",
CloseButtonText = "OK"
};
await msgbox.ShowAsync();
No, there is no way to do this at the moment. If you try to block waiting for the dialog to close, your app will deadlock.
Here is how I do it (I saw this in some live demo back when UWP was just introduced):
var msgbox = new ContentDialog
{
Title = "Error",
Content = "Already at the top of the stack",
CloseButtonText = "OK"
};
var ignored = msgbox.ShowAsync();
This works as expected in a non-async void method.
public Task<ContentDialogResult> MsgBox(string title, string content)
{
Task<ContentDialogResult> X = null;
var msgbox = new ContentDialog
{
Title = title,
Content = content,
CloseButtonText = "OK"
};
try
{
X = msgbox.ShowAsync().AsTask<ContentDialogResult>();
return X;
}
catch {
return null;
}
}
private void B1BtnBack_Click(object sender, RoutedEventArgs e)
{
MsgBox("Beep", "Already at the top of stack");
return;
// ^^^ Careful here. MsgBox returns with an active task
// running to display dialog box. This works because
// the next statement is a return that directly
// returns to the UI message loop. And the
// ContentDialog is modal meaning it disables
// the page until ok is clicked in the dialog box.
}
Im new to programming, and after 7 days of searching I couldn't find a solution.
My "MainPage" opens, and I click to Open a "SecondaryPage" which opens on a new thread window with core Dispatcher. My "MainPage" has a button Click event which updates "TextBlock1". What I cant achieve is to pass this value to my "SecondPage" "TextBlock2".
MainPage.xaml below.
<StackPanel>
<Button Click="Button_Click_NewWindow" Content="Start a new window" FontSize="30"/>
<TextBlock Name="Textblock1" Text="Empty" FontSize="30"/>
<Button Click="Button_Click_Add" Content="+1" FontSize="30"/>
</StackPanel>
Code Behind SecondPage.xaml
<StackPanel>
<TextBlock Name="Textblock2" Text="Empty" FontSize="30"/>
</StackPanel>
MainPage.xaml.cs
namespace MulitViewV1
{
public sealed partial class MainPage : Page
{
public int T1G = 0;
public MainPage()
{
this.InitializeComponent();
}
private async void Button_Click_NewWindow(object sender, RoutedEventArgs e)
{
CoreApplicationView newView = CoreApplication.CreateNewView();
int newViewId = 0;
await newView.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
Frame frame = new Frame();
frame.Navigate(typeof(SecondPage), null);
Window.Current.Content = frame;
// You have to activate the window in order to show it later.
Window.Current.Activate();
newViewId = ApplicationView.GetForCurrentView().Id;
});
bool viewShown = await ApplicationViewSwitcher.TryShowAsStandaloneAsync(newViewId);
}
private async void Button_Click_Add(object sender, RoutedEventArgs e)
{
T1G = T1G + 1;
Textblock1.Text = T1G.ToString();
await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
{
//UI code here
// NOT WORKING error "TextBlock2" does not exist in the current context
Textblock2.Text = T1G.ToString();
});
}
}
}
I see the Dispatcher is working to my MainPage View, but I cant figure out code to direct it to my SecondPage "newViewID".
There's no need to call Dispatcher all the time. You button clicks are coming to you on the UI thread so the handlers are also going to be on the UI thread.
A way to think about it is that you're always on the UI thread unless you're doing something with *Async. If you await async stuff, you're good. This is not true all of the time, but it's a place to start and handle the problems you might encounter as the come.
Generally you only want a single Frame in your application. Multiple Frames (and multiple windows) is an advanced topic; save it for later.
By default, you UWP app will contain a Frame. Pages are hosted in this frame. The frame can navigate between the pages it hosts. Think of the frame as you browser window, and the pages are like web pages.
You can get the Frame by getting the Frame property on the page. To navigate to a new page you do this:
private async void Button_Click_NewWindow(object sender, RoutedEventArgs e)
{
Frame.Navigate(typeof(SecondPage), null);
}
Next you want to change the text of TextBlock2. TextBlock2 is located on SecondPage, but your code is in MainPage, which has no TextBlock2, and the compiler is telling you that TextBlock2 isn't a thing in MainPage.
Inside your SecondPage.xaml.cs you are however able to get to TextBlock2 as it is present there. Remember though, MainPage will not be visible after navigation to SecondPage (just as you can't see two web pages at the same time when navigating from one to another).
In your Button_Click_NewWindow where you are calling
frame.Navigate(typeof(SecondPage), null);
instead of null, pass the text from TextBlock like below
string TextBoxText = Textblock1.Text;
frame.Navigate(typeof(SecondPage), TextBoxText);
And in your SecondPage, Override OnNavigated to method and you can get the Text like below.
string PassedText=e.parameter.ToString();
and you can assign this to your TextBlock as below
Textblock2.Text=PassedText;
Update
Change your Button_Click_NewWindow to below and add the two variables on top.
int newViewId = 0;
Window SecondWindow;
private async void Button_Click_NewWindow(object sender, TappedRoutedEventArgs e)
{
string TextBoxText = Textblock1.Text;
int mainViewId = ApplicationView.GetApplicationViewIdForWindow(CoreApplication.MainView.CoreWindow);
if (newViewId == 0)
{
CoreApplicationView newCoreView = CoreApplication.CreateNewView();
ApplicationView newAppView = null;
await newCoreView.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
newAppView = ApplicationView.GetForCurrentView();
Window.Current.Content = new Frame();
(Window.Current.Content as Frame).Navigate(typeof(SecondPage), TextBoxText);
Window.Current.Activate();
SecondWindow = Window.Current;
});
newViewId = newAppView.Id;
}
else
{
await SecondWindow.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
SecondWindow.Content = new Frame();
(SecondWindow.Content as Frame).Navigate(typeof(SecondPage), TextBoxText);
SecondWindow.Activate();
});
}
await ApplicationViewSwitcher.TryShowAsStandaloneAsync(newViewId, ViewSizePreference.UseHalf, mainViewId, ViewSizePreference.UseHalf);
}
And your output
I have a UWP app that dynamically creates another view. But, the problem is when I close the first window, the second still stays on.
With this code I am creating new view:
CoreApplicationView newView = CoreApplication.CreateNewView();
int newViewId = 0;
await newView.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
Frame frame = new Frame();
frame.Navigate(typeof(SecondPage), null);
Window.Current.Content = frame;
// You have to activate the window in order to show it later.
Window.Current.Activate();
newViewId = ApplicationView.GetForCurrentView().Id;
});
bool viewShown = await ApplicationViewSwitcher.TryShowAsStandaloneAsync(newViewId);
How to close the application when user close the first window?
Please refer to Show multiple views for an app.
If secondary views are open, the main view’s window can be hidden – for example, by clicking the close (x) button in the window title bar - but its thread remains active. Calling Close on the main view’s Window causes an InvalidOperationException to occur. (Use Application.Exit to close your app.) If the main view’s thread is terminated, the app closes.
private void Btn_Click(object sender, RoutedEventArgs e)
{
Application.Current.Exit();
}
You could also choose if you want to close the initial window and remove it from the taskbar by specifying the value of ApplicationViewSwitchingOptions.So that there is a single view on your desktop. You just need to close it for closing the application.
private int currentViewId = ApplicationView.GetForCurrentView().Id;
private async void ShowNewView()
{
CoreApplicationView newView = CoreApplication.CreateNewView();
int newViewId = 0;
await newView.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
{
Frame frame = new Frame();
frame.Navigate(typeof(SecondPage), null);
Window.Current.Content = frame;
Window.Current.Activate();
newViewId = ApplicationView.GetForCurrentView().Id;
});
await ApplicationViewSwitcher.SwitchAsync(newViewId, currentViewId, ApplicationViewSwitchingOptions.ConsolidateViews);
}