How to show "page setup" and "printer setup" as modeless forms? - c#

Is possible to show "page setup" and "printer setup" as modeless forms? I used code as follows, but that forms display as modal forms:
// page setup
private void btnPageSetup_Click(object sender, EventArgs e)
{
this.pageSetupDialog1.PageSettings = new PageSettings();
this.pageSetupDialog1.PrinterSettings = this.printDocument1.PrinterSettings;
this.pageSetupDialog1.ShowDialog();
if (this.pageSetupDialog1.PageSettings != null)
{
this.printDocument1.DefaultPageSettings = this.pageSetupDialog1.PageSettings;
}
}
// print setup
private void btnPrintSetup_Click(object sender, EventArgs e)
{
this.pageSetupDialog1.Document = this.printDocument1;
if (this.pageSetupDialog1.ShowDialog() == DialogResult.OK)
{
this.printDocument1.Print();
}
}

You can show a form as non-modal by calling Show rather than ShowDialog.
However, you'll also have to shuffle your code around, because your main form will no longer sit and wait for one of the subforms to close in order to check what the user did.
For example, you'll have to change the Print Setup code such that your PageSetupDialog prints the document itself when the user clicks OK, rather than relying on the main form to act when the user has clicked OK.
Similarly, you'll need to change the Page Setup code such that your PageSetupDialog sets Document.DefaultPageSettings itself, rather than "returning" settings in the PageSettings property and relying on the main form handling them.

Related

How do I open another window (Form) when a button is pressed?

Complete C# beginner here, just wondering how to open an existing form from within the current form, and close the first one. I have literally no code yet so any help is much appreciated
I had a bit of a mess around, of course not knowing anything. Something like this is about all I tried just going off of intellisense prompts:
Application.Run(Terry2);
It obviously didn't work. Here was the error
Error CS0119 'Window2' is a type, which is not valid in the given context.
I have no idea where to start so thanks in advance for the help. I am using Visual Studio 2022.
Actually there are plenty of examples for this code on the internet, first you should create both of your forms and then just create an instance of your form2 in form1 under the event of your form1's button and call it's Show() method.
private void button1_Click(object sender, EventArgs e)
{
Form2 f2 = new Form2();
f2.ShowDialog(); // Shows Form2 you can also use f2.Show()
}
Here is a step by step explenation of the process that you should follow. I recommend you to watch some fundamental c# programming tutorials as well.
Click Here
If you've used windows for some time, you've noticed that there are two types of Dialog Boxes (forms): Modal and Modeless Dialog Boxes
Modal dialog boxes: while this one is being shown, you cannot use the other dialog boxes of the application; you'll have to finish this one before you can continue using your application. Example: File Open dialog box.
Modeless Dialog Box. A kind of dialog box that gives some extra information next to the other dialog box of your application. While this one is being shown, you can switch back to your original dialog box and enter some input.
How to show a modal dialog box
For this, you use Form.ShowDialog
In your form:
private DialogResult AskFileName()
{
using (Form myForm = new SaveFileDialog();
{
// Before showing the dialog box, set some properties:
myForm.Title = "Save file";
myForm.FileName = this.DefaultFileName;
myForm.ValidateNames = true;
...
// show the file save dialog, and wait until operator closes the dialog box
DialogResult dlgResult = myForm.ShowDialog(this);
// if here, you know the operator closed the dialog box;
return dlgResult;
}
}
private void SaveFile()
{
DialogResult dlgResult = this.AskFileName();
switch (dglResult)
{
case DialogResult.Ok:
// File is saved:
this.HandleFileSaved();
break;
case DialogResult.Cancel();
// operator pressed cancel
this.ReportFileNotSaved();
break;
...
}
}
A form is disposable, hence you should use the using statement. After creation of the form you have time to set the properties. The form is shown using ShowDialog. While this dialog box is shown, your form can't get the focus. After the operator closes the dialog box, your form gets the focus again. The return value of ShowDialog indicates the result.
If you want to save the file as a result of the operator selecting the menu item "file save", or pressing the button "Save", do the following:
private void OnMenuItemFileSave_Clicked(object sender, ...)
{
this.SaveFile();
}
private void OnButtonSave_Clicked(object sender, ...
{
this.SaveFile();
}
How to show a modeless Dialog Box
A modeless dialog box is shown using Form.Show. After you call this, your dialog box can get the focus. Therefore you'll have to remember that the form is being shown.
class MyModelessDialogBox : Form {...}
class MyForm : Form
{
private MyModelessDialogBox {get; set;} = null;
private void ShowMyModelessDialogBox()
{
if (MyModelessDialogBox != null)
{
// Dialog box already shown, TODO: report to operator?
...
return;
}
else
{
// Dialog box not shown yet; create it and show it
this.MyModelessDialogBox = new MyModelessDialogBox();
// if needed, before showing set some properties
// make sure you get notified if the dialog box is closed
this.MyModelessDialogBox.FormClosed += new FormClosedEventHandler(this.MyModelessDialogBoxClosed);
// show the dialog box:
this.MyModelessDialogBox.Show(this); // I am the owner / parent window
}
// when the modeless dialog box is closed, you get notified:
void MyModelessDialogBoxClosed(object sender, FormClosedEventArgs e)
{
// if needed, inspect e.CloseReason
// if needed, handle the DialogResult
// finally: not my form anymore. The form should disposes itself:
this.MyModelessDialogBox = null;
}
}
}
}
Before closing your form you should check if the dialog box is being shown, and close:
private void OnFormClosing(object sender, FormClosingEventArgs e)
{
if (this.MyModelessDialogBox != null)
{
// the modeless dialog box is being shown. Order it to close itself:
this.MyModelessDialogBox.Close();
// this will lead to MyModelessDialogBoxClosed
}
}
Sometimes you have a dialog box that refuses to close, for instance because the dialog box warns the operator and he clicks cancel. In that case, you should not Close the dialog box directly, but add a method to ask the dialog box nicely.
In the dialog box:
bool RequestToClose()
{
bool allowedToClose = this.AskOperatorIfCloseAllowed();
if (allowedToClose)
{
// close this dialog box.
this.Close();
// the owner of the dialog box will be notified via MyModelessDialogBoxClosed
}
return allowedToClose;
}

How to reopen a form after changing form to not lose the input data

I have multiple form but for now only 2. A main one which gets loaded on app lunch and the other appears when the use select it from the side menu. The problem is that if the user clicks again on the menu, it creates a new form instead of keeping the same one. Something happens if they change form.
Here is the current code that handles the Click event.
private Form activeForm = null;
private void openChildForm(Form childForm)
{
if (activeForm != null)
{
activeForm.Close();
}
activeForm = childForm;
childForm.TopLevel = false;
childForm.FormBorderStyle = FormBorderStyle.None;
childForm.Dock = DockStyle.Fill;
this.panelChildForm.Controls.Add(childForm);
this.panelChildForm.Tag = childForm;
childForm.BringToFront();
childForm.Show();
}
private void btnFalderon_Terminal_Click(object sender, EventArgs e)
{
openChildForm(new FalderonTerminal());
}
So, what modification I would have to make to my code for it to understand and check if the form is already open and displays the same one, so I don't louse the information on it. Like in this case the Serial Connection being made on the "FalderonTerminal" form.
UPDATE:
So I made some changes to the condition where it look if the form is already open. But it seem's that it's alway creating a new form instead of using the old one :/
private void btnFalderon_Terminal_Click(object sender, EventArgs e)
{
if (Application.OpenForms.OfType<FalderonTerminal>().Any())
{
Application.OpenForms.OfType<FalderonTerminal>().First().BringToFront();
System.Diagnostics.Debug.WriteLine("Old Falderon Terminal");
}
else
{
System.Diagnostics.Debug.WriteLine("New Falderon Terminal");
openChildForm(new FalderonTerminal());
}
}
If the form is not null and not IsDisposed, you should be able to just show it. The problem with this code is that closing it will cause it to dispose. In that case (IsDispose || Disposing) new to create a new copy (childForm = new ChildForm()), which will start you over with a clean form.
You should be aware about the difference between a modal dialog box and a modeless dialog box. The kind of dialog box influences how you should use it in your main form.
Modal dialog box
A modal dialog box is closed before the operator can use the main form. As long as the modal dialog box is shown the user input events of the main form are not called.
The structure of usage of this is similar to the following.
Example of a file save as modal dialog box
In your main form:
public void OnMenuFileSaveAs_Clicked(object sender, ...)
{
this.FileSaveAs();
}
And FileSaveAs:
public void FileSaveAs()
{
using (SaveFileDialog dialog = new SaveFileDialog())
{
// if needed set some properties of SaveFileDialog
dialog.Title = "File Save As";
dialog.InitialDirectory = ...
dialog.AddExtension = ...
// Show the dialog and wait until the operator is finished
DialogResult dlgResult = dialog.ShowDialog(this);
switch (dlgResult)
{
case DialogResult.Ok:
// operator selected a file name
string fileName = dialog.FileName;
this.SaveFileAs(fileName);
break;
case DialogResult.Cancel:
// operator cancelled file save as
this.CancelSaveFileAs();
break;
// etc.
}
}
}
The Modal dialog box only exists during this method. As long as the modal dialog box is not closed, the main form is not accessible.
Modeless dialog box
You want to show a Modeless dialog box. While this dialog box is shown, the operator can still handle the main form. This means that your return from the event handler in the main form before the modeless dialog box is closed. Therefore you should remember the modeless dialog box.
While the modeless dialog box is not closed, everything that makes it possible to open de modeless dialog box again should be disabled.
A nice method for this, would be to disable (gray out), the buttons and the menu items that can open this dialog box. A second method would be to leave the controls enabled, and to put focus on the modeless dialog box when the control (button / menu item) is clicked.
private MyModelessDialog ModelessDialog {get; set} = null;
private bool IsModelessDialogShown => this.ModelessDialog != null;
public void OnMenuShowModelessClicked(object sender, ...)
{
// only open the modeless dialog box if not shown yet
if (!this.IsModelessDialogShown)
{
// if desired, make sure that the menu item is disabled
this.DisableMenuItemShowModeless();
this.ShowModelessDialogBox();
}
else
{
// either do nothing, or decide to give the focus to the modeless dialog box
this.SetFocusOnModelessDialogBox();
}
}
private void DisableMenuItemShowModeless()
{
this.menuItemShowModeless.Enabled = false;
// is there also a button? Disable this as well
}
private void EnableMenuItemShowModeless()
{
this.menuItemShowModeless.Enabled = true;
// is there also a button? Enable this as well
}
You can give the modeless dialog box focus by using control.Focus(). How this is done properly is out of scope of this question.
private void ShowModelessDialogBox()
{
// Create a new modeless dialog box object, and set some properties
this.ModelessDialogBox = new MyModelessDialogBox()
{
...
}
// make sure I get called when the modeless dialog box closes
this.ModelessDialogBox.Closed += this.ModelessDialogBox_Closed;
// Show the modeless dialog box as a modeless dialog box:
this.ModelessDialogBox.Show(this);
}
The modeless dialog box is shown. The operator can handle both the modeless dialog box as well as the main form. The menu item is disabled, so the operator can't click on it. If you chose not to disable it, and you click on it, no new modal dialog box is shown. The model dialog box gets the focus.
When the dialog box is closed, your main form gets notified:
private void ModelessDialogBox_Closed(object sender, EventArgs e)
{
if (Object.ReferenceEquals(sender, this.ModelessDialogBox)
{
this.ModelessDialogBox_Closed();
}
}
private void ModelessDialogBox_Closed()
{
// if needed read and process properties of the modeless DialogBox
this.ProcessModelessDialogBoxResults(this.ModelessDialogBox);
this.ModelessDialogBox -= ModelessDialogBox_Closed;
this.ModelessDialogBox.Dispose();
this.ModelessDialogBox = null;
this.EnableMenuItemShowModelesss();
}
Because I disabled the menu items that show a new modeless dialog box, Only one box will be shown. If you chose not to disable the menu item, then clicking it will set the focus on the modeless dialog box.
One last remark: did you notice that I use a lot of small procedures instead of doing everything in a few procedures. This prepares your program for changes, for instance if you decide to add a button instead of only a menu item. Of if you need to disable several menu items. Or if you decide in future versions not to disable the menu items anymore. Small procedures also make reuse and unit testing easier.
Be aware, if you close your main window while the modeless dialog box is still shown, the modeless dialog box is closed, because its Parent is closed. Everything is handled nicely, except that the operator has no chance to react on the result of the modeless dialog box. If you don't want this, subscribe your main form on IsClosing event and check IsModelessDialogBoxShown. If so, decide what to do: cancel closing? Warn the operator about the open dialog box?

C# and Modal Windows

I have the following situation: The main window where some of the data is completed, it is also a button that opens a new modal window where you choose the product and the range of products, I click OK and move on to the next screen where choose the quantity, price, and after approval of the data which I click OK, I want to have access to the data selected in modal windows in the main window, as it can be done using C #?
You can do something that looks more or less like this:
private void button1_Click(object sender, EventArgs e)
{
Form2 firstPopup = new Form2();
firstPopup.ShowDialog();
var someData = firstPopup.SomeValue;
Form2 secondPopup = new Form2();
secondPopup.ShowDialog();
var someOtherData = secondPopup.SomeValue;
doSomeStuff(someData, someOtherData);
}
In this case SomeValue is a property on the form with a public getter and a private setter. It's value will be set sometime before the form is closed. You can have any number of such properties per form, and any number of forms.
This is similar to the way that Open, Save As and Folder dialogs work. Take for example the Open File Dialog, once you click OK, you have access to the file that was selected
Suggestion:
In your modal window, set some public properties which hold your data. Set your OK button to set the forms DialogResult to OK, in your parent form you can do the following test (in this instance, DataModel is the data you are trying to access)
if(modalWindow.DialogResult == DialogResult.OK)
{
this.DataModel = modalWindow.DataModel;
}
Here is some information on how to use DialogResult
http://msdn.microsoft.com/en-us/library/system.windows.forms.form.dialogresult.aspx
Typically with a Modal window you follow a similar pattern to that used by the OpenFileDialog, where you do something like this:
public class MyDialog : Form
{
public MyResult Result { get { /* code elided */ } }
}
I.e, in addition to having the modal form's code - you also expose a public Result property of a given type which can provide the data entered on that form's UI (this is better than exposing the controls on the form as it means you are free to change all of that without having to re-code any other forms that depends on it).
You make sure that the Result is guaranteed to be available and valid if the Ok or Yes (whatever the confirmation button is) button is clicked. Then you make sure that you set the DialogResult of the form accordingly (you can assign one to each button as well - e.g. DialogResult.Cancel to a cancel button).
Then when you open your form you do it something like this:
var form1 = new MyDialog();
if(form1.ShowDialog() != DialogResult.OK)
return; //abort the operation, the user cancelled/closed the first modal
MyResult form1Data = form1.Result; //get your actual data from that modal form.
//...and then on with your second modal
So you collect the data from the modal(s) as you go along, aborting if any are cancelled.
Try not using a modal window!
If you call
var dialog = new DialogWindow();
If (dialog.ShowDialog(mainWindow) == DialogResult.OK) {
use the result of the dialog window
}
the dialog window will be modal, which means that you cannot access other windows while it is open.
With the following code, both windows are accessible at the same time. The problem is, however, that the code in the main window is not paused while the dialog runs.
var dialog = new DialogWindow();
dialog.Show(mainWindow);
You cannot use the result of the dialog window here!
Therefore you need a way to communicate the completion of the dialog to the main window. I suggest creating an event in the dialog window for this purpose
public class ProductResultEventArgs : EventArgs
{
public ProductResultEventArgs(List<Product> products)
{
Products = products;
}
public List<Product> Products { get; private set; }
}
In the dialog window
public event EventHandler<ProductResultEventArgs> ProductsChosen;
private void OnProductsChosen(List<Product> products)
{
var eh = ProductsChosen;
if (eh != null) {
eh(this, new ProductResultEventArgs(products));
}
}
private BtnOk_Click(object sender, EventArgs e)
{
OnProductsChosen(somehow get the product list);
}
In the main window you can do something like this
var dialog = new DialogWindow();
dialog.ProcuctsChosen += Dialog_ProductsChosen
dialog.Show(mainWindow);
and create a handler
private Dialog_ProductsChosen(object sender, ProductResultEventArgs e)
{
use e.Products here
}
Note: Passing the main window as parameter to ShowDialog or Show makes the main window owner of the dialog. This means that the dialog window will always stay in front of the main window and cannot disappear behind it. If you minimize the main window, this will minimize the dialog window as well.

How to disable drag/drop when a dialog box is open

I am working on a large application and am adding some drag/drop functionality to it. Specifically, I am allowing the user to drag and drop a file into the main window to open the file.
The problem is that the drag/drop operation is still allowed to happen when the main window is displaying a dialog box (for example, a properties window for an item in the currently-open file). I would rather not allow this to happen if the main window is displaying a modal dialog box. This is because loading the new file in the application while the dialog box is open would probably crash the program: the code calling the dialog box does not expect the open file to be changed while the dialog box is open (that is why the dialog box was modal...).
The main application is written in C++, but I am posting a C# sample. The symptom/behavior is the same on both platforms, but I can demonstrate it in much less code with C#. I am very familiar with both languages/platforms so I can translate any answers to the appropriate language as needed.
To demonstrate the problem with my sample code, compile and run the following C# code. It will create a "main window" that is a valid drop target. Drag and drop a file from Windows Explorer onto the main window: you should see a "dropped" message box. Now, click the button on the form to pop up a dialog box. Again, attempt to drag and drop a file onto the main window while the dialog box is open. Notice that the drop is allowed even though a modal dialog box is open. How can I prevent this from happening when the dialog is open?
The obvious answer is to temporarily set AllowDrop to false while opening the dialog box. The problem is that the main application is very large and so there are numerous places that open dialog boxes. It will be difficult to find every single place that opens a dialog and add this code. Plus, every developer here would need to know to perform this action every time they open a modal window; it is unlikely that everyone will remember. I am worried that this is not a very good solution.
Surely there is a more maintainable method of doing this that doesn't require adding code in every place that a dialog is opened?
using System;
using System.Windows.Forms;
using System.Drawing;
public class MyDialog : Form {
public MyDialog() {
Text = "MyDialog";
}
}
public class MainForm : Form {
public MainForm() {
Button btn = new Button();
btn.Location = new Point(0, 0);
btn.Text = "ShowDialog";
btn.Size = new Size(75, 23);
btn.Click += new EventHandler(GoToDialog);
this.AllowDrop = true;
this.Controls.Add(btn);
this.Text = "Drop Target";
this.DragDrop += new DragEventHandler(this.MyDragDrop);
this.DragEnter += new DragEventHandler(this.MyDragEnter);
}
private void MyDragDrop(object sender, DragEventArgs e) {
MessageBox.Show("dropped");
}
private void MyDragEnter(object sender, DragEventArgs e) {
e.Effect = DragDropEffects.Copy;
}
private void GoToDialog(object sender, EventArgs e) {
using (MyDialog ab = new MyDialog()) {
ab.ShowDialog(this);
}
}
}
static class Program {
[STAThread]
static void Main() {
Application.Run(new MainForm());
}
}
I'm not sure how things work in C#, so let me know if this answer is incorrect. In C++ MFC, the main window is disabled when a dialog is displayed. You can test to see if the main window is disabled and ignore the drop if so.
private void MyDragDrop(object sender, DragEventArgs e) {
if (CanFocus)
MessageBox.Show("dropped");
}
private void MyDragEnter(object sender, DragEventArgs e) {
if (CanFocus)
e.Effect = DragDropEffects.Copy;
else
e.Effect = DragDropEffects.None;
}

Working with Forms

I'im writing a program that works with 2 forms, the main form and the form where the configuration is made, so when the user clicks toolstripmenu->Preferences the Preferences form is showned and I want it to make the user only capable of having one Preferences form at a time.
When I use:
Prefs preferencias = new Prefs;
private void preferenciasToolStripMenuItem_Click(object sender, EventArgs e)
{
preferencias.Show();
}
It works, but when I close the Preferences form and try to open a new one the program crashes.
And When I use:
private void preferenciasToolStripMenuItem_Click(object sender, EventArgs e)
{
Prefs preferencias = new Prefs;
preferencias.Show();
}
The user can hav multiple Preferences form.
What can I do?
Thanks in advance.
It sounds like you want a modal dialog, so you need to use the ShowDialog( ) method instead of Show( ):
private void preferenciasToolStripMenuItem_Click(object sender, EventArgs e)
{
preferencias.ShowDialog();
}
The ShowDialog() that others have suggested is a good answer. If you're interested in an alternative, here's something I sometimes do:
private void FormClosing(object sender, FormClosingEventArgs e)
{
if (e.CloseReason == CloseReason.UserClosing)
{
e.Cancel = true;
Hide();
}
}
What this does is simply hides the form so that if you show it again, its already loaded into memory. Additionally, if you have a timer or some other thread in that form running, it can still run and do its thing.
preferencias.ShowDialog()
will only allow one preference window to be open .
You can use the Application.OpenForms property in your menu item's click event to check if a form of that type is already open. If there is no form of that type open, then you can open your instance. If there is, it simply won't show.
foreach (Form form in Application.OpenForms) {
if (form.GetType() != typeof(PreferencesForm)) {
new PreferencesForm().Show();
}
}
Or as already stated you can call PreferencesForm.ShowDialog() to make the form modal, in which case the user has to close the form before they can even interact with the main form again.
The method you use depends on if you want the user to be able to use the main form even if the preferences form is open.
If you're looking for 1 and ONLY 1, You probably want to implement the Singleton pattern for the Prefs class.

Categories