Program with central class calling other classes?

Rhodan

Well-known member
Joined
Apr 7, 2015
Messages
52
Programming Experience
10+
I'm familiar with amateur programming so I know about OOP and stuff (to a certain degree) but I'm not really liking having instances of everything and no global variables (I started using basic in the 1970s so I am biased). I've not done any real programming for probably 10 years but now I am on a mission to make a simple Point of Sale system, I was wanting to use MySQL but vs2022 and vs 2019 don't seem to work with MySQL any more. I tried MS's localDB but can't quite figure out how to create and connect to the file inside c# so I settled on SQLite which works no problem at all (I just want a local DB file, don't need a server etc as this POS is for one machine only at home.

Before I really get started I wanted to know, can I use a single C# class as my main program and have it display the different forms? The forms would have thier own purposes which the main program knows nothing about but they would set variables IN the main program (my way around no globals). This way I get what seems like a sane structure to me and I know where to look for everything.

I am guessing I would make the main class my startup object and make it a static class?

Or am I just rambling gibberish?
 
Before I really get started I wanted to know, can I use a single C# class as my main program and have it display the different forms?

Yes, but you would have to hand write your forms. If you use the Visual Studio WinForms Designer, it will create a new class for each form and you said that you didn't want to create multiple instances of classes. To use those forms, you would have to create instances of each form.

So this is not great programming practice, but as a crutch for making the transition from BASIC with everything global, to eventually following the encapsulation principle that is a tenet of object oriented programming, you could pass around an object that has all the "globals" that you want to keep track of. That object could be your main program itself.

In pseudo-code and bad OOP:
C#:
class CustomForm : Form
{
    public MainProgram _main;

    :
    void btnAccelerate_Click(...)
    {
        _main.Speed += 10;                // touch the "global" Speed
    }
    :
}

class MainProgram
{
    public int Speed = 0;                // the "global" Speed

    static void Main()
    {
        var customForm = new CustomForm();
        customForm._main = this;                         // tell the CustomForm about us
        Application.Run(customForm);
    }
}

If you don't want to pass around the object tha hold the "globals", you can also use the singleton anti-pattern:
C#:
static class Global
{
    public static int Speed = 0;
}

class CustomForm : Form
{
    :
    void btnAccelerate_Click(...)
    {
        Global.Speed += 10;                // touch the "global" Speed
    }
    :
}

class MainProgram
{
    static void Main()
    {
        var customForm = new CustomForm();
        Application.Run(customForm);
    }
}

All off the above are bad practices that you should wean yourself away from, but if you are desperate for something now, those should get you rolling.
 
Last edited:
Yes, but you would have to hand write your forms. If you use the Visual Studio WinForms Designer, it will create a new class for each form and you said that you didn't want to create multiple instances of classes. To use those forms, you would have to create instances of each form.
<SNIP>
I've done a lot of thinking and figured out that I can use a single form as the controlling code (always present, doesn't close). If I do that I can set public variables there and just refer to them from other forms - almost like a global variable even though they aren't. I can have each child form perform their functions but in the end set the variables in the main form for the rest of the program to use.

I am assuming I just need to make the variable public (not static) since there are no other instances of it and never will be.
 
You don't need to make the variable public if all the code that touches it lives within that single form. It's when you launch other forms and they need to update the variable in your main form when you need to make it public and pass on an instance of your form to the other forms. (Alternatively, those other forms can assume that their Parent is your main form, and they can cast that Parent reference to the type of your main form. Again this is a bad practice because you are tightly coupling the other forms to your main form.)
 
This way I get what seems like a sane structure to me and I know where to look for everything.

In your house, do you have one massive refrigerator in the kitchen where you keep everything? When you buy breakfast cereals you put them in the fridge? and your milk? Do you keep the kettle in it too? And your hairbrush? iPad? Set of wrenches? The cat?

I'm just imagining it this way because usually people form mental models in programming like they do in life and if you like to keep literally everything in one place "so you know where it is", then you must have something like this?

It's more likely that you don't do this and instead keep things in a relevant place; the milk would be in the fridge, the cereals have their own cupboard, the kettle close by as it's typically a part of the breakfast routine, the wrenches in the garage and the iPad and cat roam around. Some things are shared, some things stay within their areas.

Having everything as global state may feel comfortable for what you did 40 years ago in programming but there's a reason we've moved away from it..
You don't have to go full on, but walking a little way up the learning curve will save you a lot of headache later on. Unless you plan on e.g. only ever having one item per sale you'll definitely need more than one instance of *something* at some point in your software's life: when a customer buys two items, that's one Order with two Product instances on it..
 
Last edited:
From my own experience and seeing others, I think that it takes at least 2 "incidents" where someone was using globals (or singletons) and then needing to spend hours or days of debugging trying to find out why a value changed unexpectedly, or trying to find all the places and scenarios where or when a value could potentially change. These "incidents" tended to also coincide to when the programmer is making the transition from imperative /procedural programming to event driven or multithreaded programming.

At the end of the the first incident, there is usually a lot of relief and happiness around having found the bug and fixing it, but there is no real architectural or programming discipline changes made. At the end of the second incident, it is usually the feeling of exhaustion and the thoughts of "OMG! Are there other similar issues are lurking in the code?" It's after this second time around that typically leads to looking at writing code with better encapsulation and loose coupling.
 
You can setup property in the main form then in each child form, setup an event which when created in the main form listens for changes and in this case adds or subtracts.

Main form and Baggage model:
public partial class MainForm : Form
{
    // could come a database or another source
    private int SomeValue { get; set; } = 1;
    public MainForm()
    {
        InitializeComponent();
        CurrentValueLabel.Text = $"Current value: {SomeValue}";
    }

    private void ShowChildForm1_Click(object sender, EventArgs e)
    {
        using ChildForm form = new(SomeValue);
        form.SendBaggage += Form_SendBaggage;
        form.ShowDialog();
    }

    private void Form_SendBaggage(Baggage sender)
    {
        SomeValue = sender.Value;
        CurrentValueLabel.Text = $"Current value: {SomeValue}";
        OperationTypeLabel.Text = sender.Add ? "Added" : "Subtracted";
    }
}

public class Baggage
{
    public bool Add { get; set; }
    public int Value { get; set; }
}

A child form:
public partial class ChildForm : Form
{
    public delegate void OnBaggage(Baggage sender);
    public event OnBaggage SendBaggage;
    public int Value { get; set; }
    public ChildForm()
    {
        InitializeComponent();
    }
    public ChildForm(int value)
    {
        InitializeComponent();

        numericUpDown1.Minimum = 1;
        Value = value;
        CurrentValueLabel.Text = $"Current value: {Value}";
    }
    private void ExecuteButton_Click(object sender, EventArgs e)
    {
        // consider validation e.g. if value is less than 1
        Baggage baggage = new()
        {
            Add = AddCheckBox.Checked,
            Value = AddCheckBox.Checked ? Value += (int)numericUpDown1.Value : Value -= (int)numericUpDown1.Value
        };

        SendBaggage?.Invoke(baggage);
        DialogResult = DialogResult.OK;

    }

    private void CancelButton_Click(object sender, EventArgs e)
    {
        DialogResult = DialogResult.Cancel;
    }
}
 
public delegate void OnBaggage(Baggage sender);
public event OnBaggage SendBaggage;

Events and their related delegates should use names that are based on a verb, and it is also wise to include a tense to indicate if the event is about to occur or has recently occured eg FormClosing / FormClosed
 
Back
Top Bottom