Passing Details From One Window to Another Window - c#

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

Related

C# WinForms ShowDialog() call arbitrary method

I'm working on a console application that creates a form to alert users of some given state - at a later stage, the code base will become a class library.
For now, however, I need to show the form (ShowDialog would be the best method here, I guess) THEN call an arbitrary method before the form closes.
As an example, I need to show the form, set the text value of a label control, wait for n number of seconds, then change the value of the label, then close the form. I know that this sounds a little trivial, but I'm trying to proof-of-concept the design.
I've taken a look around and it doesn't look like this is possible, as ShowDialog() requires me to close the form before I can continue through code listing in the calling method/class.
Here's what I have so far:
PopUpForm myForm = new PopUpForm(string messageToDisplay);
myForm.ShowDialog();
//call myForm.someMethod() here, before the form closes
//dispose of the form, now that we've no use for it
myform.Dispose();
//target method in PopUpform class
public void someMethod()
{
lblText.Text = "Waiting for some reason";
//wait n number of seconds
lblText.Text = "Finished waiting. Form will now close";
//it doesn't matter if the form closes before the user can see this.
}
It looks like ShowDialog() doesn't support this sort of behaviour. I'm looking into BackgroundWorker threads, but was wondering if anyone has any advice on this, or have encountered this before.
If you want to show the form, then continue working, then close it - you can do so via Form.Show() instead of Form.ShowDialog():
using (var myForm = new PopUpForm(messageToDisplay))
{
myForm.Show(); // Show the form
DoWork(); // Do your work...
myForm.Close(); // Close it when you're done...
}
However, if this is purely a console application (and doesn't have a message pump), then this will likely not work properly.
Other options would be to provide a timer within your Form to have it close, or pass a delegate into the Form to run your method on Show, after which it could close itself.
at a later stage, the code base will become a class library.
When you do this, you'll likely want to come up with a different mechanism to provide notifications. Coupling your library to a specific UI technology is a bad idea. It would likely be better to have your library just provide events or other notification, and allow the user to provide the UI/notification to the user.
If this code ie destiend to end up in a code library, I recommend against having it display any forms, ever, of its own volition. The library should generate events, or in some cases exceptions, that can be caught by the invoking application to allow it to display the form. If certain details requiring presentation to the user are internal to the library, expose a DisplayEventData() method from the library.
The ShowDialog-method creates a "modal" window, and usually blocks the UI until you close it (either by clicking OK or Cancel). You would need to create a WinForm yourself, can be a simple one though and create a message-pump for it. You can run your own form by calling
Application.Run(YourForm);
You would need to hold your console-thread with a mutex for example, to keep it from continuing, while the form is open.
The form offers all the methods you know from WinForms like Close, where you could tell your console-thread to continue.
See this on MSDN.

C# Using a form to load other user controls and have access to a base control or property

Currently I have a C# program with a windows form and then a user control template put onto the form. The user control template is really just used as a placeholder. I have a series of other controls which inherit from this user control template.
Each of those controls have navigation buttons like 'Continue' and 'Back' on them and each control knows which control needs to be loaded next. However what I need to figure out is an easier way to have variables that are global to these controls.
The only workaround I have is that I pass the form to each control when they are loaded and use variables inside of the form to read and write to. What would be the proper way to have each of these user control screens be built off of a base control which contained objects all of the controls could get to?
Sorry for the rambling nature of the post but I've been thinking about this problem all morning.
Here is some of the code:
Most of what I have written was based on hiding and showing the user controls so that content in the controls wouldn't be lost during navigation. I won't be needing to do that as eventually it will be loading the fields of data from a database.
Code for initially loading control from form click:
conTemplate1.Controls.Clear();
conInbound Inbound = new conInbound(this);
Inbound.Dock = DockStyle.Fill;
Inbound.Anchor = (AnchorStyles.Left | AnchorStyles.Top);
conTemplate1.Controls.Add(Inbound);
Code for Continue button inside of one of the controls:
if ((Parent.Controls.Count - 1) <= Parent.Controls.IndexOf(this))
{
UserControl nextControl = new conPartialClear();
nextControl.Dock = DockStyle.Fill;
Parent.Controls.Add(nextControl);
this.Hide();
Parent.Controls[Parent.Controls.IndexOf(this) + 1].Show();
}
else
{
this.Hide();
Parent.Controls[Parent.Controls.IndexOf(this) + 1].Show();
}
The best-practice for communicating from a control to a parent is to use events, and for communicating from a parent to a control is to call methods.
However, if you don't want to or can't follow this practice, here's what I would recommend.
Each UserControl has a ParentForm property that returns the Form that contains the control. If you know that the UserControl will always be attached to MyParentForm, you just cast the ParentForm and then you can access all public controls, methods, etc.
Here's what I mean:
public class conTemplate
{
public MyParentForm MyParentForm
{
get
{
return (MyParentForm)this.ParentForm;
}
}
}
This way, you can easily access any public members of MyParentForm. Your conInbound class could have code such as this.MyParentForm.GlobalSettings.etc..., and could even have access to any public controls.
I'm not totally sure I understand your problem. It sounds like you want the user control to "do something" with it's parent form. If that's the case, you may want to consider adding events to the UC and then handle them on the form itself.
Basically, for your UC's "continue", you'll have an event that's fired when it's pressed. You'll want to handle that in your form. I'm not real sure about the syntax from memory, or I'd work something out for you code-wise. But I think that's the route you'll want to take. Think of your UC like any other windows form control. If you add a button to your form, you assign it it's event method. Do the same with the UC.
I found this and thought it may be helpful. Scroll down to where it talks about UC's and events.
http://www.akadia.com/services/dotnet_user_controls.html
Hope this helps.
EDIT after new info from OP.
You could declare a global variable inside the UC of type yourForm and then set that variable to the ParentForm at run-time, if I'm understanding you correctly.
So, inside your UC Class, you could do:
private parentFormInstance;
then inside the constructor of the UC, you could set it as such:
parentFormInstance = this.ParentForm; (or whatever the property name is).
This allows you at design-time to use:
parentFormInstance.DoSomething();
without the compiler yelling at you.
Just basic advice, but if you can go back and make it easier on yourself, even if it takes some additional time re-working things, it'd be worth it. It may save you time in the long run.

Window manipulation and inctences control

In my application there are only 2 windows — win_a & win_b, on each of these windows there is button that call another window, e.g. click on btn1 of win_a will call win_b, click on btn2 of win_b will show win_a.
Desired behaviour:
1. Only one instance of object is premitted at the same time, e.g. situation, where 2 instances of win_a running at the same time is not permitted.
When you click on button that calls windows that already exist this action will only change a focus to needed window.
If you call a window that previously had been created, but after this has been closed this action will create a new instance of this window. E.g. there are 2 running windows. you close one of them and after try to call this window back, so related button will create it.
How to write it in WPF (XAML + C#). For the moment I wrote a version that can create a lot of instances of the same window (no number of instances control implemented), but I want to see only one instance of the same window, as we can see it in a lot of applications.
Example of my code:
Window win = new Window();
win.Show();
Thanks.
first you need 2 references on each other window. on button click
you need to check one reference.
say in win_a
//win_b is a member on Windows_a class
if(_win_b.IsVisible())
{
// set focus on it
}
else
{
//show win_b
}
make the same for windows_b
I would suggest a different approach:
make a singleton class, that holds a list of tuples List>
when creating windows you can check if the window is in the collection or not.
if the collection holds a window you can set it activ win.Activate(),
else you can create it and add a reference to the collection list.add(tuple(win,"windowA"))
3.finally on the windows that you can add to the collection, on closing you need to remove the window from the singletons list, you can do this handling the Close event of the window
i don't have the code i wrote here, but i hope it helps.

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

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

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