I have a Visual C# 2010 application, and it has one main form called MainWnd with other tool windows and dialogs. I want the other tool windows to be able to 'talk' to the main form, and call its methods. But that requires an instance of MainWnd, and since there will only be one of these forms created at any given time there is no reason while I should enumerate through all instances of MainWnd or look for the first one. So I want my main application form MainWnd to be a singleton so other windows can easily call code from it.
Here is the code of the main form that I would like to make a singleton:
using System;
using System.ComponentModel;
using System.Windows.Forms;
namespace MyLittleApp
{
public partial class MainWnd : Form
{
public MainWnd()
{
InitializeComponent();
}
public void SayHello()
{
MessageBox.Show("Hello World!");
// In reality, code that manipulates controls on the form
// would go here. So this method cannot simply be made static.
}
}
}
I am looking to be able to call SayHello() from another form, simply by writing:
MainWnd.SayHello();
How could I accomplish this?
You could probably find a way to make the main window a singleton, however that's not the best way to achieve the outcome you want, nor is it really an appropriate situation in which to use the singleton pattern.
If all of the other tool windows/ dialogs are encapsulated within the main window, then a much better pattern to use for communication would be events.
Have the inner windows/dialogs raise events to represent a 'request' for the main window to do something. Have the main window subscribe to these events, and do the work via the event handlers.
By avoiding the singleton approach, you avoid the difficulties of testing the singleton, as well as avoiding extensive explicit circular references, where not only does the main window have references to the encapsulated windows/dialogs, but they in turn have explicit references back to the main window.
See below.
using System;
using System.ComponentModel;
using System.Windows.Forms;
namespace MyLittleApp
{
public partial class MainWnd : Form
{
public static MainWnd Instance;
public MainWnd()
{
Instance = this;
InitializeComponent();
}
public void SayHello()
{
MessageBox.Show("Hello World!");
// In reality, code that manipulates controls on the form
// would go here. So this method cannot simply be made static.
}
}
}
You can now use it anywhere in your code by calling MainWnd.Instance
All its members are also available to the instance.
You can certainly do this.
public MainWnd Instance = new MainWnd();
Then access as MainWnd.Instance.SayHello().
Replace following calls
MainWind instance = new MainWnd();
To
MainWnd instance = MainWnd.Instance;
I am not sure how Visual Studio designer would react after making the constructor as private though.
But if it does not allow, it will be Visual Studio issue, rather than language/compiler issue.
Related
Background
I'm building a winforms application where I am using an IoC container (SimpleInjector) to register my types. In my application, a majority of the screens (i.e. forms) will only have one instance at any given time.
Problem
For forms that only need one instance at any given time, I can register them as singletons:
container.Register<IHomeView, HomeView>(Lifestyle.Singleton);
This allows me to use the container to keep track of all forms. In this case, however, when a form gets closed it will then get disposed (forms implement IDisposable). If the application tries to open that form again using the container, the container's instance of the form will be disposed, and an exception is thrown.
Question
What is the proper way to deal with this? I currently see two solutions:
For each form, override the form close to instead hide the form, rather than actually close it. I don't really like this idea. I feel like I'd rather close the form each time and start with a new/fresh form.
Register the form with a transient lifestyle rather than as a singleton. In this case, the container really just acts more as a factory. I run into two problems: a) I lose the ability to track forms through the container, and, b) the container throws an exception during verification saying that disposable types should not be registered as transient (which I don't understand why that is). These problems both apply to forms where I will need multiple instances at once as well.
I can get around problem b) by suppressing the diagnostic warning during verification.
registration = container.GetRegistration(typeof(ILoginView)).Registration;
registration.SuppressDiagnosticWarning(
DiagnosticType.DisposableTransientComponent,
"Winforms registration supression.");
What is the correct approach to be taking here? Am I missing something?
Ideally, you would want to register your forms as Singleton. In my experience, however, this will result in hard to debug errors, especially when you use a BindingSource for binding your data to whatever.
A second problem with using Singleton as the lifestyle is that if your application uses modeless windows, this windows will throw an ObjectDisposedException when opened a second time, because the Windows Forms Application framework will dispose the Form on the first close, while Simple Injector should be in charge of that. So Simple Injector will create one–and exactly one–instance, if registered as Singleton. If somebody else (e.g. your application, the windows forms framework) will dispose the object, it won't be recreated.
The most easy solution, which is also easy to understand, is to register your forms as Transient. And yes, you need to suppress the diagnostic warnings. The reason for this diagnostic warning according to the documentation:
A component that implements IDisposable would usually need deterministic clean-up but Simple Injector does not implicitly track and dispose components registered with the transient lifestyle.
Simple Injector is unable to dispose a transient component because it is unable to determine when the object should be disposed. This means, however, that forms that are opened in a modal fashion with a call to .ShowDialog() will never be disposed! And because a windows forms application typically runs for a long time, maybe even a week or month, this will eventually result in a 'Win32Exception' with a message: "Error Creating Window Handle". Which essentially means you exhausted all resources of the computer.
Disposing of the forms is therefore important. And although Simple Injector is able to do this job if you would use a Scope, this is with Windows Forms not so easy to implement. So you yourself have to take care of disposing the closed Forms which have been shown using ShowDialog().
Depending on your specific use case there are several ways to implement a FormOpener or NavigationService. One way to do it:
public interface IFormOpener
{
void ShowModelessForm<TForm>() where TForm : Form;
DialogResult ShowModalForm<TForm>() where TForm : Form;
}
public class FormOpener : IFormOpener
{
private readonly Container container;
private readonly Dictionary<Type, Form> openedForms;
public FormOpener(Container container)
{
this.container = container;
this.openedForms = new Dictionary<Type, Form>();
}
public void ShowModelessForm<TForm>() where TForm : Form
{
Form form;
if (this.openedForms.ContainsKey(typeof(TForm)))
{
// a form can be held open in the background, somewhat like
// singleton behavior, and reopened/reshown this way
// when a form is 'closed' using form.Hide()
form = this.openedForms[typeof(TForm)];
}
else
{
form = this.GetForm<TForm>();
this.openedForms.Add(form.GetType(), form);
// the form will be closed and disposed when form.Closed is called
// Remove it from the cached instances so it can be recreated
form.Closed += (s, e) => this.openedForms.Remove(form.GetType());
}
form.Show();
}
public DialogResult ShowModalForm<TForm>() where TForm : Form
{
using (var form = this.GetForm<TForm>())
{
return form.ShowDialog();
}
}
private Form GetForm<TForm>() where TForm : Form
{
return this.container.GetInstance<TForm>();
}
}
This class must be registered as Singleton:
container.RegisterSingleton<IFormOpener, FormOpener>();
And can be used by injecting this service in for example your root form of the application:
public partial class RootForm : Form
{
private readonly IFormOpener formOpener;
public RootForm(IFormOpener formOpener)
{
this.formOpener = formOpener;
this.InitializeComponent();
}
private void ShowCustomers_Click(object sender, EventArgs e)
{
this.formOpener.ShowModelessForm<AllCustomersForm>();
}
private void EditCustomer_Click(object sender, EventArgs e)
{
var result = this.formOpener.ShowModalForm<EditCustomerForm>();
// do something with result
}
}
I am using several services that are registered inside my IoC container. There is an interface IFormService to make forms accessible from within my main application. The interface is defined by
public interface IFormService
{
System.Windows.Forms.Form Form { get; }
}
and for example implemented by a class MainFormService which returns the main form instance.
container.Register(
Component.For<IFormService>()
.ImplementedBy<MainFormService>()
.Named("Application.Forms.MainForm")
);
After application initialization, I am using this service to retrieve the form and let the application run:
Form mainForm = this.ServiceContainer
.Resolve<IFormService>("Application.Forms.MainForm")
.Form;
Application.Run(mainForm);
This works perfectly. The only issue is that the controls on the form look sort of 95-ish:
Later in my development I want to switch to using DevExpress skins which seems to produce the same issue. Why is that happening and how could I solve this properly?
Thanks in advance.
A little new to C#, and approaching something beyond me. Apologies for length.
I have a Windows Form application in Visual Studio C# Express, using the default classes VS spawns. I want to start and stop a Marquee style progressBar from a class other than the default Form1 in which it is declared.
These seems surprisingly difficult, I am sure I am missing something important.
My project has the usual classes that Visual Studio auto generates:
Form1.cs, Form1.Designer.cs , Program.cs .
I added myClass.cs that wants to talk the load bar.
I add progressBar1 bar to my form using the designer, setting Style:Marquee.
In Form1.cs' Form() constructor, I write
this.progressBar1.Visible = false;
This works. Intellisense 'sees' progresBar1.
code in Form1.cs can see and control progressBar1 declared in Form1.Designer.cs.
this makes sense to me.
But the functions which need to start and stop the load bar must live in myClass.cs.
I want to be able to code like this, within myClass.cs:
public void myFunction(){
Form1.progressBar1.visible=true
//do stuff that takes a bit of time
Form1.progressBar1.visible=false
}
This does not work. Intellisense cannot 'see' progresBar1 when typing code in myClass.cs.
In fact, intellisense cannot 'see' anything in Form1.cs from within myClass.cs.
No public propeties or functions added to Form1 ever become visible to intellisense.
This does not make sense to me, I am confused.
This seems like something you would want to do often and easily.
Some searching indicates that this blocking of external access to Form controls is by design. Something to do with 'decoupling' your logic code from GUI code, which makes sense in principal.So clearly there is an expected approach, yet an clear example is hard to find. I can only find examples of loadbars controlled from entirely within the Forms that declare them, or terse half-examples about creating and registering Events or using Invoke or other things I know too little about. There are many apparent solutions but none that I can see clearly apply to me, or that I am able to implement, in my ignorance.
I think I could do it if my Form were an instance.
[EDIT] nope. instance or not, Form1 controls never become exposed outside of Form1.cs
So, How do I to start and stop a Marquee style progressBar from a class other than the default Form1 in which it is declared, in the proper way?
Is there a clear and useful example somewhere?
You can't access your properties this way:
Form1.progressBar1
because Form1 is a type (not an instantiated object). The only methods or properties you can access with this approach have to be marked as static.
To answer your question of how to communicate, you probably want to use the event approach that you mentioned. First you need an event in your logic class:
public event Action<int> UpdateProgress;
Which is called just like a function:
if (UpdateProgress != null)
UpdateProgress(10);
This declares a new event using the Action generic delegate, which means the listening function has to return void and take one int as a parameter.
Then in your forms code, you'll have:
MyClass logic = new MyClass();
private void SomeFunction
{
logic.UpdateProgress += UpdateProgressBar;
}
private void UpdateProgressBar(int newProgress)
{
progressBar1.BeginInvoke(new Action(() =>
{
progressBar1.Value = newProgress;
}));
}
This creates a new instance of your logic class, and assigns the function "UpdateProgressBar" to be called whenever your logic class raises the UpdateProgressBar event. The function itself uses Dispatcher.BeginInvoke because your logic class is likely not running on the UI thread, and you can only do UI tasks from that thread.
There is a lot going on here, so please let me know if I can clarify anything for you!
I would create a model that has properties matching your form, and pass that around.
So you would make a new class like this...
using Windows.Forms;
public class Form1Model {
public ProgressBar progressBar { get; set; }
}
Then when you want to get to your other class holding that function you would create an instance of Form1Model, fill it, and call your function
var fm = new Form1Model {
progressBar = this.progressBar1;
};
otherClass.MyFunction(fm);
now you would have to change your function to accept the new model
public void MyFunction(Form1Model fm){
// do stuff
}
Another option is just making the function take an instance of the form, and not creating a model, but then you are going to be passing a lot of extra bits you probably won't care about
public void MyFunction(Form1 form){
// do stuff
}
Then on your form you would call the function like this
otherClass.myFunction(this);
I would recommend the first way over the second, you can control what data is being passed around
You are trying to access the type Form1 instead of the forms instance. I'll show you, how you can access the instance below.
I assume that Form1 is the applications main form that stays open as long as the application runs. When you create a WinForms application VS creates this code in Program.cs:
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
}
}
A simple way to make your main form accessible throughout the application is to make it accessible via a public static property. Change the code like this
static class Program
{
public static Form1 MainForm { get; private set; }
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
MainForm = new Form1();
Application.Run(MainForm);
}
}
In Form1 create a property that exposes the progress bar's visibility:
public bool IsProgressBarVisible
{
get { return this.progressBar1.Visible; }
set { this.progressBar1.Visible = value; }
}
Now you can make the progress bar visible from any part of the program like this:
Program.MainForm.IsProgressBarVisible = true;
Another way of accessing the main form is, since it is always opened as the first form:
((Form1)Application.OpenForms(0)).IsProgressBarVisible = true;
However, it requires the form to be casted to the right type, since OpenForms returns a Form.
And don't forget: A Form is just a class like any other class. You can do almost everything you can make with other classes. So, communicating with forms is not very different than communication with other objects, as long as you are not using multithreading.
I have a method which is invoked in class A and it is defined in class B:
class B{
[STAThread]
public static void ScanForAxisCameras() {
DNSSDService service = new DNSSDService();
DNSSDEventManager eventManager = new DNSSDEventManager();
eventManager.ServiceFound += new _IDNSSDEvents_ServiceFoundEventHandler(eventManager_ServiceFound);
DNSSDService browse = service.Browse(0, 0, "_axis-video._tcp", null, eventManager);
Application.Run();//if not invoked everything above does not start
}
}
class A{ ...before invoking..... B.ScanForAxisCameras(); ....after invoking....}
The code in class B "starts"/works only if I invoke Application.Run(). But it causes that all the code in class A ....after invoking.... method does not work. How to handle it so it will not freeze the application?
Edit: the class A is class MainWindow.xaml.cs. It is WPF application.
public partial class MainWindow : Window {
public MainWindow() {
InitializeComponent();
}
private void Window_Loaded(object sender, RoutedEventArgs e) {
createGUI();
}
private void createGUI() {
LocalNetworkScanner.ScanForAxisCameras();//when there is no Application.Run() ScanForAxisCameras() does not work.
}
}
The WPF UI thread on which you call ScanForAxisCameras() already has a message loop. I believe the problem with your code is that all objects you create inside ScanForAxisCameras have the local scope:
public static void ScanForAxisCameras() {
DNSSDService service = new DNSSDService();
DNSSDEventManager eventManager = new DNSSDEventManager();
eventManager.ServiceFound += new _IDNSSDEvents_ServiceFoundEventHandler(eventManager_ServiceFound);
DNSSDService browse = service.Browse(0, 0, "_axis-video._tcp", null, eventManager);
Application.Run();//if not invoked everything above does not start
}
Without Application.Run(), your objects (service, eventManager, browse) may be getting destroyed and finalized as soon as ScanForAxisCameras finishes. So, the events you're looking for (like ServiceFound) may not even have a chance to get fired.
If you call Application.Run(), then ScanForAxisCameras doesn't exit (at least not until Application.Run() itself exits). That keeps your objects alive and functional.
Try refactoring your code to keep the references to these objects in member fields of your class (or in static variables, FWIW). I believe that should fix the problem.
[EDITED] On a side note, the [STAThread] attribute doesn't make sense in that context (unless you use ScanForAxisCameras as an entry point for a new thread - apparently, that's not the case here).
You can introduce a new Run()-method in your App in the App.xaml.cs file.
Here you can perform custom actions before the application itself gets initialized.
Further information here.
public partial class App : Application
{
public new void Run()
{
// Do your stuff here
B.DoStuff();
// Call the base method
base.Run();
}
}
Application.Run starts the message loop for that particular thread, if there is no message loop then there is no notification for your objects to know they have to do something.
The code in class B "starts"/works only if I invoke Application.Run(). But it causes that all the code in class A ....after invoking.... method does not work.
Run is a blocking call therefore any code after that call is not reachable until the application is closing down i.e. when you exit the message loop.
How to handle it so it will not freeze the application?
In short, you can't. Run will always block so any code you need to run as part of your application startup will have to happen before the call.
After your edit to mention that this is a WPF application then Application.Run as a static method is not the right way to go here. If you need to run initialization when your application starts then you can do what has already been suggested and override the Run method of the Application class, or alternatively (maybe more appropriately) you can hook into the OnStartup event e.g.
public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
// code before startup
base.OnStartup(e);
// code after startup
}
}
It seems as though in this situation you would need to add Application.Run() in a different class. Run() accepts nothing, ApplicationContext, Form. This controls the lifetime of the application and should be called before class A, unless class A is the entry point.
See http://msdn.microsoft.com/en-us/library/ms157900.aspx for details.
A Windows Forms application starts when the Main method is called. You can implement initialization procedures on the Main function. However, to initialize a Windows Forms application fully and start it routing Windows Forms events, you need to invoke Application.Run.
you can read about Application here
How to make it so if one copy of a program is running another won't be able to open?
Or better yet, how to make it so that if one copy is already running, then trying to run another copy will just act as if you maximized the original process?
Scott Hanselman wrote a post on doing this sort of thing
This article
True Single instance application - WinForms.NET
explains how to create a true single instance:
This article simply explains how you
can create a windows application with
control on the number of its instances
or run only single instance. This is
very typical need of a business
application. There are already lots of
other possible solutions to control
this.
e.g. Checking the process list with
the name of our application. But this
methods don't seems to be a good
approach to follow as everything is
decided just on the basis on the
application name which may or may not
be unique all across.
using System;
using Microsoft.VisualBasic.ApplicationServices;
namespace Owf
{
public class SingleInstanceController
: WindowsFormsApplicationBase
{
public SingleInstanceController()
{
// Set whether the application is single instance
this.IsSingleInstance = true;
this.StartupNextInstance += new
StartupNextInstanceEventHandler(this_StartupNextInstance);
}
void this_StartupNextInstance(object sender,
StartupNextInstanceEventArgs e)
{
// Here you get the control when any other instance is
// invoked apart from the first one.
// You have args here in e.CommandLine.
// You custom code which should be run on other instances
}
protected override void OnCreateMainForm()
{
// Instantiate your main application form
this.MainForm = new Form1();
}
}
}
Change you main function this way:
[STAThread]
static void Main()
{
string[] args = Environment.GetCommand
SingleInstanceController controller = new SingleInstanceController();
controller.Run(args);
}
Your best option is to use a named mutex. These articles explain the design pretty well and provide all the necessary code:
http://sanity-free.org/143/csharp_dotnet_single_instance_application.html
http://iridescence.no/post/CreatingaSingleInstanceApplicationinC.aspx
Extending this to maximise the main window of the running application should be a simple alteration to either of the examples provided.
You can use Mutex to make your app singleton. There are plenty of examples how to do it.
The Microsoft.VisualBasic.dll assembly contains a class 'WinformsFormsApplicationBase' which contains some functionality like the thing you want.
You can use this class in a C# application as well.
Just create a class which inherits from this class.
Set the SingleInstance property to true and override the necessary methods.
Offcourse, this means that you have a reference to the VisualBasic.dll assembly, which could be seen as a disadvantage, but, I think it is by far the most simple and easiest solution.
More info can be found here