Reusing a method between classes - c#

A simple/beginner question on code resuse in a basic OOP program recording the sale of motor vehicle tyres.
The tyres are stored in an array:
public Tyre[] tyres = new Tyre[5];
I have two forms.
Form1
End user simply uses a combo/lookup of current stock items (tyres) to select item.
public partial class Form1 : Form
{
Fitting fitting;
public Tyre[] tyres = new Tyre[5];
Tyre currentTyre;
public Form1()
{
InitializeComponent();
//populate array with tyres
tyres[0] = new Tyre("155/80S13", 56.00m, 10);
tyres[1] = new Tyre("165/70P15", 42.00m, 10);
tyres[2] = new Tyre("195/70S13", 46.00m, 10);
tyres[3] = new Tyre("158/90S19", 70.00m, 10);
tyres[4] = new Tyre("185/66R13", 66.00m, 10);
}
// search through array to find current selected tyre
public Tyre findTyre(string tyretype)
{
for (int i = 0; i < tyres.Length; i++)
{
if (tyretype == tyres[i].Type)
return tyres[i];
}
return null;
}
private void cmbTyreType_SelectedIndexChanged(object sender, EventArgs e)
{
currentTyre = findTyre(cmbTyreType.Text);
lblPrice.Text = currentTyre.Price.ToString();
lblStockQ.Text = currentTyre.StockQty.ToString();
}
The findTyre() method is defined as:
public Tyre findTyre(string tyretype)
{
for (int i = 0; i < tyres.Length; i++)
{
if (tyretype == tyres[i].Type)
return tyres[i];
}
return null;
}
Form 2 (AddStock) On this form, the end user currently uses a similar combo/lookup again to view the range of tyres (just like on Form1)
public partial class AddStock : Form
{
Form1 frm2;
Tyre currentTyre;
public AddStock(Form1 frm)
{
frm2 = frm;
InitializeComponent();
for (int i = 0; i< frm.tyres.Length ; i++)
{
cmbTyreType.Items.Add(frm.tyres[i].Type);
}
}
public Tyre findTyre(string tyretype )
{
for (int i = 0; i < frm2.tyres.Length; i++)
{
if (tyretype == frm2.tyres[i].Type)
return frm2.tyres[i];
}
return null;
}
private void cmbTyreType_SelectedIndexChanged(object sender, EventArgs e)
{
currentTyre = findTyre(cmbTyreType.Text);
lblCurrentQ.Text = currentTyre.StockQty.ToString();
}
My concern is that I've had to re-define findTyre() again, eventhough it was already defined in Form1. My hope was (perhaps ill-informed) that i could re-use the findTyre() method from Form1, but Visual Studio is preventing me.
Could the reason be that the findTyre() method is bound to instances which render it inaccessible outside the class?

Create a class that manages your tyres:
public class Tyres
{
private Tyre[] tyres = new Tyre[5];
public Tyre this[int i]
{
get { return tyres[i]; }
set { tyres[i] = value; }
}
public Tyre findTyre(string tyretype )
{
for (int i = 0; i < frm2.tyres.Length; i++)
{
if (tyretype == frm2.tyres[i].Type)
return frm2.tyres[i];
}
return null;
}
}
Instead of passing the array between forms, pass an instance of this class. This class can also hold even more specific methods, which do operations on your tyres. This is one fundamental of OOP: keeping the data and the methods acting on that data togehter (in a class). This also helps you separating your logic from your GUI. Imagine you would like to change your winforms application to a console or a web application. In that case, you can reuse the tyres class. Searching for a tyre has nothing to do with a GUI, that's why it doesn't belong into a form class.

Why is the findTyre method inside the forms class and not inside the Tyres class?
In your case i would just move the method inside the Tyre object class make it static and add a new parameter to the method.
It is never good to have duplicate methods inside your program. Reason being if that method changes functionaly you will have to change it everywhere in your whole program instead of one. Also always try to bind you object specific code to your object class. So you know if you need a method that is linked to tyres you just need to look into the tyres class and not go through all your classes trying to find it.
I would recommend you also read the following article, it is an explanation to what separation of concerns is: https://www.castsoftware.com/blog/how-to-implement-design-pattern-separation-of-concerns
public static Tyre findTyre(string tyretype, Tyres[] Tyres )
for (int i = 0; i < tyres.Length; i++)
{
if (tyretype == tyres[i].Type)
return tyres[i];
}
return null;
}
I would also recommend you implementering the code #SomeBody provided in the answer below. Will make your code cleaner and more sustainable.

In AddStock can't you just use the findTyre method from Form1?
currentTyre = frm2.findTyre(cmbTyreType.Text);

Seems you can just call findTyre from Form1
public Tyre findTyre(string tyretype)
=> frm2.findTyre(tyretype)

Related

Not scoping static vars properly in C#

I'm new to C# - this is nearly my first program. I'm trying to create some public static variables and constants to use anywhere in the program. The - wrong - way I have tried is to declare them in a separate class in the same namespace but they are out of context for the main program. It's a WPF application. The code looks like this:
namespace testXyz
{
class PublicVars
{
public const int BuffOneLength = 10000;
public static int[] Buff1 = new int[BuffOneLength];
public const int BuffTwoLength = 2500;
public static int[] Buff2 = new int[BuffTwoLength];
private void fillBuff1()
{
Buff1[0] = 8;
Buff1[1] = 3;
//etc
}
private void fillBuff2()
{
Buff2[0] = 5;
Buff2[1] = 7;
//etc
}
}
}
Second file:
namespace testXyz
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
public static int isInContext = 0;
int jjj = 0, mmm = 0;
private void doSomething()
{
isInContext = 5; // this compiles
if (jjj < BuffOneLength) // "the name 'BuffOneLength' does not exist in the current context"
{
mmm = Buff2[0]; // "the name 'Buff2' does not exist in the current context"
}
}
}
}
My actual program is much longer of course. I created the above WPF application exactly as shown to test this problem and I got these errors, also occurring in the real program. I really don't want to fill the arrays in the main program as they are very long and it would mean much scrolling. I also want to have one place where I can declare certain public static variables. What is the right way to do this?
You have to either specify class:
// BuffOneLength from PublicVars class
if (jjj < PublicVars.BuffOneLength) {
...
// Buff2 from PublicVars class
mmm = PublicVars.Buff2[0];
or put using static:
// When class is not specified, try PublicVars class
using static testXyz.PublicVars;
namespace testXyz {
public partial class MainWindow : Window {
...
// BuffOneLength - class is not specified, PublicVars will be tried
if (jjj < BuffOneLength) {
mmm = Buff2[0];
You can't access a static variable that is in another class by just calling the variable. You need to first go thru the class that contains it in your case it would be
PublicVars.BuffOneLength
and
PublicVars.Buff2[0]

How can I parse data between installed WinForm projects?

I have created an installer-project, that installs multiple projects, that I have created. There is a main-window that opens other programs on the click on a button. I want to parse data between the mainWindow and the program to open (string value), when the user clicks one of the buttons.
I use processes to start the programs the installer has installed to the application folder.
Process OpenProject1 = Process.Start(".\\" + "Project1.exe", "StringToParseHere");
How can I do this?
Thanks in advance:)
I want to parse data between the mainWindow and the program to open (string value)
For Project1.exe to read in the "StringToParseHere" when it starts, add code to the Main Event:
using System;
class Program
{
static void Main(string[] args)
{
if (args != null)
{
for (int i = 0; i < args.Length; i++) // Loop through array or command line parameters
{
string argument = args[i];
MessageBox.Show(argument);
}
}
}
}
If you need the argument value to go into say Form1, then make an overloaded class constructor and save to a private member variable, eg:
private string argumentParsedIn = string.empty; //This is the member variable
//base class/form constuctor
Public Form1()
{
}
//Overloaded class/form constructor that takes a parameter
Public Form1(string argument)
{
argumentParsedIn = argument;
}
One thing to be aware of is that the base class constructor for a WinForm Form has the InitializeComponent(); method. So your overload constructor should call that method, a design pattern to do this is, eg:
Solution
Program.cs
static class Program
{
[STAThread]
static void Main()
{
if (args != null)
{
for (int i = 0; i < args.Length; i++) // Loop through array or command line parameters
{
string argument = args[i];
//MessageBox.Show(argument);
}
}
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
var application = new WindowsFormsApplication();
application.Run(new Form1(argument)); //<-- see here is how I pass it
}
}
Form1.cs
private string argumentParsedIn = string.empty; //This is the member variable
Public Form1() : System.Windows.Forms.Form
{
InitializeComponent();
}
Public Form1(string argument) : base() //<-- see here, adding the base will call the base constructor
{
argumentParsedIn = argument;
}

Pass Values from Form -> Class -> Form C#

I have 2 Forms: V.Batch and V.BatchEdit and a Class: M.Batch
In V.Batch there is a DataGrid. I want to pass the value I get from the DataGrid to V.BatchEdit and the get set method is in M.Batch.
The problem here is that the value isn't passed properly in V.BatchEdit. It returns 0.
Here is the code
V.Batch:
M.Batch bt;
public Batch()
{
bt = new M.Batch();
InitializeComponent();
}
private void metroButton3_Click_1(object sender, EventArgs e)
{
bt.batchNum = Convert.ToInt32((metroGrid2.CurrentCell.Value).ToString());
V.BatchEdit bEdit = new V.BatchEdit();
this.Hide();
bEdit.Show();
}
M.Batch:
public int batchNum;
public int BatchNum
{
set { batchNum = value; }
get { return batchNum; }
}
V.BatchEdit
static M.Batch bt = new M.Batch();
DataSet a = bt.getBatch(bt.batchNum);
public BatchEdit()
{
db = new Database();
InitializeComponent();
System.Windows.Forms.MessageBox.Show(bt.batchNum.ToString() + "Batchedit");
try
{
metroTextBox1.Text = a.Tables[0].Rows[0][2].ToString();
}
catch (Exception exceptionObj)
{
MessageBox.Show(exceptionObj.Message.ToString());
}
}
I'm new to coding and c#. I'm not sure if I placed static even though it should not be static or what.
Yes, you are using static improperly here.
The easiest way to see what's going wrong is to notice that you are calling new M.Batch() twice. That means you have two different instances of M.Batch in your application. And nowhere in your code do you attempt to share those instances.
What you should be doing is passing your instance of M.Batch from one form to another, e.g. in the constructor:
// V.Batch
bt.batchNum = Convert.ToInt32((metroGrid2.CurrentCell.Value).ToString());
V.BatchEdit bEdit = new V.BatchEdit(bt);
this.Hide();
bEdit.Show();
// V.BatchEdit
private M.Batch bt;
private DataSet a;
public BatchEdit(M.Batch batch)
{
this.bt = batch;
this.a = this.bt.getBatch(bt.batchNum)
// Rest of your code here.
}
If You don't need the 'M.Batch' class for something else and you only use it for passing the value to V.BaychEdit, just declare a public property in V.BatchEdit like you did in M.Batch and use it like this:
V.BatchEdit bEdit = new V.BatchEdit();
bEdit.BatchNum = Convert.ToInt32((metroGrid2.CurrentCell.Value).ToString());
Your problem is that although your using statics you're still assigning a new instance to the static field.

use of fields of runing form in another form [duplicate]

This question already has answers here:
Call method on another form that is still open?
(2 answers)
Closed 9 years ago.
I create a form that contains a public field and a button. The button creates another form and shows it as dialog. Can I access to the public fields on main form? the main form run in main method like this:
static class Program
{
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new MainForm());
}
}
public partial class MainForm : Form
{
public int number;
public MainForm()
{
InitializeComponent();
}
private void button_Click(object sender, EventArgs e)
{
(new SecondForm()).ShowDialog();
}
}
public partial class SecondForm : Form
{
public SecondForm()
{
InitializeComponent();
}
void Method()
{
//How can I access to number?
}
}
I don't want use constructor because there are a lot of variables in mainform.
You can use the ShowDialog Method that sets the owner of the created form.
secondForm.ShowDialog(this);
usage in your second form:
var temp = ((MainForm)this.Owner).number;
Create an object of the MainForm class and you can use it access all the public variables in the class
MainForm f = new MainForm();
int a = f.number;
This might not really work as it will create a new instance of the MainForm class and you might not get the desired result, so a better will be to make number static.
public static int number;
Then you can use
int a = MainForm.number;
Sure, just do:
var form = new SecondForm();
form.<property> = <value>
// ...
DialogResult result = form.ShowDialog();
if (result == DialogResult.OK)
{
// ...
You said you don't want to use the SecondForm constructor to pass all of those variables from MainForm. Why not just create a class to hold the values and pass the class instance between the two?
public class FormData
{
public int number;
public int anotherInt;
public FormData(int num)
{
number = num;
}
}
public class SecondForm : Form
{
private FormData myData;
public SecondForm(FormData data)
{
myData = data;
}
}
public void AMethodInMainForm()
{
FormData d = new FormData(this.number);
d.anotherInt = 26;
SecondForm frm = new SecondForm(d);
}
Declare your number as public static int number and then:
public partial class SecondForm : Form
{
public SecondForm()
{
InitializeComponent();
}
void Method()
{
MainForm.number = // something
}
}
Define a public number in SecondForm
public int number;
Then before you show SecondForm try this:
SecondForm f2 = new SecondForm();
f2.number = this.number;
f2.ShowDialog();
Then you can access your number inside of your SecondForm.This works if you call number variable several times from your methods inside of your form2.But if you want access number in Form1 whenever you want,you should define it as Static and Public

Update ProgressBar in another window from a function?

I have a Background Task that uploads a file, and i want it to report the progress to a progressbar on another form. How would i go about that? I'm slightly new to C# but long time VB .net programmer.
I was messing with this code but its totally wrong.
System.Windows.Forms.Form progForm = new ProgressWindows();
Control[] ctrls = progForm.Controls.Find("fileProgress1", true);
If you're using a BackgroundWorker then just call ReportProgress. Otherwise you'll need to dispatch the UI change to the correct thread. In WinForms see the Control.InvokeRequired property and related methods. The WPF equivalent is DispatcherObject.VerifyAccess.
Edit: Visual Studio isn't in front of me so there may be some minor compile errors.
public partial class MyForm : Form
{
public MyForm()
{
InitializeComponent(); // fileProgress1 setup.
}
private void StartTask()
{
Task t1 = new Task(BackgroundMethod1, fileProgress1); // Explicitly pass a reference to the progress bar.
Task t2 = new Task(BackgroundMethod2); // Use a method that has access to the bar.
Task t5 = new Task(BackgroundMethod3, IncrementPBMethod); // Pass an action to the background method. Abstracting the physical progress bar as something where you can set the progress.
Task t4 = new Task(delegate() { /* fileProgress1 referened*/ }); // Create a closure. I don't recommend this method.
}
private static void BackgroundMethod1(ProgressBar pb)
{
for(int i = 0; i < 100; ++i)
{
if(pb.InvokeRequired)
{
pb.Invoke(delegate() { pb.Value = i; });
}
Thread.Sleep(1000);
}
}
private void BackgroundMethod2()
{
for(int i = 0; i < 100; ++i)
{
if(fileProgress1.InvokeRequired)
{
fileProgress1.Invoke(delegate() { fileProgress1.Value = i; });
}
Thread.Sleep(1000);
}
}
private static BackgroundMethod3(Action<int> setProgress)
{
for(int i = 0; i < 100; ++i)
{
setProgress(i);
Thread.Sleep(1000);
}
}
private void IncrementPBMethod(int value)
{
if(fileProgress1.InvokeRequired)
{
fileProgress1.Invoke(IncrementPBMethod, value);
}
else
{
fileProgress1.Value = value;
}
}
}
If you problem is just accessing the fileProgress1. The easiest solution is make it public in the ProgressWindow class. By default controls are private.
Then you can access the fileProgress1 control as below.
ProgressWindows progForm = new ProgressWindows();
//progForm.fileProgress1
However the better way is exposing a public method in the PrefressWindows class that updates the progress.
In the ProgressWindows Class
public void UpdateProgressBar(int percentage)
{
// Set the progress in the progress bar.
fileProgress1.Percentage = percentage;
}
Call the above method as below.
ProgressWindows progForm = new ProgressWindows();
progForm.UpdateProgressBar(percentage);
I'm assuming that your second form (the one you want to show progress in) is instantiated in the form doing the "task". Why don't you simply create a public method in the second form that excepts an integer parameter to update the progress bar value? Seems like a relatively easy solution.

Categories