I am creating a Quiz program which contains many subjects. But I struck at the following stage. I created many pages and want to load them based on the subject selected. But I don't know how to make it.
Here is what I wanted.
Here Play,Settings and Exit are buttons in main screen. Once you click Play it will show you different subjects available. Once you select the subjects you have different options too.
at first I created Single page and I used grids with hide and show options. But its kind of buggy. So I created pages but I don't know how to navigate between the pages.
How can I achieve it.?
#Ajit,
Yes you can navigate between Pages using :
currentFrame.Navigate(typeof(NextPage));
If currentPage is not this, you can find it the following helper class.
It allows to navigate, even from a ViewModel class :
public class NavigationExtension
{
public static void Navigate(Type typeOfPage)
{
Windows.UI.Xaml.Window window = Windows.UI.Xaml.Window.Current;
if (window != null)
{
Windows.UI.Xaml.Controls.Frame frame = window.Content as Windows.UI.Xaml.Controls.Frame;
if (frame != null)
{
frame.Navigate(typeOfPage);
}
}
}
}
Regards
If you want to go to another page and don't want to come back,
use this code:
Frame rootFrame = Window.Current.Content as Frame;
rootFrame.Navigate(typeof(Your Page Name));
Related
I'd like to pre-load the ShellPage in a WinUI 3 (v1.1.5) Desktop application. That is, during Activation (called by
await App.GetService<IActivationService>().ActivateAsync(args);
in the OnLaunched handler of the App class), I'd like to make sure ShellPage is loaded before any of the navigation pages are displayed. I've changed the service configuration to include
services.AddSingleton<ShellPage>();
services.AddSingleton<ShellViewModel>();
in the constructor for the App class which should mean only one of each of ShellPage and ShellViewModel will be instantiated for the app run but the question is when are they fully provisioned?
The normal progression is that the Activation step first assigns ShellPage to MainWindow.Content, then navigates to MainPage (these are the names for the default project). Because MainPage is actually loaded into a Frame on ShellPage, it seems layout for MainPage happens before ShellPage layout is completed.
Any idea how I do this on initial startup? This is only an issue when the first Page is presented. After that, ShellPage is reused.
By default, TemplateStudio's navigation re-instantiates pages for every navigation. It doesn't use the ServicesProvider, so registering your pages as singleton won't help.
If you want to keep your page instances, you need to set NavigationCacheMode="Required" on your pages. This way, your pages will be cached even after you navigate away.
Still, your pages won't be instantiated until you navigate to them once at least. In order to instantiate all of your pages at the very beginning, you need to navigate through them at least once.
You can get all the NavigationViewItems with a method like this.
private static IEnumerable<NavigationViewItem> GetNavigationViewItems(IEnumerable<object> items)
{
foreach (var item in items.OfType<NavigationViewItem>())
{
yield return item;
foreach (var grandChild in GetNavigationViewItems(item.MenuItems.OfType<NavigationViewItem>()))
{
yield return grandChild;
}
}
}
And use it like this in the NavigationViewService's Initialize method.
[MemberNotNull(nameof(_navigationView))]
public void Initialize(NavigationView navigationView)
{
_navigationView = navigationView;
_navigationView.BackRequested += OnBackRequested;
_navigationView.ItemInvoked += OnItemInvoked;
IEnumerable<NavigationViewItem> menuItems =
GetNavigationViewItems(_navigationView.MenuItems);
foreach (var item in menuItems)
{
if (item.GetValue(NavigationHelper.NavigateToProperty) is string pageKey)
{
_navigationService.NavigateTo(pageKey);
}
}
}
A little clarification first, and then the answer I found to the issue.
Andrew's answer (above) is great for instantiating all of the Pages in the NavigationView at startup but the very first page loaded still would not have access to a fully loaded ShellPage in its constructor (and thus, a fully populated element tree). Andrew is right that the NavigationViewItems (Pages) don't persist by default, but the ShellPage does as it's part of the UI. Specifically, it is the content of the MainWindow and defines a Frame into which NavigationViewItems are loaded. Regardless of which Page is displayed, it's the same instance of the ShellPage people see.
The issue arises because of the order in which Activation (specifically, the DefaultActivationHandler) is done at App startup. When the App starts, it calls
await App.GetService<IActivationService>().ActivateAsync(args);
which does
// Set the MainWindow Content.
if (App.MainWindow.Content == null)
{
_shell = App.GetService<ShellPage>();
App.MainWindow.Content = _shell ?? new Frame();
}
and navigates to the first Page (loads the first Page into the NavigationView.Frame by calling DefaultActivationHandler) before finishing the loading of ShellPage. Thus, ShellPage is not fully loaded (ShellPage.IsLoaded == false) when MainPage is loaded.
To fully instantiate ShellPage before any of the NavigationViewItem Pages are loaded, simply change the loading sequence. First, defer the navigation to the first page (whichever you choose) by editing HandleInternalAsync in DefaultActivationHandler.cs to
protected async override Task HandleInternalAsync(LaunchActivatedEventArgs args)
{
//_navigationService.NavigateTo(typeof(MainViewModel).FullName!, args.Arguments);
await Task.CompletedTask;
}
Move the navigation to the OnLoaded handler in ShellPage.xaml.cs:
private void OnLoaded(object sender, Microsoft.UI.Xaml.RoutedEventArgs e)
{
TitleBarHelper.UpdateTitleBar(RequestedTheme);
KeyboardAccelerators.Add(BuildKeyboardAccelerator(VirtualKey.Left, VirtualKeyModifiers.Menu));
KeyboardAccelerators.Add(BuildKeyboardAccelerator(VirtualKey.GoBack));
App.GetService<INavigationService>().NavigateTo(typeof(MainViewModel).FullName!);
}
All Pages now receive a loaded ShellPage when navigated to, regardless of order.
I have a little problem. I have to make an app in UWP, and I have to send an item to other page while staying on the same page. And I have no idea how to acomplish that. I have tried different things, but I always ended up on transfering my List to the page, and also transfering view to thta page.
I would really apreciate your help.
If you want to pass parameters when navigating to the page, you can use the following method:
If you don't use the Frame control for navigation
var frame = Window.Current.Content as Frame;
frame.Navigate(typeof(MyPage), myItem);
If you use the Frame control for navigation
frame.Navigate(typeof(MyPage), myItem);
If you are not passing parameters through navigation, you are passing data to another page in the current page. Then we can achieve this in another way.
Suppose we want to modify the data of the parentPage in the childPage, we can add a static object when navigating to the parentPage.
public static MainPage Current;
public MainPage()
{
this.InitializeComponent();
Current = this;
}
public void doSomething(string param)
{
// do something...
}
In other pages, we can make some modifications by accessing this object:
MainPage.Current.doSomething("Hello");
C# WindowsFormsApplication
So let's say we have 3 User Controls and 3 buttons . So i programed the Buttons to change the User Controls on Click_event . so what happens is that it changes the first User Control to the Second User Control and from the Second to the third .
But when i go from the first one to the second one and then back from the second to the first User Control then i get an error . and that error is :
System.ArgumentException: 'A circular control reference has been made. A control cannot be owned by or parented to itself.'
and this is my code in the Buttons :
if (!this.Controls.Contains(Example.Instance))
{
this.Controls.Add(Example.Instance);
Example.Instance.Dock = DockStyle.Fill;
Example.Instance.BringToFront();
}
else
{
Example.Instance.BringToFront();
}
and this is my code in all 3 User control :
private static Example _instance;
public static Example Instance
{
get
{
if (_instance == null)
_instance = new Example ();
return _instance;
}
}
And since 3 Days i did nothing but to search for a solution .. so please .. can you help me !?
And sorry for my bad Englisch . . . :-)
What is the goal you are trying to accomplish? You want to dynamically change a user control based on a button that is clicked? You could accomplish this task with loading forms into a panel and having each control on different form to load.
For those who are very familiar with C# or VB.NET using the UserControl component in the .NET Framework (which is the hottest framework in my opinion), you were used to adding several buttons that preview different user controls as follows:
1) First you would prepare an appropriate user interface (contains 3 buttons and a single panel on the right area to view each user control after clicking one of the added buttons).
2) Adding 3 user controls from the solution explorer...
3) Inserting the content on each user control...
4) Implementing code for the 3 buttons on the frmMain.cs as the following (for this implementation we will be implementing the "Welcome" button carrying the object name as welcomeBtn, and the rest will have identical code but different user control names instead):
private void welcomeBtn_Click(object sender, EventArgs e)
{
//Clear up everything from the panel if any item exist(s)...
mainPanel.Controls.Clear();
//Create a new instance of a user control for the button...
UserControl1_Welcome welcome = new UserControl1_Welcome();
//Show up the created instance of the user control
mainPanel.Controls.Add(welcome);
}
5) Finally, the program will end up initially like this when running:
http://i.stack.imgur.com/OENwG.png
** Usage of the program **
When you click on the "Welcome" button for example, the result should be expected to be like this:
http://i.stack.imgur.com/iCyo3.png
... and when you click on a different button, lets say "License Agreement" button, you would expect to see something other than your current selection.
MAIN QUESTION
How can we bring the simplicity of Windows Forms in QT CREATOR by applying the "QDockWidget"?
I have tried inserting the QDockWidget component with no problems, but when I try to do the equivalent .NET code for adding the QWidget inside the QDockWidget:
ui->dockWidget->setWidget(myWidget);
which I think is equivalent to this line of code in C#.NET (correct me if I'm wrong here):
ui.Controls.Add(myWidget);
After using this code, my program won't crash nor shows anything running...
P.S. I'm sorry for linking the images, I don't have 10 reputation for making them show up...
What I want is to have a program that does the same thing with the C# example (showing a user control based on the click of a button).
If you want to show a particular widget based on a button click, I suggest to use a QStackedWidget
A simple example would be like this:
// In the constructor of your CustomWidget
// Create your buttons
QPushButton* firstButton = new QPushButton("First Button", this);
QPushButton* secondButton = new QPushButton("Second Button", this);
QPushButton* thirdButton = new QPushButton("Third Button", this);
// Create your (custom) widgets
QLabel* firstPageWidget = new QLabel("First Label", this);
QLabel* secondPageWidget = new QLabel("Second Label", this);
QLabel* thirdPageWidget = new QLabel("Third Label", this);
// Add them to the stackWidget
/*QStackedWidget* */ m_stackedWidget = new QStackedWidget(this);
m_stackedWidget->addWidget(firstPageWidget);
m_stackedWidget->addWidget(secondPageWidget);
m_stackedWidget->addWidget(thirdPageWidget);
// Insert buttons and stackWidget to CustomWidget
QVBoxLayout* layoutStack = new QVBoxLayout();
layoutStack->addWidget(m_stackedWidget);
QVBoxLayout* layoutButtons = new QVBoxLayout();
layoutButtons->addWidget(firstButton);
layoutButtons->addWidget(secondButton);
layoutButtons->addWidget(thirdButton);
QHBoxLayout* layout = new QHBoxLayout();
layout->addLayout(layoutButtons);
layout->addLayout(layoutStack);
setLayout(layout);
// Connect button clicks to slots
connect(firstButton, SIGNAL(clicked()), this, SLOT(onFirstButtonClicked()));
connect(secondButton, SIGNAL(clicked()), this, SLOT(onSecondButtonClicked()));
connect(thirdButton, SIGNAL(clicked()), this, SLOT(onThirdButtonClicked()));
Then you change the currently visible widget in the slots:
void CustomWidget::onFirstButtonClicked() {
m_stackedWidget->setCurrentIndex(0);
}
void CustomWidget::onSecondButtonClicked() {
m_stackedWidget->setCurrentIndex(1);
}
void CustomWidget::onThirdButtonClicked() {
m_stackedWidget->setCurrentIndex(2);
}
Note that if you want the button clicks just to simply change some text (as opposed to change the visible widget), you probably better use a QTextEdit instead of a QStackedWidget, and in the slots call setText("....");
If you have a lot of buttons, you'd better use QSignalMapper to limit the number of slots.
Also, I didn't get why you mentioned QDockWidget since they have a quite specific usage:
The QDockWidget class provides a widget that can be docked inside a QMainWindow or floated as a top-level window on the desktop.
QDockWidget provides the concept of dock widgets, also know as tool palettes or utility windows. Dock windows are secondary windows placed in the dock widget area around the central widget in a QMainWindow.
If you simply want a separate window, you're probably looking for a QDialog
How to do this with QtDesigner:
First you would prepare an appropriate user interface (contains 3 buttons and a single QStackedWidget on the right area to view each user control after clicking one of the added buttons).
Adding 3 pages for the user controls in the stack (+ one for the "empty" page if you really need that). If you want to design the Controls in separate UI Files / Only in Code (instead of all controls in your MainFrame), you would add plain QWidgets and promote them to the appropriate specific widget type
Inserting the content on each user control...
Implementing code for the 3 buttons on the frmMain.cpp/.h as the following (for this implementation we will be implementing the "Welcome" button carrying the object name as welcomeBtn, and the rest will have identical code but different user control names instead):
void FrmMain::on_welcomeBtn_clicked() {
ui->stack->setCurrentWidget(ui->welcomeWidget);
}
Select the "empty" page at as the current page in the designer, so the program will end up initially like this when running: (your screenshot)
When you click on the "Welcome" button for example, the result should be expected to be like this: (your second screenshot)
In my opinion, Miki's answer is the only correct approach to this use case (using a QStackedWidget).
For sake of completeness, I'll demonstrate how the same Clear and Add method as used in .NET is done in Qt:
// Assume controlPanel is a QWidget where you want to place the items
// Assume that controlPanel has set a layout (e.g. QHBoxLayout)
// Clear: Remove all Items from layout
QLayoutItem *child;
while ((child = controlPanel->layout()->takeAt(0)) != NULL) {
delete child;
}
// Now widgets are still there, but not layouted. Delete them explicitly
foreach (QWidget * w, controlPanel->findChildren<QWidget*>()) {
w->deleteLater();
}
// Now controlPanel is cleared
// Add new control
controlPanel->layout()->addWidget(new MyNewControlWidget);
First is, we can not force how other framework works to another one. Each framework has its flow and design.
What I am understand is you want to show another widget to the main window.
If you want to use the QDockWidget, its says on the documentation like this :
void QDockWidget::setWidget(QWidget * widget)
Sets the widget for the dock widget to widget.
If the dock widget is visible when widget is added, you must show() it explicitly.
Note that you must add the layout of the widget before you call this function; if not, the widget will not be visible.
Please share here you code of myWidget, so we can try to help you to figure out what is wrong.
On my side, I can achieve it by add the QVboxLayout on your ui->dockwidget and add QLabel with emtpy string and when you want to show myWidget just call ui->dockwidget->vboxlayout->replaceWidget(label, myWidget);
I have an application for Windows 8 with a page (Frame) for displaying a list of items and a page for downloading & displaying the items details. I am also using MVVM Light for sending notifications.
Application use goes something like this:
Open Main Page
Navigate to List Page
Frame.Navigate(typeof(MyPage));
Choose Item
//Complete logic
Frame.GoBack();
Back on Main Page, I start downloading the file in the view model, I send ONE NotificationMessage saying BeginDownloadFile and after it is downloaded ONE NotificationMessage saying EndDownloadFile.
The first time I do steps 2,3, & 4 my NotificationReceived method is hit once, the second twice and so forth.
private async void NotificationMessageReceived(NotificationMessage msg)
{
if (msg.Notification == Notifications.BeginDownloadFile)
{
FileDownloadPopup.IsOpen = true;
}
else if (msg.Notification == Notifications.EndDownloadFile)
{
FileDownloadPopup.IsOpen = false;
}
}
Additional information: I only have one FileDownloadPopup, yet each time, an additional popup is shown each time the NotificationMessageReceived method is called.
My only conclusion is that between navigating forwards and backwards in my app, there are multiple MainPages being created and never closed. This results in many NotificationsMessageReceived methods just waiting for a notification to come their way so they can show their popup.
I have two questions:
1. Does this sound like normal behaviour for a Windows 8 app?
2. How can I close all instances of the MainPage or return to the previous instance without creating a new instance?
Please let me know if I have missed something important out before marking my question down.
This sounds normal to me. The default navigation behaviour in Windows 8 is to create a new page instance each time you navigate to a new page, regardless of whether this is forward or back navigation.
Try setting the NavigatinCacheMode on MainPage to Required. See the MSDN documentation for details of how page caching works.
It sounds like you are registering eventhandlers in the page and then not removing them. Each time you navigate to the page again the handler is being added again in addition to the one you previously added. Try to add your event handler in OnNavigatedTo, and make sure you unregister it in OnNavigatedFrom.
protected override void OnNavigatedTo(Windows.UI.Xaml.Navigation.NavigationEventArgs e)
{
MyEvent.OnDownloadRequest += MyLocalDOwnloadHandler; // add the handler
}
protected override void OnNavigatedFrom(Windows.UI.Xaml.Navigation.NavigationEventArgs e)
{
MyEvent.OnDownloadRequest -= MyLocalDOwnloadHandler; // remove the handler
}