How can I get the reference to currently active modal form? - c#

I am writing a small class for driving integration testing of a win form application. The test driver class has access to the main Form and looks up the control that needs to be used by name, and uses it to drive the test. To find the control I am traversing the Control.Controls tree. However, I get stuck when I want to get to controls in a dialog window (a custom form shown as a dialog). How can I get hold of it?

You can get a reference to the currently active form by using the static Form.ActiveForm property.
Edit: If no Form has the focus, Form.ActiveForm will return null.
One way to get around this is to use the Application.OpenForms collection and retrieve the last item, witch will be the active Form when it is displayed using ShowDialog:
// using Linq:
var lastOpenedForm = Application.OpenForms.Cast<Form>().Last()
// or (without Linq):
var lastOpenedForm = Application.OpenForms[Application.OpenForms.Count - 1]

I'm not sure if you can access controls on a pre-built dialog box; they seem all packaged together. You may have more luck building a dialog box of your own that does what you want it to do. Then you can access the .Controls inside of it.

Correct me if i'm wrong, though, it sounds as if you are possibly attempting to access the controls on the dialog form when it's not quite possible to.
What I mean is, ShowDialog will "hold up" the thread that the form was created on and will not return control to the application (or, your test class) until ShowDialog has finished processing, in which case your user code would continue on its path.
Try accessing or manipulating the controls from a separate thread (in this case, refactor the test driver class to spawn a separate thread for each new form that must be displayed and tested).

Related

Passing Details From One Window to Another Window

I have a selection of TextBoxes that a user fills in when they wish to note that they have had contact with another person. Most of the TextBoxes are imply filled in by typing into them. However, for one of them I would like the user to be able to select from a list of People that appears when they click on a button.
This is where I am having problems. So far I have just made a DataGrid appear and handled it's SelectionChanged method to fill in the TextBoxes text property. This has worked fine, however now there is not enough space on the current page to show an entire DataGrid with all the people they can select from.
I've decided to show the People in a separate, smaller Window that appears when the user clicks a Button. The issue I have is that when the user selects the Person they wish to mark the contact for in the new Window, I have no idea how I can notify the original Window that a Person has been selected, close the new smaller Window and fill in the appropriate TextBox on the original Window.
What would be the most intuitive way to fill in the TextBox on the original Window, based on the selection on the Window that opens?
I would use delegates,which call a function of the original window and parse the changed variable with it. So you know when the user clicked something and you can directly react to this "event".
Link:
https://msdn.microsoft.com/en-us/library/ms173171.aspx
If you use a framework like Galasoft's MVVM Light (http://www.galasoft.ch/), they have a messenger system just for this purpose. It allows you to "broadcast" messages that can be "received" by any other part of the application
This is when considering using Domain, Model, Presentation (Winforms/WPF version of MVC formatting) to do your app.
You can have each form as its own class, well they are their own class. Create each form class but add some public members to it if the controls are private. Have them have "get" properties only and to return the values of whatever controls or variables are in that form. Your main form will be the controlling form. All forms will be handled by the main form so when you open it, it is a class the main form can access.
Now, if I remember (been doing more MVC and not any Winforms lately) I believe if you use the ShowDialog() method it will freeze the main thread so when you close out the main form you can continue and read in public members you have in your forms class you opened. Synchronous I believe it runs as. If you use just Show() the thread will keep on trucking, asynchronous. With asynchronous you may then have to use a main form in your startup code so there is always a window there but subscribe to the close event of your forms and have a method that can grab those public members out. Be sure to instantiate the extra forms at the root of the main class so it doesn't fall out of scope when it exists the method that calls it. You may even be able to make the method that calls is a async call and have an await before the command that runs the Show method on the form.
Summary, treat each form as its own class but add public members that can read the values from the controls and/or variables you want. Read that data from the class when it closes via an event or synchronously when the thread closes out from the form closing. The form closing doesn't discard the object, just the visualization of the form.
Oh, if you are passing info from the main form to a child for you are opening, either add a constructor for that form class that takes your input as a model or values to fill in the appropriate variables or forms before showing it or create a public property you can put your values you want to send in before showing the class.
Remember, everything is a class, once you look at it as such and treat it as such, the answer will come. :-)
I should warn, I am a long winded explainer.
At work putting all this down from memory so some errors may exist. Let me know if there are.
I think the problem is to access the controls of the main window, isn`t it?
You can define an event of changing user`s choise and access MainWindow control by using the following construction:
((MainWindow)Application.Current.MainWindow).MyTextBox

Accessing forms from different locations in WPF

Still getting used to WPF from a win forms programmer. I have multiple forms in an application that can be accessed from multiple locations, so I need to keep the forms "global" as I'm not sure of a better terminology.
For instance "Details" can be opened from a "Main Menu" but can also be opened from a grid in "Search", I'd like the details returned from the search to be displayed in the "Details" page even if it was pre-opened from the main menu.
I've come across Application.Current.Properties and have started storing a few forms in it but it just feels plain wrong to set:
Vehicle vehicleForm = new Vehicle();
Application.Current.Properties["frmVehicle"] = vehicleForm;
And then to access it:
if (Application.Current.Properties["frmVehicle"] == null)
Application.Current.Properties["frmVehicle"] = new frmVehicle();
Vehicle vehicleFrm = (Vehicle)Application.Current.Properties["frmVehicle"];
vehicleFrm.Show();
vehicleFrm.Activate();
I have just discovered Application.Current.Windows as well which has thrown me a little.
What is the most efficient/industry standard way of dealing with form like this?
I would just check whether Application.Current.Windows contains an instance of your window. If so then you give it focus, if not then you create an instance.
I'm not sure if I understand how are you opening the window correctly. But if all you want to do is to have one instance of the window through the whole run time of the application, you can use the Singleton pattern. Basically, the window class has a static property that holds the only instance.
If you don't need to keep any state in the window, you can just create new instance of it every time you want to show it.

Dock Windows Forms (tabbed chat interface)

Edit for those who say to use tab control
I would love to use a tab control; yet i have no idea how to go about linking the tab control up from the main form. I would assume that I would have to do something like this:
Create Form with a blank TabControl on it, no pages created.
Create a CustomuserControl (Add -> user Control), with my controls on it.
When a new chat comes in, create a tab control Item, Tab Control Page, add the Custom Control to the Tab Control Page. Add the tab control handle to the hash table, so that when new messages come in, they can be referenced in the proper control.
But, i am so not sure how to do this. For example, I know that I can create custom events inside of the User Control, so that, for example, if each control has a 'bold' button, i can each page that has that control on it, to actually USE the button.
Yet i also need to register message callbacks, so that I can use a MessageGrabber to send data to it, and tha'ts not assigned inside of the UserControl, that's assigned programatically when a new window comes in; but since I have no controls to reference, i can't assign.
KISS Philosophy
Wouldn't it be easier to just create the form, like i do now, and then just dock that form within a window or something? So that, in essence, it's still creating the form, but it's also a separate window?
Original Question
Okay, so i'm stumped (which isn't that big of a surprise when it comes to complex C# logic lol)! What i'm trying to do is the following:
Goal: Setup tabbed chatting for new chat application.
Completed: Open new window whenever a chat message is received, or a user requests a new chat from the roster. This is working perfectly, and opens only a window when the user doesn't already have the chat open. Nice and happy there.
Problem: I dont want windows. Well, i do want A window, but, i do not want tons of separate windows. For example, our Customer Service team may have about 10 active IM windows going at one time, i do not want them to have to have 10 windows tiled there lol. I'd rather they have a single Private IM window, and all 10 tabs docked within the window.
Logic: This is my logic here, which may be flawed, i do apologize:
OnMessage: Open new chat window if one doesn't already exist; if one exists, open it as a tab within the current chat window.
SendMessage: ^^ ditto ^^
Code Examples:
if (!Util.ChatForms.ContainsKey(msg.From.Bare))
{
RosterNode rn = rosterControl1.GetRosterItem(msg.From);
string nick = msg.From.Bare;
if (rn != null)
nick = rn.Text;
frmChat f = new frmChat(msg.From, xmpp, nick);
f.Show();
f.IncomingMessage(msg);
return;
}
Note on above: The Util. function just keeps tracks of what windows are opened inside of a hashtable, that way, when messages come in, they route to the proper window. That is added with the:
Util.ChatForms.Add(m_Jid.Bare.ToLower(), this);
Command in the frmChat() form.
Library in Use: agsxmpp from: http://www.ag-software.de/agsxmpp-sdk/download/
Problem:
How can i convert this code to open inside of tabs, instead of windows? Can someone please give me some ideas, and help with that. I just can't seem to wrap my head around that concept.
Use TabControl

Manage muliple windows forms in c# app

Any thoughts,recommendations, patterns on a good way to manage an app with multiple forms.
First page is login, that loads a "main form", from there a user could launch a number of other "sub forms" (could grow over time). User should be able to cancel out of whole app at any point.
I know the way I do it right now is not always elegant.
Cody
Consider using the DockPanel Suite. It allows you to create multiple form, and have a complete docking panel solution for you application.
I like an Explorer-style interface: Use a splitter in the main form. Use a list control or tree control in the left side, and add a reference to each sub-form as a tag on an item. When the user clicks on the item, push the sub-form from the tag to the right side of the splitter.
You can monitor the select changed event on the list/tree to do form-level validation.
There's nothing fundamentally wrong with how you've set things up, but I would change the relationship between your login form and your main form so that your main form isn't loaded by your login form.
In the Main method in your Program.cs file, replace this (presumed) line:
Application.Run(new LoginForm());
with something like this:
LoginForm login = new LoginForm();
DialogResult result = login.ShowDialog();
login.Dispose();
if (result != DialogResult.Cancel)
{
Application.Run(new MainForm());
}
Rewrite your LoginForm so that it just returns DialogResult.OK if the login is successful (this.DialogResult = DialogResult.OK), instead of loading and showing an instance of MainForm.
From that point on, there's nothing wrong with loading and showing additional forms from MainForm, provided that a user interface like that makes sense for your program (like, for example, a graphics editing program that incorporates various other floating tool windows as needed).
The user can "cancel out" of your entire application by just closing the main form, which is quite normal behavior for a Windows program.
"to manage an app with multiple forms."
Hi Cody,
Tip : do keep in mind that you have access to a very handy way to know, at all times (in any context where you are using the System.Windows.Forms library) the number of open forms in the Application via :
Application.OpenForms.Count
I think a lot depends on what exactly you mean by "manage" your multiple forms, and by what you mean by the term "sub-forms." I'm assuming you're already familiar with the built-in MDI multiple window facility in .NET, and looking for an alternative to that, or you wouldn't be asking this question. Please correct me if my assumptions are wrong.
If you mean by "sub-forms" : forms created within the scope of your "Main Form," then they will, of course, be disposed when the Main Form is closed.
I personally like the "multiple independent window" model in WinForms sometimes referred to as SDI+ (I think it was Chris Sells that coined this acronym).
I like to start by "hi-jacking" the standard Program.cs file so the Main procedure looks like this :
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Initializer.Initialize();
// it's now your responsibility to shut down the application !
Application.Run();
}
Where 'Intializer is a public static class with one public static method 'Intialize; it might look something like this :
public static class Initializer
{
public static StartFormTemplate StartForm;
public static MainFormTemplate MainForm;
public static void Initialize()
{
MainForm = new MainFormTemplate();
MainForm.FormClosing += new FormClosingEventHandler(FormClosing);
// StartForm will display MainForm in this case
// MainForm.Show();
StartForm = new StartFormTemplate();
StartForm.FormClosing += new FormClosingEventHandler(FormClosing);
StartForm.Show();
}
}
Every form created within the scope of the Initializer class is going to be an "indepedent window" (effectively with : Parent == null).
The interesting "business" in this model is the logic in the FormClosing event (not shown here) which, in this example, both StartForm and MainForm share. You can easily test for the Mainform closing by taking the 'sender parameter of the FormClosing call and comparing to MainForm :
MainForm == sender
And you can use Application.OpenForms.Count to detect when there is only one "independent" window left to close. The tricky part is making sure you detect the cases where you want to keep the MainWindow open and handle those first, cancelling the FormClose event as necessary.
I think that's enough for starters : this may be a "primrose path" you don't really wish to go down :)
best, Bill
I would recommend the Smart Client UI Application Block.
The Block is designed to help you build complex, WinForm–based solutions. It provides a proven architecture and implementation that helps you to build applications using the common patterns found in line-of-business front-end applications.
It allows your application to be
based on the concept of modules or
plug-ins.
Maintainable, reusable code through UI composition.
It facilitates development using
patterns for loose coupling between
modules.
Separation of Model (business logic and data access) from Presentation.
The Model-View-Presenter pattern.
You can use MDI. To do this, set the IsMdiContainer property of the parent from to true, and set the MdiParent property of each child from to the parent from before the child form is shown. You can then use an MdiList menu to automatically list all of the child forms.
Alternatively, you can use tabs; this is easiest to do with a third-party framework.
Finally, you could do it normally by calling the Show method of each child form with the parent form as a parameter.
For a more detailed and specific answer, please provide more details.
I use a navigation-style UI for that sort of thing. Check out this article on MSDN which describes a WinForms framework for "inductive UI" applications.

Load a form without showing it

Short version: I want to trigger the Form_Load() event without making the form visible. This doesn't work because Show() ignores the current value of the Visible property:
tasksForm.Visible = false;
tasksForm.Show();
Long version: I have a WinForms application with two forms: main and tasks. The main form is always displayed. The user can either click a button to open the tasks form, or click some buttons that just run a task directly without opening the tasks form.
When a user asks to run a task directly, I'd like to just call some public methods on the tasks form without showing it. Unfortunately, the task logic depends on stuff that happens in the Form_Load() event. The only way I can find to trigger Form_Load() is to call Show(). The best I've been able to do is to show the form in the minimized state:
tasksForm.WindowState = FormWindowState.Minimized;
tasksForm.Show();
I suppose the cleanest solution would be to pull the tasks logic out of the tasks form and into a controller class. Then I can use that class from the main form and from the tasks form, and only load the tasks form when I need it visible for the user. However, if it's an easy thing to load the form without displaying it, that would be a smaller change.
Perhaps it should be noted here that you can cause the form's window to be created without showing the form. I think there could be legitimate situations for wanting to do this.
Anyway, good design or not, you can do that like this:
MyForm f = new MyForm();
IntPtr dummy = f.Handle; // forces the form Control to be created
I don't think this will cause Form_Load() to be called, but you will be able to call f.Invoke() at this point (which is what I was trying to do when I stumbled upon this SO question).
It sounds to me like you need to sit down and re-think your approach here. I cannot imagine a single reason your public methods need to be in a form if you are not going to show it. Just make a new class.
I totally agree with Rich B, you need to look at where you are placing your application logic rather than trying to cludge the WinForms mechanisms. All of those operations and data that your Tasks form is exposing should really be in a separate class say some kind of Application Controller or something held by your main form and then used by your tasks form to read and display data when needed but doesn't need a form to be instantiated to exist.
It probably seems a pain to rework it, but you'll be improving the structure of the app and making it more maintainable etc.
From MSDN:
Form.Load
Occurs before a form is displayed for the first time.
Meaning the only thing that would cause the form to load, is when it is displayed.
Form.Show(); and Form.Visible = true; are the exact same thing. Basically, behind the scenes, Show checks for various conditions, then sets Visible to true. So obviously, setting visible to false (which it already is) before showing the form is meaningless.
But let's forget the technicalities. I completely agree with Rich B and Shaun Austin - the logic shouldn't be in that form anyway.
Sometimes this would be useful without it being bad design. Sometimes it could be the start of a migration from native to managed.
If you were migrating a c++ app to .NET for example, you may simply make yourwhole app a child window of the .NET form or panel, and gradually migrate over to the .NET by getting rid of your c++ app menu, status bar, toolbar and mapping teh .NEt ones to your app using platform invoke etc...
Your C++ app may take a while to load, but the .NET form doesn't..in which you may like to hide the .NEt form until your c++ app has initialised itself.
I'd set opacity=0 and visible=false to false after calling show, then when your c++ app loads, then reverse.
If you make the method public, then you could access it directly.... however, there could be some unexpected side effects when you call it. But making it public and calling it directly will not draw the screen or open the form.
Move mandatory initialization code for the form class out of the Load event handler into the constructor. For a Form class, instantiation of an instance (via the constructor), form loading and form visibility are three different things, and don't need to happen at the same time (although they do obviously need to happen in that order).
None of the answers solved the original question, so, add the below, call .Show() to load the form without showing it, then call .ShowForm() to allow it to be visible if you want to after:
private volatile bool _formVisible;
protected override void SetVisibleCore(bool value)
{
base.SetVisibleCore(_formVisible);
}
public void ShowForm()
{
_formVisible = true;
if (InvokeRequired)
{
Invoke((Action) Show);
}
else
{
Show();
}
}

Categories