I'm creating a custom component for my WinForms application. It will handle file commands such as New, Open, Save, and Save As.
I've done this before so I'm pretty clear how I want it to work. But I'm wondering if there is any way to have the component automatically hook the related commands in the main form. Or maybe even dynamically add those commands to the main form. This would save me the trouble of having to hook up those commands in each application that uses this component.
I'm not married to using a component if there's a better way. Basically, it is a class that accesses other components like the common dialogs.
Thanks for any tips.
I was not able to find any slick way to do this.
I ended up just adding a method to my component, which the form must call, that adds all the menu commands and attaches the component's own handlers. The method takes one argument: an instance of the ToolStripMenuItem that the commands should be added to.
public void CreateMenuCommands(ToolStripMenuItem menuItem)
{
menuItem.DropDownItems.Insert(0, new ToolStripMenuItem("&New", null, MenuNew_Click, Keys.Control|Keys.N));
menuItem.DropDownItems.Insert(1, new ToolStripMenuItem("&Open...", null, MenuOpen_Click, Keys.Control | Keys.O));
menuItem.DropDownItems.Insert(2, new ToolStripMenuItem("&Save", null, MenuSave_Click, Keys.Control | Keys.S));
menuItem.DropDownItems.Insert(3, new ToolStripMenuItem("Save &As...", null, MenuSaveAs_Click));
}
It still requires a manual step on the part of the form, but it's a simple matter to call this single method.
Related
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
So I have a main window that shows MDI type interface with multiple document tabs open inside it (just like VS). Both the main window and the document windows have their respective VMs. The CloseDocument command is handled in the document, but needs to tell main window VM about it, so that main window VM could update its Documents collection. What is the proper way of managing this in MVVM? A few ideas that I have:
I could add an event to document VM that is raised just before closing. I could then add its event listener to main window VM for each new document that I add.
I could move the CloseDocument command to main window VM, but ideally the event doesn't belong there.
I could pass reference of the Documents collection to my document VM, so that it updates the collection before closing itself.
Which among these (or if someone has a better one) should be used while following MVVM practices?
I think I would pick solution 1. If you use MVVM Light then you can apply Messenger type to pass the information between Documents.
Each document would have a Command with reference to this method:
private void CloseDocumentExecuteCommand()
{
var message = new DocumentCloseMessage() { Document = this};
Messenger.Default.Send<DocumentCloseMessage>(message);
}
And in the VM of main Window you would have something like this:
(in constructor)
Messenger.Default.Register<CloseMessage>(this, (msgData) => this.CloseMessageReceived(msgData));
... but this could works only if you have Messenger, otherwise you could use events, but then I am afraid you need to use strong references between VMs.
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.
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.
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();
}
}