2

I have multiple instances of forms within my windows form application that I instantiate upon loading the main form (including an instance of the main form itself). I would like them to be shared between classes so I can use them from any location. I also need to use the members of each class through these instances.

Please note that I am very confused as to how this works. I don't know how I should be declaring instances of my forms but I only want one instance of each so that I can use the .Hide() method and also use each form's/class's members.

I've tried making the instances as properties but I am not sure how to proceed. I am not sure if that is correct.

    // get and set for form instances
    public Menu menu { get; set; }
    public RandomFacts randomFacts { get; set; }
    public QuizMenu qm { get; set; }
    public AskHowManyQuestions ahmq { get; set; }

    // in the main form load method
    menu = new Menu();
    randomFacts = new RandomFacts();
    qm = new QuizMenu();
    ahmq = new AskHowManyQuestions();

That code is all within the same 'main' form.

I hope that you can help me with being able to access these instances globally and help me solve this problem. Thank you for reading about my issue.

itzgeez
  • 39
  • 8
  • Check this: https://stackoverflow.com/a/53658779/5718868 – Jonathan Applebaum Feb 09 '19 at 22:19
  • 1
    It should be noted that the pattern of using Singleton is discouraged in favor of explicit dependency injection in constructors. – theMayer Feb 09 '19 at 22:30
  • Agree w/ @theMayer only with the caveat that the two things aren't mutually exclusive - you should still use singleton instances in cases like this, you should just not have your dependent code control the lifetime of its dependencies. That's a job for the composition root. – Preston Guillot Feb 10 '19 at 17:11
  • Possible duplicate of [C# Let multiple class share the same instance of another class](https://stackoverflow.com/questions/36235240/c-sharp-let-multiple-class-share-the-same-instance-of-another-class) – Pvria Ansari Feb 23 '19 at 12:00

3 Answers3

4

Trivial example:

using System;
using System.Collections.Generic;
using System.Linq;

public class Program
{

    public static void Main()
    {   

        var NewClassInstance = Singleton.GetSingleton().NewClassInstance;
        Singleton.GetSingleton().NewClassInstance.Method();
        var OtherClassInstance = Singleton.GetSingleton().OtherClassInstance;
        var Proparty  = OtherClassInstance.Name;
    }
}



public class Singleton
{
    public NewClass NewClassInstance {get; private set;}
    public OtherClass OtherClassInstance {get; private set;}


    private static readonly NewClass _newClass = new NewClass();
    private static readonly OtherClass _otherClass = new OtherClass();
    private static readonly Singleton _singleton = new Singleton();
    private Singleton()
    {
        NewClassInstance = _newClass;
        OtherClassInstance = _otherClass;
        // Prevent outside instantiation
    }

    public static Singleton GetSingleton()
    {
        return _singleton;
    }
}

public class NewClass
{
    public NewClass()
    {
    }

    public void Method()
    {
    }
}


public class OtherClass
{
    public string Name {get; set;}

    public OtherClass()
    {
    }
}

You can access this intance doing

Singleton.GetSingleton();

Let leave you an other exemple https://dotnetfiddle.net/C1PB05

Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
Amadeu Antunes
  • 678
  • 1
  • 8
  • 28
  • So do I just do this for each form in my case? Thanks for the explanation. – itzgeez Feb 09 '19 at 21:59
  • This way you can instance each class only one time and acess it any time without create a new instance in your cause you can do it for any class you would like to share – Amadeu Antunes Feb 09 '19 at 22:01
  • Thank you. So in my case, do I change each public form constructor to a private one? And just set the properties below the call to InitializeComponent(); , repeating this process for each class? Sorry I am just confused. @AmadeuAntunes – itzgeez Feb 09 '19 at 22:32
  • The singleton pattern is cute, but I'm not really convinced that this is the right place for it. Maybe... but smells fishy. I'd recommend the approach where the instances are passed back and forth through constructors. – Vilx- Feb 09 '19 at 22:34
  • You Class singleton has to have access to others classes to can instanciate it – Amadeu Antunes Feb 09 '19 at 22:35
  • @Vilx- Its a simple way to share instances – Amadeu Antunes Feb 09 '19 at 22:36
  • 1
    @AmadeuAntunes Yes, but it also limits you to one instance per form. If one day you want to instantiate two copies of the same form (for whatever reason), things will get ugly. – Vilx- Feb 09 '19 at 22:37
  • @you can always duplicate instances in class singleton – Amadeu Antunes Feb 09 '19 at 22:38
  • Singletons are usully used for things that are singular by their very nature. Something where it's absolutely inconceivable that you'll ever need another instance ever. In fact, 90% of such cases are even better solved with static classes/properties. Singletons are for the case where you absolutely *need* it to be an instance, yet you also *need* to have just one instance ever. Like `DBNull`, that's a good example. – Vilx- Feb 09 '19 at 22:41
  • In this case, we do need it to be an instance (forms can't be static), but there is no real need to limit us to just one instance. We merely don't need another instance right now, so a singleton works, but it doesn't feel like the right solution. – Vilx- Feb 09 '19 at 22:43
  • Forgive me but I am still unsure. Do I need to create a new class to serve as the 'Singleton'? – itzgeez Feb 09 '19 at 22:47
  • 1
    In this exemple yes – Amadeu Antunes Feb 09 '19 at 22:48
  • So whenever I need to use the single instance, no matter where I am, I just type "var NewClassInstance = Singleton.GetSingleton().NewClassInstance;"? – itzgeez Feb 09 '19 at 23:08
  • yes, you can call anywhere the sigleton to get the others instances – Amadeu Antunes Feb 09 '19 at 23:11
  • Now I am getting: System.TypeInitializationException: 'The type initializer for 'wQuiz.Single' threw an exception.'. This is when I try to do the code: var MenuForm = Single.GetSingle().MenuForm; – itzgeez Feb 09 '19 at 23:53
2

Just assign them to properties on your forms

// in the main form load method

// Class which holds all of the state shared by these forms
sharedState = new SharedState();

menu = new Menu() { SharedState = sharedState };
randomFacts = new RandomFacts() { SharedState = sharedState };
ahmq = new AskHowManyQuestions() { SharedState = sharedState };
qm = new QuizMenu() { SharedState = sharedState };

Obviously, your forms need to declare a SharedState property:

public SharedState SharedState { get; set; }
canton7
  • 37,633
  • 3
  • 64
  • 77
  • One thing of note, if you don't keep a parameterless constructor for the form in addition to the constructor that takes in the SharedState the visual designer for the form may not work. – Scott Chamberlain Feb 09 '19 at 22:24
  • @ScottChamberlain fair point - edited to use properties instead, which will be easier grok – canton7 Feb 09 '19 at 22:27
  • @ScottChamberlain I was afraid of that too, but oddly enough I've never had any problems with it. I've made forms with a single private constructor and forms with a single parametrized constructor and the designer hasn't complained once. I suspect it doesn't actually instantiate them, it just parses the `InitComponents` method. – Vilx- Feb 09 '19 at 22:48
  • @Vilx- I may have been thinking of custom controls instead of a form. I believe if you are making a custom control and do it when you try to add it in the designer on a form it messes up. – Scott Chamberlain Feb 09 '19 at 22:51
  • @ScottChamberlain - Now that you mention it... maybe it was me who did custom controls and it worked? It was long ago, and I don't really remember anymore... Anyways, I guess the moral is - it _might_ fail, see for yourself if it works in your case. – Vilx- Feb 10 '19 at 20:39
2

I only want one instance of each

Move your declarations to their own Class and implement the Singleton Pattern. Since you are dealing with Forms, you can instantiate them on demand, and set them back to null when they are closed by subscribing to their FormClosed event.

Example usage:

MyForms.menu.Show();

Code:

class MyForms
{

    private static Menu _menu = null;
    public static Menu menu {
        get {
            if (_menu == null || _menu.IsDisposed)
            {
                _menu = new Menu();
                _menu.FormClosed += (sender, e) => { _menu = null; };
            }
            return _menu;
        }
    }

    private static RandomFacts _randomFacts = null;
    public static Menu randomFacts
    {
        get
        {
            if (_randomFacts == null || _randomFacts.IsDisposed)
            {
                _randomFacts = new RandomFacts();
                _randomFacts.FormClosed += (sender, e) => { _randomFacts = null; };
            }
            return _menu;
        }
    }

    private static QuizMenu _qm = null;
    public static QuizMenu qm
    {
        get
        {
            if (_qm == null || _qm.IsDisposed)
            {
                _qm = new QuizMenu();
                _qm.FormClosed += (sender, e) => { _qm = null; };
            }
            return _qm;
        }
    }

    private static AskHowManyQuestions _ahmq = null;
    public static AskHowManyQuestions ahmq
    {
        get
        {
            if (_ahmq == null || _ahmq.IsDisposed)
            {
                _ahmq = new AskHowManyQuestions();
                _ahmq.FormClosed += (sender, e) => { _ahmq = null; };
            }
            return _ahmq;
        }
    }

}
Idle_Mind
  • 38,363
  • 3
  • 29
  • 40