How can I parse data between installed WinForm projects? - c#

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;
}

Related

Reusing a method between classes

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)

Determine form's identity (type information) at runtime

Say I have a common class that performs some time-consuming step (eg. saving stuff to USB). I'd like to be able to call that code from multiple forms and receive feedback whenever a step is completed. How does the common class know to whom to send feedback to? The code below describes the situation:
// ### Common class frmCommon ###
// Parent form (when feedback on some slow operation is required)
private static Form m_frmParent = null;
// ...
public static void SetParentForm(Form frmParent)
{
// When some time consuming process takes place (such as saving to USB), setting the
// parent form allows feedback to be given to the user (eg. as a progress bar)
m_frmParent = frmParent;
}
public static void DoSomething()
{
for (int nStep = 0; nStep < 100; nStep++)
{
// Tell the parent form how many product sets (groups of 20) there are to read
if (m_frmParent != null)
{
// How to decide whether to call form 1 or form 2?
((frmForm1)m_frmParent).SendFeedback(nStep);
((frmForm2)m_frmParent).SendFeedback(nStep);
}
// Perform the time-consuming step...
SlowStep(nStep);
}
}
// ### FORM 1 frmForm1 ###
private void SomeEventForm1(int nStep)
{
frmCommon.SetParentForm(this);
frmCommon.DoSomething();
frmCommon.SetParentForm(null);
}
public void SendFeedback(int nStep)
{
// Do something like update a progress bar on form 1
Application.DoEvents();
}
// ### FORM 2 frmForm2 ###
private void SomeEventForm2(int nStep)
{
frmCommon.SetParentForm(this);
frmCommon.DoSomething();
frmCommon.SetParentForm(null);
}
public void SendFeedback(int nStep)
{
// Do something like update a progress bar on form 2
Application.DoEvents();
}
Aiming for .NET 2.0 if that makes a difference.
I'd rather use an event:
public class SlowProcess {
...
// Simplest, not thread safe
public static event EventHandler<int> StepChanged;
public static void DoSomething() {
for (int nStep = 0; nStep < 100; nStep++) {
if (null != StepChanged)
StepChanged(null, nStep);
SlowStep(nStep);
}
}
}
...
public partial class MyEventForm: Form {
...
private void onStepChange(Object sender, int nStep) {
//TODO: update form here after receiving a feedback
}
private void TraceSlowProcess() {
// feedback is required
SlowProcess.StepChanged += onStepChange;
try {
SlowProcess.DoSomething();
}
finally {
// No need of feedback
SlowProcess.StepChanged -= onStepChange;
}
}
}
The calling code will have to provide a delegate to that class. When the class is done with the time consuming process, it will call that delegate to inform the calling code that it finished. Look here for a good tutorial on how to do this.
1 - If SendFeedback is a function you implemented in both forms, and they do the same, consider creating a single static method in a static class to extend the Form:
public static class FormExtender
{
public static void SendFeedback(this Form frm, int nStep)
{
//do what must be done
//you can call this anyhere using, for instance: m_frmParent.SendFeedback(nStep)
//when you call it like that, m_frmParent will be given to this function as the argument frm
}
}
2 - But if the methods are different in both forms, I suggest you create an interface:
interface IFormWithFeedback
{
void SendFeedback(int nStep);
}
Then form1 and form2 should implement this (just add , IFormWithFeedBack where your forms are declared):
public class frmForm1 : Form, IFormWithFeedback
public class frmForm2 : Form, IFormWithFeedback
And your parent form inside that class should be an IFormWithFeedback instead of a form:
private static IFormWithFeedback m_frmParent = null;
Both options (extension method or interface) would allow you to call SendFeedback direclty from m_frmParent without casting it.

C# can't see text when updating text box

I was wondering if someone could help me with a little issue I'm having. I'm trying to update a textbox from another class but the text is not showing up on in the textbox even though it is being sent as I have printed it to the screen.
The code I'm using is below:
Program.cs
namespace Search
{
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
try
{
Application.SetCompatibleTextRenderingDefault(false);
}
catch (InvalidOperationException e)
{
}
Application.Run(new Form1());
}
public static readonly Form1 MainLogWindow = new Form1();
}
}
HexToASCII:
public class HexToASCII
{
Output o = new Output();
public void hexToAscii(String hex, int textBox)
{
//Convert the string of HEX to ASCII
StringBuilder sb = new StringBuilder();
for (int i = 0; i < hex.Length; i += 2)
{
string hs = hex.Substring(i, 2);
sb.Append(Convert.ToChar(Convert.ToUInt32(hs, 16)));
}
//Pass the string to be output
string convertedHex = sb.ToString();
Program.MainLogWindow.UpdateTextBox(convertedHex);
}
}
Form1:
private delegate void NameCallBack(string varText);
public void UpdateTextBox(string input)
{
if (InvokeRequired)
{
textBox2.BeginInvoke(new NameCallBack(UpdateTextBox), new object[] { input });
}
else
{
textBox2.Text = textBox2.Text + Environment.NewLine + input;
}
}
I have tried to run it using a new thread ThreadStart ts = delegate()... but I'm unable to get the textbox to update. Sorry I'm very new to c#, could someone please explain the issue so I can understand it and learn for next time. Many thanks :)
This is the problem:
static void Main()
{
...
Application.Run(new Form1());
}
public static readonly Form1 MainLogWindow = new Form1();
You're creating two forms: one of them is being shown (with Application.Run) but you're changing the contents of the text box on the other one:
Program.MainLogWindow.UpdateTextBox(convertedHex);
You haven't shown how you're calling hexToAscii in the first place - personally I would try to avoid having static references to GUI elements like this, but you could get your code to work just by changing your Main method to use:
Application.Run(MainLogWindow);

Implementation to change current user in winforms app

I want to realize to change current user in my application. I have the following code:
public class Framework
{
private MainForm mainForm = null;
... // other fields
public virtual void run()
{
if (appInitializer!=null)
{
ISecurityManager securityManager = appInitializer.SecurityManager;
if (securityManager!=null)
{
if (securityManager.DoLogin())
{
RegisterDefaultActionsGroup();
InitializePlugins(appInitializer.Plugins);
// Apply rights for user
ActionsManager.Inst.ApplySecurity(securityManager, securityManager.CurrentUser);
mainForm = new MainForm();
mainForm.Text = appInitializer.ApplicationTitle;
if (appInitializer.ApplicationIcon != null)
{
mainForm.Icon = appInitializer.ApplicationIcon;
}
CorrectFormSizes(mainForm);
Context[Constants.MainForm] = mainForm;
MenuManager.Inst.FillMenu(DefaultGroups.MAIN_MENU, mainForm.MainMenu, ActionClick);
if(appInitializer.IsHaveToCreatePanelInfo) PanelInfoManager.Inst.FillInfo(mainForm);
if (appInitializer.IsHaveToCreateToolBar)
{
MenuManager.Inst.FillToolbar(DefaultGroups.MAIN_TOOLBAR, mainForm.MainToolStrip, ActionClick);
}
mainForm.MainToolStrip.Visible = mainForm.MainToolStrip.Items.Count > 0;
NotifyPluginsAboutShowing(appInitializer.Plugins);
Application.Run(mainForm);
}
}
}
}
...//other methods
}
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Framework framework = new Framework(new EArchiveInitializer());
framework.run();
}
}
In the button for change user I have:
Framework.Instance.MainForm.MainMenuStrip.Items.Clear();
Framework.Instance.run();
But, I got error: Starting a second message loop on a single thread is not a valid operation. Use Form.ShowDialog instead.
I know that means this error, but I can't to rewrite my code.
Can you help me?
Thanks.
SOLUTION:
Rewrite the last line in run method:
if (!Application.MessageLoop)
Application.Run(mainForm);
else
mainForm.Show();
Thanks Jonathan.
The problem is actually quite easy, the issue is you are calling Application.Run twice (first on load, second on the button)
A quick work around for this, would be to have the Application.Run an ApplicationContext, instead of a form initially, and from your public virtual void run() method, load the required form.
public class Framework
{
private MainForm mainForm = null;
... // other fields
public virtual void run()
{
if (appInitializer!=null)
{
ISecurityManager securityManager = appInitializer.SecurityManager;
if (securityManager!=null)
{
if (securityManager.DoLogin())
{
RegisterDefaultActionsGroup();
InitializePlugins(appInitializer.Plugins);
// Apply rights for user
ActionsManager.Inst.ApplySecurity(securityManager, securityManager.CurrentUser);
mainForm = new MainForm();
mainForm.Text = appInitializer.ApplicationTitle;
if (appInitializer.ApplicationIcon != null)
{
mainForm.Icon = appInitializer.ApplicationIcon;
}
CorrectFormSizes(mainForm);
Context[Constants.MainForm] = mainForm;
MenuManager.Inst.FillMenu(DefaultGroups.MAIN_MENU, mainForm.MainMenu, ActionClick);
if(appInitializer.IsHaveToCreatePanelInfo) PanelInfoManager.Inst.FillInfo(mainForm);
if (appInitializer.IsHaveToCreateToolBar)
{
MenuManager.Inst.FillToolbar(DefaultGroups.MAIN_TOOLBAR, mainForm.MainToolStrip, ActionClick);
}
mainForm.MainToolStrip.Visible = mainForm.MainToolStrip.Items.Count > 0;
NotifyPluginsAboutShowing(appInitializer.Plugins);
mainForm.Show();
}
}
}
}
...//other methods
}
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new MyHiddenContext());
}
}
public class MyHiddenContext
: ApplicationContext
{
private static Form activeFormInstance;
public MyHiddenContext()
{
this.RunFramework();
}
public void RunFramework()
{
Framework framework = new Framework(new EArchiveInitializer());
this.framework.run();
activeFormInstance = Framework.Instance.MainForm;
}
public static void ChangeUser()
{
activeFormInstance.Close();
activeFormInstance.Dispose();
Framework.Instance.MainForm.MainMenuStrip.Items.Clear();
Framework.Instance.run();
}
}
Don't quote me on the code actually working, but its more to give an idea on which way to go. The problem though is you can't call Application.Run more than once, so the principal is to have a containing instance or context (in any such sense, form, console etc)

Passing data to a non-static listBox, by calling function from another class

I have a simple forms program that I have been fighting with for a while now. I simply want to be able to call a method from a different class file (when a certain step is triggered in the code in that class file) in order to insert a string in the listBox.
Here is my main method, pretty standard:
class Program
{
[STAThread]
static void Main(string[] args)
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new MainForm());
}
}
Here is the function which resides in my MainForm.cs file, which I can call just fine from that class file (via 'TextToBox(myString);'):
public partial class MainForm : Form
{
...
// Function to output results to main Listbox window
public void TextToBox(string aString)
{
// Place messages in Main Display list box window
this.listBox1.Items.Insert(0, aString);
}
...
}
But my problem is when I am in another class and I want to call 'TextToBox(myString);'. If I create another object reference of the MainForm, the code compiles fine but nothing will show up in the listBox. How do I do this? I cannot simply make TextToBox() static. I know I must create the object reference but I can't figure out how to reference the ORIGINAL MainForm, the one that was created in the Main method. Thanks in advance...
This will work, but only when you have one instans of MainForm.
public class MainForm : Form
{
public MainForm()
{
Instance = this;
}
public static MainForm Instance { get; private set; }
// Function to output results to main Listbox window
public void TextToBox(string aString)
{
// Place messages in Main Display list box window
this.listBox1.Items.Insert(0, aString);
}
}
public class Other
{
public void AddTextToListBox()
{
MainForm.Instance.TextToBox("Test");
}
}
...Edit...
Alternative:
class Program
{
public static MainForm MainFormInstance;
[STAThread]
static void Main(string[] args)
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
MainFormInstance = new MainForm();
Application.Run(MainFormInstance);
}
}
public class Other
{
public void AddTextToListBox()
{
Program.MainFormInstance.TextToBox("Test");
}
}
I would just pass a delegate to the other class.
/* this runs (previous code was not guaranteed to run) */
class OtherClass
{
public delegate void TextToBox(string s);
TextToBox textToBox;
int next = 0;
public OtherClass(TextToBox ttb)
{
textToBox = ttb;
}
public void SendSomeText()
{
textToBox(next.ToString());
next++;
}
}
I'm assuming you'll be instantiating OtherClass from MainForm. Is this how you're calling "OtherClass"?
public partial class MainForm : Form
{
OtherClass otherClass;
public MainForm()
{
/* just two controls -- listBox1 and button1 */
InitializeComponent();
otherClass = new OtherClass(this.TextToBox);
}
public void TextToBox(string aString)
{
listBox1.Items.Add(aString);
}
private void button1_Click(object sender, EventArgs e)
{
otherClass.SendSomeText();
}
}
On a button click the next numeric value is added at the beginning of the ListBox. You'll have to post some of your code if you need further help.
alternatively you could use a singleton pattern, or static methods and make sure you include the class in a 'using' statement at the top of your program

Categories