Making multiple forms appear as one in VB.NET - vb.net

I am writing a Windows Forms application in VB.NET. I have three forms: the main form, which shows a list of accounts, the account form which allows the user to view/edit the information for a specific account, and the policy form which allows the user to view/edit the information on a specific policy for that account. I want the forms to appear as if they are all the same window. Example: when the application starts, the user clicks an account name in the list box on the main form and clicks "edit". What I want to happen is that the window stays in the exact same place and stays the same exact size, only the content of the main form appears to be replaced with the content of the account form. Same thing if the user then chooses to edit a policy from the account form. When the user finishes and clicks "save", the main form comes back up. Through this entire use case, it would appear to the user as if they were viewing the same window the entire time, with the content of that window changing.
How can I do this? I have tried something like:
Dim newForm as New AcctForm
newForm.Location = Me.Location
newForm.Show()
Me.Close()
The problem is that if the user moves the original window, the new window appears where the parent form originally appeared, not where it ended up.

I see this is already in the comments, but what I have done in this case in the past is build each "form" in the application as a custom control. Then I have one actual form, and navigation works by changing which custom control is currently loaded on the parent form. To move from one screen/view to another, you remove the current custom control from the form's controls collection and add the new custom control.
I believe this is superior to manually setting the startup position and size, because you can use the form's .SuspendLayout()/.ResumeLayout() methods to hide the interim state, where there is no control loaded, from the user. This is harder to do when you want one form to be completely replaced by another.
This also makes it easy to set certain form properties in one place and have them be consistent for the application. You can even have an area on the form with controls that will now show in every view.
When using this pattern, I typically have each of my custom controls inherit from a common base. You may not have anything specific you will do with that base at the outset, but it almost always comes in handy later.
Finally, switching to use this scheme is easier than you think. Just go to the code for the each of your current forms, and you will find that each class currently inherits from System.Windows.Forms.Form. Most of the time, all you really need to do is change them to inherit from System.Windows.Forms.Panel and you're most of the way there.

As others have said, it may be better to redesign your application using custom controls or panels etc.
However, to answer your question regarding the seemingly random location of your forms, the first thing to check is that each form has it's StartPosition property set to Manual.
If your main form is resizable, then I would also add code to adjust newForm to the same size too.
I hope that helps with your immediate issues; so that you can move on to redesigning the application!

good morning there is another way . set property for second form to (top most) and use also
from2.show();
that make you switch between forms and keep form2 top other
Thanks

try using ShowDialog()
Dim newForm as New AcctForm
newForm.Location = Me.Location
newForm.ShowDialog()
Me.Close() <-- removed this

Related

Parent form and child User Control communication in WinForms

I have my form with a menu bar and space underneath to display my controls. One of the buttons in my menu bar is suppose to be a print button that prints a graph that's currently in a User Control I display in the form. If the graph was on the form in the print button's eventhandler I could just simply call
graph.printing.print(true)
which isn't going to work in my case since the graph is in the control and not the form.
How do I communicate with a User Control from the containing form and access or pass its variables when needed? I also have a status bar on the bottom of the form which would also need to get updated from the User Control, but I'll be able to deal with that if I got help with just this one part. Please bear in mind, I also have another User Control I'm going to add to the form which will also contain a graph which will need the same treatment as the other graph on the first control when the print button is pressed. I plan on swapping these two out so I have one form displaying one control at a time.
I got this idea from this answer: https://stackoverflow.com/a/18191630/2567273 but after further research I can't find anyone asking about the actual communication process between a form and the control it contains.
I think this answer is close to what I'm looking for, but I think it's leading me down the path to using panels instead of User Controls.
After typing this I noticed the closest answer to my question may be this, but that question has the child raising events and the parent responding while I have the parent raising the event and the parent has to get information from the child.
One way to think about this is Roles. Presumably you built this UserControl to handle the management of the data related to the graphs. As such you can think of them in the Role of a Graphs Specialist . Once you do that, printing them is actually just one more thing it should perhaps do.
The form on the other hand, is not special just because it happens to get receive the command from the user to print. Its role in this might simply to be to know which usercontrol to contact and which method to invoke:
Sub PrintGraphMenuClick....
Select Case something ' determinant as to which UC to contact
Case operation.Foo
ucFoo.PrintGraph
Case operation.Bar
ucBar.PrintGraph
Other menu options like Clear, NewGraph, Save and whatever else there is somewhat the same way. The Form's Role here may be to receive the command from the user and pass it along to the right control, invoking the correct right method and passing the correct parameters - that is not a trivial task.
Of course, rather than a MainMenu, the usercontols could alternatively implement a ContextMenu and even receive those commands directly.
Very often offloading an operation to something else results in so many properties, filenames, streams etc having to be moved from here to there that it becomes burdensome. In this case it is not like the MainForm has some special ability regarding printers that the UserControl cannot handle.
There is only one right solution:
1) Add an event to your user control.
2) Raise the event when the particular "thing" happens in the user control.
3) Attach a handler to the event in Form code.
4) Add code to update the bottom bar in the event handler.

Do not require attention for ShowDialog

I had a question about Dialogs in VB.NET. I am working on a point of sale program, and at one point during a sale, I have a few windows that pop up. For example, a user will go into a sale that is window A. In window A, they have the option of entering products, etc., and if they choose a 'repair' product, it opens window B, allowing them to choose options. In window B, there is a button that pops up window C that allows them to attach products TO the repair. My issue is with window B opening window C.
Because I open window B as a Dialog (in order to check if DialogResult.OK is true), any window I open with B is non-touchable, as B is a Dialog and requires attention before going to any other windows/forms.
My question is - is there any way to still use a dialog, but allow for manipulating other open forms while the dialog is up, and if not, what would be the best way to check if the user selected OK, or cancelled out of the window?
The only solution I can think of right now would be to open window C as a dialogue as well (it's actually a UserControl, and I'm still trying to find where in the code it's actually getting openned/called), or to create a variable that is passed in to the form, and then passed back out when it's closed, that basically sets a flag to either continue or cancel...
Any advice/ideas??
If I were to explain this using code, this answer would be very long, so instead I'm going to give you a high level overview.
.Show() vs .ShowDialog()
The link below will take you off to Microsofts website to explain the technical differences between these two. However in laymans terms, .ShowDialog() will create the form where it is the only window allowed to have focus in the application. Forms that are called in this instance are hierarchical, in that if you open them in order of 1,2,4,3 then they must be closed in the 3,4,2,1 order. Forms that are opened with just .Show() can be focused at any time.
How to: Display Modal and Modeless Windows Forms
Form.FormBorderStyle property
This property controls how the OS will display the window. The different options under this selection changes the way the window behaves. Depending on the options that are chosen you can make a window that only has a close button on it, or it may not even have a title bar at all. Setting this option to None will take away all controls of the form and only leave you with the Me.ClientArea to work with. When you want a completely custom GUI, this is how you do it but you have to implement your own controls for everything, closing the form, size handles, the ability to move the form on the screen, etc...
Form.FormBorderStyle Property
Passing data between forms
When someone asks how to pass data back and forth between forms, they are usually talking about modeless forms that were created using .Show(). The most common thing I see on SO is to use the tag property of an object (a form is an object that has this property too) to pass data back and forth. While I won't say this is a bad practice, I will recommend creating public properties on your forms. These can be set from a separate form and you can perform additional actions when setting the values (be careful though, this way of doing things isn't thread safe). If you are using a Modeless form as though it were a Modal form, then you can simply override the .Dispose property to return a value or you can create a method named DialogResult that will return the value you need. The caveat to using a DialogResult or similar method is that if the form has been disposed then you can't access the value you wanted to return.
You can use myNewForm.Show(Me) for the Window you want to be shown as a dialog. This will show myNewForm as a child of the current form, but lets you interact with the current form.

Reset focus to control with lowest tab index

I have a form that is re-used. That is, instead of creating a new instance of the form each time, the form is kept hidden, and is made visible when needed. (A design I inherited; I presume this was a performance optimization.)
The problem: The second time that the form is used, the focus is on the OK or Cancel button, from the first use of the form.
The user wants the focus to start the way it did the first time the form appears - on the control with lowest tab index.
If there were just one such form, I would hack it: add a line of code hardwired to the desired control.
But there are many such forms, and the visibility logic is in a common base class.
So it would make more sense to do this right, and tell the form to focus on its first (lowest tabindex) control.
Is there an easy way to do so?
(I could iterate through all the controls, but then I have to correctly handle nested controls. Since the GUI has to do this the first time it shows a form, I am hoping there is some method I can call that does it for me.)
(Coded in VB.net, but a C# answer would be fine.)
It is a one-liner, the logic to find the next control is exposed as a method, SelectNextControl(). You should start at the Form object, the one that can never get the focus, and ask it to find the next one in the tabbing order. Which is the child with the lowest TabIndex, whatever value it might have.
So something like this:
public void ShowAgain() {
this.Show();
this.SelectNextControl(this, true, true, true, true);
}
And do consider that a Form object that isn't visible is a rather major resource hog, using up lots of operating system resources for a small convenience. Surely you can also Close/Dispose it and recreate it when needed. YMMV.
You can try to set ActiveControl property before making form visible:
_frm.ActiveControl = null;
This should clear the active control for the form and remove focus from its controls.

VB.NET forms keep disappearing while system is in use

We have a "Core" system that we use to run the business and there are about 15-18 people using it at any one time. The program is written in VB.NET and has about 165 forms.
The way it works is when the user runs the program he/she is prompted to log in and if the login is successful a "Main" form is displayed with a number of menus (Customers, Suppliers...). From there they can click on the menus which open another form on top of the "Main" (the "Main" form needs to be visible in the background because it displays information that is relevant to the users while they are in other screens)
The issue we are having is that if the users have other programs open while using the "Core" system (Outlook, Word, Chrome, anything really) and switch to another program and then back to the system, it only displays the "Main" form and any other forms open on top disappear. The way we get around this is by switching back to the other programs they have open and clicking on the minimise button in the top right corner of the window until all the other programs are minimised, which only leaves the "Core" system visible. However this is becoming a nuisance to all the users (including myself and the other developer) and we really need to sort this issue out in order to keep out staff happy :)
I would appreciate any advice or pointers in the right direction which will help us solve this issue and please feel free to ask if you need any more information.
It seems you are creating the ChildForm from the MainForm but the ChildForm itself is showing itself with Me.ShowDialog(). What you should probably try is showing the form from the MainForm and passing the MainForm in as the parent. This should keep the form tied to it's parent and on top. For example:
childForm.showDialog(Me)
Where Me is the MainForm. This is the documentation for that method.
This is the important part:
Owner Type: System.Windows.Forms.IWin32Window
Any object that implements IWin32Window that represents the top-level window that will own the modal dialog box.
Does that make sense?
Changing code to show dialog will change behavior of your code little bit, like your main from execution will hold till you close child form
But you can you use only show as child (not dialog)
childForm.show (Me)
This will not change anything except whenever you click on main form it will display its entire children on it.

How do I create a "wizard" style ui using vb.net?

The idea is that I would have a set of forms, users would click through a "forward" and "back" button, and the current form would change to a different one. My issue is that I can write code that just pops up a new form, but im not sure how to do a "replacement" of my current form. How is this usually done?
What I did recently was to create a form with buttons already in place and a large panel to contain each step. The dialog would accept an initial step in the form of a IWizStep instance, and the things would roll from there.
Each step was a class exposing a UserControl responsible for the visual aspect of the step, while the logic itself was handled by the class (it was a little more complicated that that, but that was the general idea).
The IWizStep interface, implemented by the step and accepted by the dialog, was on the lines of:
Interface IWizStep
Event StateChanged As EventHandler
ReadOnly Property Control As Control
ReadOnly Property Title As String
ReadOnly Property CanMovePrevious As Boolean
ReadOnly Property CanMoveNext As Boolean
Function MovePrevious As IWizStep
Function MoveNext As IWizStep
End Interface
To put everything together, a controller class would know how to compose the steps necessary for each given action. Therefore I had a controller for, say, "Emit Order", which needed some 10 steps, and a controller for "Emit Orders in Batch", which needed only a couple of steps.
Create a set of UserControls, and add and remove them from a Panel in a single form. (and set Dock to Fill)
You could define a user control which acts as a "wizard". It just needs the buttons you have and an array of content panels, just have it switch through the panels when the buttons are pressed assuming a certain condition is met within the controls on the panel. There's no real definitive "wizard" maker, since it's pretty easy to roll your own wizard.
You don't need to do a "replacement" of your current form really, you could just add a new one to the project. If you do need to for whatever reason, just grab the control collection with Me.Controls, copy that somewhere, and put the new controls up. When you don't need the wizard, swap them out again. It's generally best practice to make a new form however!