How to go back to main window from page in wpf? - c#

On a C# application that I am working on, I have a frame on my main window.
<Frame x:Name="frame" Grid.Column="1" Grid.Row="0" Grid.RowSpan="2" NavigationUIVisibility="Hidden"/>
I click a button on my main window, and then I use the frame that I created to navigate to a page using the code below.
//Create a new object for the page
CameraPage camera = new CameraPage();
//Navigate to the new page
frame.NavigationService.Navigate(camera);
What I would like to do is if I click a button on the camera page that is contained within the frame, then it only exits the page while still keeping the main window intact.
I have tried something like this.
NavigationService.GoBack();
But then I get an error:
System.InvalidOperationException: 'Cannot navigate because there is no entry in the Back stack of the journal.'
I believe this error is happening because the navigation stack I used to go the page is apart of the main window code and not the page that I navigated to.
So my question really is, how do I close a page contained within a frame with a button contained within the page, without closing the entire application?

If you want to end up with an empty Frame again, all you have to do is clear the Frame's contents. According to this answer, you can do so like this:
frame.Content = null;
That will get you back to how things started, now you just to trigger it. For that, I'll refer you to the question WPF Frame and Page Get event. The answer there shows you how to use DelegateCommand (a.k.a. RelayCommand) to accomplish this. This is the way I would go, because it keeps the Page nice and separate from whatever Window is hosting it.
Technically, you could also pass your Page a reference to frame when you initialize it, then have it set frame.Content = null; that way. But that's a sort of "quick and dirty" approach, not really a best practice.

Related

WInUI 3.0 Desktop - Crash on exception when try to navigate

I want to change frame, but I get this exception:
Navigation:
Frame rootFrame = new Frame();
rootFrame.Navigate(typeof(ScoreWindow), null, new EntranceNavigationTransitionInfo());
Exception appears on constructor:
public ScoreWindow()
{
this.InitializeComponent();
results = new List<Result>();
playerList = new();
LoadData();
var _resultsView = ConvertToView();
sfDataGrid.ItemsSource = _resultsView;
}
Thanks for answers in advance and happy holidays!
P.S. Thanks to Raymond, I detected this message:
WinUI: Error creating second Desktop Window on the current process. No more than one Desktop Window is allowed per process.
There is another question: how to change current frame to other? I mean, I have login view, user logged in successfully and want to see data/other things.
After a little break, I understood what I did wrong. There is a way how to navigate between pages.
Rule #1: In WinUI, you have only one active window, and it's MainWindow. Always. If you want to change layout, you have to use Frames.
In MainWindow.xaml, you write this:
<Grid>
<Frame x:Name="mainFrame"/>
</Grid>
Element "Frame" give ability to navigate between pages.
Then switch to MainWindow.xaml.cs and in constructor have to be something like this:
public MainWindow()
{
InitializeComponent();
mainFrame.Navigate(typeof(NameOfYourPage));
}
It will immediately activate your page.
Then, if you want to navigate from page to page, write in control of page this:
Frame.Navigate(typeof(NameOfYourPage), null, new EntranceNavigationTransitionInfo());
The third parameter is animation, but I didn't notice changes.
More information you can find here:
Tutorial about navigation
Microsoft documentation

What is the best approach to create a Visual Studio-like Start Window using ReactiveUI?

I want to make a Start / Welcome / Solution selection window which has similar functionalities to the one used in VS19 / VS22, but I'm not sure how to do it exactly.
For more context, my current StartWindowView has a "Create New Project..." button, which should replace the entire window content with a 2-page project creation wizard.
Page 1 should be "Basic Options" where on the bottom there are "Cancel" and "Next" buttons ("Cancel" goes back to the original StartView and "Next" goes to Page2View).
Page 2 should be "Extra Options" where on the bottom there are "Back" and "Finish" buttons ("Back" goes back to Page1View and "Finish" goes back to the StartView, returning a ProjectModel)
From my understanding, these are the ViewModels I'd have to deal with:
StartWindowViewModel // Main window host (possible router)
StartViewModel // Main project selection view
ProjectCreationViewModel // Possible router for Page1 & Page2
ProjectCreationPage1ViewModel // Basic options
ProjectCreationPage2ViewModel // Extra options
My idea was to use Routing, but I don't know if that's the right approach, since I'm not sure if the data between Page1 and Page2 will be preserved when going back and forth.
Also, the routing examples I saw for ReactiveUI have fixed "Back" and "Next" buttons on the bottom, therefore it makes me think that this might not be the solution for my problem, since pressing the "Create New Project..." button replaces the StartViewModel with the ProjectCreationViewModel, making manual navigation impossible.
I might be completely wrong though. I'm really unsure on how to approach this problem.
EDIT: This is how the VS22 start window looks like:
and this is what happens when you press the button marked in red:
As you can see, the entire window changes the current View.
It's not terribly difficult, just keep an observable collection of your view model stack, and always display the last one (or the first one, it's easier with WPF bindings). Note that I'll be referring to this as a stack, but there is no ObservableStack<> already built for you. Either use an ObservableCollection<> or build your own observable stack.
Then as you need to navigate "into" your wizard (new project, clone a repository, etc), you simply push the new child view model onto the stack. When you need to navigate "out of" your wizard (ie, on Cancel), pop the last view model from the stack.
The last step is to associate your views to view models (using DataTemplate in a high-level resource block, either a global one or a local one in your start page) and then bind your view model stack's top to your main window (the shell that has a ContentControl bound to the top of your stack).

WPF application in one window

I am new in WPF and want to create WPF application like cookbook. I already done this and app work correctly. But I make it in this way:
First screen show buttons, which open new windows to do something. As a result i have 14 different windows. It is ok, but now i want to make it in other way.
I am trying to make one window, which will be showed at start, and change content. I divided window on two grids. First is static and is placed on bottom. It contains buttons, which represents functionality of the program. Second one will be dynamic. There i want to show content of every window. So i want to change content of this panel instead of creating new windows.
I tried to make *.cs files which will create controls in code-behind, functions and data. But my idea is not succesful and i do not know how to do this.
At all, I want to create app, which will work like this:
- if you click button "Add receip" then app will show controls to add name, ingredients and save it at the end.
- if you clik "Show receip" previous content will be replaced by list of ingredients
and etc.
I hope you will understand me.
You can create a Frame instead of second grid. Frame allows you to show pages, and not in seperate windows, in Frame itself. You can navigate the frame into the page like
mainFrame.Source = new Uri("Page1.xaml",UriKind.Relative);
This changes the frame to your page. You can change the source again, if you wanna change the page again.
Note: You can add tags to your buttons like "showReceip" and you can make just one buttonclick event for your buttons. Code will look like this.
mainFrame.Source = new Uri((sender as Button).Tag.ToString() + ".xaml",UriKind.Relative);
That takes the tag of your clicked button, add the string ".xaml" on it and take it on the source part. So, if your tag is "Page1", Source will look like "Page1.xaml" as my solution.
Appreciate the try, I hope you are looking for WPF user controls instead for separate windows. User controls are similar to windows you can create the UI and functionalities in the user control. I would like to recommend you to design the main window like the following:
<Grid>
<Canvas Name="canFunctionalButtons">
<!--Define the buttons inside this canvas
And allocate proper place for this in the UI
-->
</Canvas>
<Canvas Name="canControlContainer">
<!--This is to display the user control
Which can be changed dynamically according to the Button's click
-->
</Canvas>
</Grid>
Then you have to add click event for those buttons, which will add specific user control to the canControlContainer canvas. An example for adding an user control to this canvas is as follows, Let btnAddSomething be a button and btnAddSomething_Click be its click event then you can do something like:
private void btnAddSomething_Click(object sender, RoutedEventArgs e)
{
canControlContainer.Children.Clear(); // will remove previous contols from this canvas
// UC_AddSomething be the user control that you wanted to add here
canControlContainer.Children.Add(new UC_AddSomething());
}

Want to create a UIContoller in a UITabController but with no tab button

I want to display an intro view (tutorial) on my monotouch app that when the user clicks on a button will take them to the main storyboard, which contains a UITabController.
I'm new to monotouch and can't work out how to do this. I'm adding an extra view that's been created to the tab bar controller in the AppDelegate.FinishedLaunching, but this always adds the button to the tab bar.
When the user has clicked the button once I don't want to show the intro page ever again, it's a one time deal (I'll save some value to disk to work this out), so I don't want to just add it to the tab controller.
Incidentally if anyone can show me where monotouch decides that it's going to start with the storyboard please let me know. The only thing I've found is the little start arrow that you drag around in XCode, but what if I have two storyboards and I want to load one based on the user being logged in or something.
You need to create simple entry ViewController with the button (Controller1). The next controller in storyboard will be the tabcontroller (TabController).
Thus you will always has the first entry screen in your application.
If you don't want to show it later than make transition from Controller1 to TabController before it is loaded. For example, override the ViewWillAppear method.
The second approach. Use this code to launch whatever you want view above all your controllers at any time:
UIViewController root= UIApplication.SharedApplication.KeyWindow.RootViewController
UIView myCustomView=new MyCustomView();
root.Add(myCustomView);
//call myCustomView.RemoveFromSuperview() and it will be dismissed

What determines whether NavigationCommands.BrowseBack calls the page constructor?

I have two pages with similar logic in them. Load the page, click some buttons that will show/hide other buttons, continue to next page. When I hit the next page, if I click the back button I am returned to the previous page.
The difference is that one page (FirstPage) will have the constructor called when I click the back button, which has a call to reset the defaults. The other page (SecondPage) doesn't get the constructor called and I'm not sure why.
public FirstPage()
{
InitializeComponent();
DisplayStuff();
}
FirstPage has KeepAlive set to False.
public SecondPage(object arg1, object arg2)
{
InitializeComponent();
DisplayStuff(arg1, arg2);
}
This page also has KeepAlive set to False. These two pages don't inherit from anything and there is nothing that overrides any of the properties. The only difference I can see is the empty constructor, so I tried giving SecondPage an empty constructor and still no luck.
I'm relatively new to WPF (I work on it for an hour or two every 6 months), so what am I missing?
Here is the back button in case it is relevant.
<Button Command="{x:Static NavigationCommands.BrowseBack}" />
Edit: When I click the back button, SecondPage doesn't keep its state. It just loads an empty page because DisplayStuff hasn't been called yet.
Navigation Code:
NavigateTo(new SecondPage(arg1, arg2));
protected void NavigateTo(Page page)
{
NavigationService.Navigate(page);
}
I created a similar sample application and had similar behaviour. What I figured out that when you go back to a page the constructor is not called unless the page is the first page in the journal
Read this section in Navigation in WPF:
When the page Page is navigated back to, using the journal, the following steps take place:
The Page (the top journal entry on the back stack) is instantiated.
The Page is refreshed with the state that was stored with the journal entry for the Page.
The Page is navigated back to.
Good luck!
After reading Paul Stovell's article on WPF navigation, the way I want to display stuff is not going to work.
When navigating, if you click "Back", WPF can't possibly know what values to pass to the constructor; therefore it must keep the page alive. Here's the trace output:
Since WPF can't call the constructor, it won't. It'll just keep the page alive.
He goes on to mention that KeepAlive doesn't work if you're not navigating via URI, and Loaded and Unloaded are called each time, so I can just move all my logic there and I won't need the constructor to be called on the back navigation.

Categories