I'm building an application using C#/WCF. There will be a main form project is essentially the thing that connects all the other projects (DLLs) and the DLLs are not aware or allowed to reference each other. However in one DLL, say the patient dll, there is a form with a button and when that button is clicked it needs to open a form in a different dll say the rx dll but the two DLL's can't reference each other they are only connected via the main form.
So I was wondering if it's possible to accomplish a task like that and if so how to go about it. I would prefer not to use a message queue or send message if possible.
Thanks for any advice or help.
You can link the forms with an event:
Add this to the form with the button:
public event Action OpenOtherFormClick;
button_Click (object sender, EventArgs e) //Link to the button's Click event...
{
if (OpenOtherFormClick != null) { OpenOtherFormClick()}
}
Add this to the main project:
instanceOfFormWithButton.OpenOtherFormClick += () =>
{
//Open other form, e.g.
new OtherForm().Show();
//or
new OtherForm().ShowDialog();
//or, with factory class:
FormFactory.ShowOtherForm();
};
alternatively:
instanceOfFormWithButton.OpenOtherFormClick += FormFactory.ShowOtherForm;
The factory has the advantage that you can restrict certain forms to a single instance, for example to show a detail form only once and only focus it if it's already open.
I am not sure if I truly understand your question, but you might take a look at MEF. However you can use WCF for interprocess communication (net.pipe), it's not a good practice.
Related
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.
I have a Caliburn.Micro shell (i.e., an empty XAML view to contain other views) rendered by a Conductor ViewModel. From there I open a Screen via:
ActivateItem(...)
Usually from the newly displayed dialog the user can perform some operations and click buttons (OK, Cancel, Build....) which should each transition to another screen (in the shell).
public MyDialog : Screen
{
public void Ok()
{
// TODO: Somehow tell the conductor or called of this class about this action.
}
}
What are good ways to achieve these kind of dialog action/message screen transitions?
Simple .NET events are possible -- Wouldn't that be a bad idea?
CM IEventAggregator should also work by changing the view
Checking from the shell Conductor the ViewModel result once it has been closed via TryClose() -- Should be possible, just don't know how to achieve this in CM.
Reference the shell Conductor instance from that screen (via IoC or directly) -- That seems strong coupling.
Could you please advise.
My preferred approach is to use the EventAggregator to facilitate messaging between VMs.
This works especially well when you have multiple windows which are listening for a certain type of event (e.g. a Visual Studio style interface with multiple tool windows which may show context sensitive properties), however it sounds a little overkill for this implementation. Of course the advantages are still a good loose coupling between VMs and a lack of events (which is a good thing!)
It sounds like you want a modal dialog to popup and present an option, and then activate another screen once the first one has returned.
You can attach an event handler to the Deactivated event in the child VM which will fire when an item deactivates. It also passes a boolean in the arguments to notify if the item which deactivated was closed - you can check for this and activate the corresponding screen in your conductor.
e.g.
this.Deactivated += new EventHandler<DeactivationEventArgs>(WorkspaceViewModel_Deactivated);
void WorkspaceViewModel_Deactivated(object sender, DeactivationEventArgs e)
{
if(e.WasClosed) // raise some event
}
Then pass an event up to the conductor, I wouldn't really go the event route for this. This couples the VMs one-way so it may not be the most flexible solution
The alternative is to fire a message via the event aggregator to tell the conductor it needs to open a different window when the child VM closes. The same method can be used but it's decoupled
this.Deactivated += new EventHandler<DeactivationEventArgs>(WorkspaceViewModel_Deactivated);
void WorkspaceViewModel_Deactivated(object sender, DeactivationEventArgs e)
{
if(e.WasClosed) MainConductor.EventAggregator.Publish(new ActivateWindowMessage(typeof(SomeVM));
}
I have a problem using CAB Framework. I created some GUI User control interfaces and put them on my screen, that works fine. However, I would like to save my data between the different user controls. I made some DataContracts using WCF and I put the data I need in the DataContract and put those in properties so they're accessible from the WorkItem. Now I used the following function in the WorkItem to process the data:
protected override void OnWorkItemSmartPartChanged(object sender, EventArgs e)
{
MessageBox.Show(_testWorkItemControlAdres.AdresContract.Gemeente);
}
When I change between user controls, I want to see if it can succesfully access the data. However, when I switch between the wizard steps (the user controls) it never triggers the event. Do I need to add this event somewhere else or not?
Did you actually link the event to the WorkItem?
Workspace.SmartPartActivated += new EventHandler(OnWorkItemSmartPartChanged);
Cheers
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.
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();
}
}