Docking an existing QWidget in QWindow - c#

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);

Related

"Include" instead of ShowDialog()

I'm trying to make a WindowsFormApplication in Visual Studio 2015 and need some help.
I've been trying to search for the answer on internet but can find out how to do the following:
I have two windows (solutions?). I open the second window with a button in the first one with this code:
this.Hide();
intermec prodinter = new intermec();
prodinter.ShowDialog();
My question is:
How can i "include" the second window (like "include" in PHP) instead of close the first window and then open the next one, like it does now?
A Form is just another Control. Think of it as a Container (because it holds other Controls).
A User Control can also hold more than one Control. There are ways you can display a Window inside another Window in a WinForms app, but the desired effect is not always guaranteed. So it would be best to place all of your controls (for "page 1", for example) in a User Control called "Page1", and then, when appropriate, add that User Control to the Form, and set its Dock property to Fill.
And when it's time to show a different "page", Hide(); "Page1", and Show(); "Page2".
I think you are talking about form inheritance:
Just create a form, lets call it as frmBase. And add some controls onto frmBase which you want to have on other forms as well.
Create other form, lets call it as frmDerived.
In the code behind of frmDerived, just do the following:
// derive the frmDerived form from frmBase
public partial class frmDerived : frmBase
{
public frmDerived()
{
InitializeComponent();
}
}
And then just check the frmDerived form design, it should include everything from frmBase.
And you may want to make the access modifier of some controls of frmBase to Public as required to access them on frmDerived.
I hope this will help you. :)

Triggering WinForm_Load() with a User Control nested in a Split Container

I'm currently working on a "Settings" screen for a project and want to implement a view similar to that found in Visual Studio, where there is a TreeView with a list of options and clicking on one of these options will load a UserControl in an adjacent panel in the same form. I am using a SplitContainer to group these two controls.
I thought that the Load event for the User Control would be triggered when it was displayed in the panel, but this is not the case. I also tried to trigger the Enter event but it still did not work so I tried to call a function when the form was initialized using the following method.
ViewSecurity newViewSecurity = new ViewSecurity(Globals._connectionString);
// This creates a new instance of the ViewSecurity form from within the TreeView.
And this is the code in the initializing function for the User Control
public ViewSecurity(string _cString)
{
InitializeComponent();
connectionString = _cString;
MessageBox.Show("Test");
populateData();
}
This method does not work either - the MessageBox does not show up and the function populateData() isn't called either. Any advice on how I could achieve what I am trying to do?
Thanks in advance!

Add and read Multiple textboxes

What is the best way to create and read multiple textboxes in a windows form? In my application, I have a windows form where customer can enter multiple addresses, email addresses
Right now I have a form like this,
TextBoxAddress1 TextBoxEmail1
TextBoxAddress2 TextBoxEmail2
.....
.....
.....
TextBoxAddressN TextBoxEmailN
For this I dragged and dropped multiple controls on a form and named each one of them.
If I use this method I had to write lengthy code to see if first row (TextBoxAddress1 TextBoxEmail1) is filled for validation and even for reading I had to write many lines of code.
Is there a better to way achieve this?
You can use the following code to add a TextBox dynamically to your form:
private int m_CurrTexboxYPos = 10;
private List<TextBox> m_TextBoxList = new List<TextBox>();
private void CreateCheckBox()
{
m_CurrTexboxYPos += 25;
TextBox textbox = new TextBox();
textbox.Location = new System.Drawing.Point(0, m_CurrTexboxYPos);
textbox.Size = new System.Drawing.Size(100,20);
Controls.Add(textbox);
m_TextBoxList.Add(textbox);
}
I would have a listbox/listview with your emails and Add/Edit/Delete buttons which show a popup form - the logic for validating emails, etc. would then be in the one place and your list can grow without you ever needing to add controls to the form.
You could dynamically create textboxes - but you end up writing code to make sure they layout nicely on the form, etc. - having some type of list is easier IMO and also lends itself to binding (e.g. to an email object)
Dynamically adding controls is pretty simple, provided you can use DockStyle and an exclusive container for them (e.g. a Panel). If you can't use DockStyle, then you need to write logic to determine Location and Size (which isn't fun).
On a simple form, I have two buttons and a panel, Button1 adds a new TextBox to Panel1, Button2 iterates through the controls in Panel1 and then checks that they are the correct type or throws an exception. This is where you you would put validation or reading logic. Panel1 needs to have AutoScroll = true; otherwise you will run controls off of the viewable screen.
This concept can be switched for anything that inherits from UserControl (all .Net native controls or your own custom controls).
private void button1_Click(object sender, EventArgs e)
{
TextBox NewEmailBox = new TextBox();
NewEmailBox.Name = "NewEmailBox" + this.panel1.Controls.Count;
NewEmailBox.Dock = DockStyle.Top;
this.panel1.Controls.Add(NewEmailBox);
}
private void button2_Click(object sender, EventArgs e)
{
foreach (Control item in this.panel1.Controls)
{
if (item is TextBox)
{
//Do your reading/validating here.
}
else
{
throw new InvalidCastException(string.Format("{0} was in Panel1 and is of type {1} not TextBox!", item.Name, item.GetType()));
}
}
}
Write a user control for each of the groupings you need. at least one One for address, one for email etc. then all of your validation, calls to your database access is contained in a single location
That is just good design. this way if you have multiple tabs for things like Home Information, Work Information, Emergency Contact Information, you can just place them on the form. This is pretty common for a user profile.
Then a listview for each grouping on a user profile page or whatever, that has edit/delete/add then popup a dialog with the appropriate user control in it.
Most simply, ListBox adove TextBox with Button.
Also you can use DataGridView, BuiltIn functionality for Add\Edit\Delete.
Here using DataGridView (ShowHeader set to false, EditMode to On Enter, with one Column with AutoSizeMode in Fill property)
The less of repeatable code you have, the better programmer you are.
Whenever you see a pattern (something what is repeatable), you could and you should try to optimize it. Unless it's something too small to worry.
In your case, determine first what is the basic of repeatable thing. Do you always have to enter address and email address? Then combine them into a control, which can carry out validation. Do you have to use this control often (or repeat N times)? Then maybe it make sense to switch to a list instead (ListBox, ListView or DataGridView).
Are you too lazy to bother configuring things? Then just optimize something what is obviously going to repeat: put validation into common method and call it from each TextBox event. Or make own TextBox with method build-in. Or do validation at once in the Ok button event by using loop.. or not by using loop.
To find best method you have to first decide best for who. Because customer want something shiny,easy to use, animated, with cats and boobs.. ok, without cats and boobs. The point is: how much work are you willing to put to have it best for the customer.
If I would have to enter table data (or data which form table), I'd go with DataGridView so it would looks like this.. or better:

Create wizard some clarification

I want to create simple wizard with 3 pages
Page 1 have just next button
Page 2 have next and previous
Page 3 have previous and finish
I have created the pages and add to them needed buttons and in the events I have call to the next pages, for instance in page one in the button click I added the following code
private void Button_Click(object sender, RoutedEventArgs e)
{
p2 = new Page2();
NavigationService.Navigate(p2);
}
In the main window cs I have changed the inheritance to NavigationWindow instead of Window and in the xaml also. Currently its working but I have 3 questions.
The pages which displayed is part of the main window, how can i avoid it, since when I run it the buttons place is not like I put in the designer? It was changed.
The button currently in the Grid, should I put them in different control (the button place should be like any wizard in the left buttom of the page) ?
How can I avoid the navigation arrows in the page right upper screen?
Thanks!
To answer your questsions in reverse,
3. How can I avoid the navigation arrows in the page upper right screen?
I have an opensource library http://winchrome.codeplex.com/ that re-styles navigation windows in several ways. For example these are all NavigationWindow s
In short you just style the NavigationWindow to only show the parts you want.
2.The button currently in the Grid, should I put them in different control (the button place should be like any wizard in the left buttom of the page) ?
If you look at the styles from WinChrome then you will see that it is just a case of rebuiliding the UI as you want and providing a ContentPresenter to hold your pages. e.g. the VS2012 style applies lots of styles on the window but avoids adding back and forward buttons., whereas the Win7 style rebuilds the back and forwards in a Win7 Style.
If you do this however you will need a means of passing your enabled or visible states to the buttons outside the pages. Take a look at http://www.codeproject.com/Articles/8197/Designer-centric-Wizard-control for how to do this in Winforms. In WPF you could either derive from your Pages to create WizardPage classes with CanBack, CanNext or IsFinish properties, or alteratively define attached properties to do the same (There are examples of how to do this in VS2012.cs where we define the glow color)
And finally
1. The pages which displayed is part of the main window, how can i avoid it, since when I run it the buttons place is not like I put in the designer? It was changed.
I'd need to see some code to comment on how you've done it, but if you look at any of the demo programs in WinChrome then you can see how I've done it without problems.
Good luck!

c# - create invisible user control

I need to create a user control in C#.Net, which can be added to the application without being visible - just like the FolderBrowserDialog. It's a new window which I'll be using often so I think this is the right way. The window will be opened by envoking the showDialog-Method as known from the other dialog.
Any Idea?
Thanks and regards,
Daniel
Since all these "invisible" controls derive from Component class, you should start by reading the MSDN article on it: http://msdn.microsoft.com/en-us/library/system.componentmodel.component.aspx.
simply set Visible to false or isn't this what you're asking for ?
A UserControl is by definition not a Form; I think what you really want is a Component. That said, couldn't you really just create a new Form class that has the functionality you want? Whenever you want to display it, create a new instance and call ShowDialog. Or, if you want to preserve state, add an instance as a class member to your parent form, call its Show method whenever you want to display it, and add an event handler to its FormClosing event to check:
if (e.CloseReason == CloseReason.UserClosing)
and, if so,
e.Cancel = true;
Hide();
(This last part is to prevent errors if the user closes the form and then tries to display again after it's been disposed.)
I think more information may be needed, but if your crating a custom user control, the control should have a .Visible property. The follow is an example of how a button can be located on the form but hidden from a user.
button.Visible = true; // shows the button
button.Show(); // Shows the button
button.Visible = false; // hides the button
button.Hide(); // Hides the button
While the button may still be on the form/control, it will not be interactible by the user. You can still perform programmatic control on the button, but essentially it is not a user control while it is 'hidden'. If you want there to be a sort of hidden button that the user can click you will need to do other things to obtain this but It doesn't should like that is what you want.
This show/hide thought process sounds a lot like pains and confusion leftover from classic VB. The old form methods of show and hide, etc., were confusing and often left me as a developer in a position to not know whether an object existed or if was merely invisible. And checking was only trivial if you used On Error Goto to prevent a null reference. So right off I would advise not to think in terms of visibility unless you are doing something with a web page and need to maintain space and state.
First, create a Windows form and add it to your project, assuming that is the type of project that you are describing. Decorate the form with the proper controls, and where applicable, create properties to allow public access to the control values. Also set the DialogResult property of the buttons that either "OK" or "Cancel" the form. Give it an appropriate border style of either Fixed3D or FixedDialog. Maybe also set the property for where you want the form to appear on startup -- center parent, center screen, Windows default, etc. The event handlers for both "OK" and "Cancel" should invoke this.Close(); to close the window.
From the calling point in the code, here's some hypothetical code to get you going in the right direction. Write something like this in the place where you want to invoke your Dialog.
int intResult = 0;
string strResult = null;
MyDialogForm frm = new MyDialogForm();
frm.Title = "Select an Item";
frm.SomeProperty = 0;
frm.SomeOtherProperty = true;
if (frm.ShowDialog() == DialogResult.OK)
{
intResult = frm.Result;
strResult = frm.StringResult;
}
else if (frm.ShowDialog() == DialogResult.Cancel)
{
// User clicked the cancel button. Nothing to do except maybe display a message.
MessageBox.Show("Canceled Task");
}
...
// Somewhere further on down, but within scope, simply repeat
// what you just did, but without having to reinstantiate the
// form Window. But if you make it that far within the same
// scope, this method might be too busy and may need to be
// factored down.
So in short:
Scrap show/hide -- its not a good
practice.
Save the form data without
using an invisible form to save it;
that's the class's job.
If the UI requires a lot of flipping back and
forth between windows, check your
design for other alternatives for
solving the original problem. Maybe a design pattern such as MVC is for you, depending upon the size and complexity of your application.
Sound good?
You can put that control in a Panel. Set the panel height = 0 visible = false when you dont want to show the control.
And do the vice versa when you want to show it.
Derive from Control, not UserControl, and in the constructor set Visible = false.
Also create an event handler in the constructor.
VisibleChanged += new EventHandler(SetVisibleFalse);
Create a method named SetVisibleFalse.
private void SetVisibleFalse(object sender, EventArgs e)
{
if (Visible) Visible = false;
}

Categories

Resources