Switching currently displayed form (winforms) - c#

If I'm going about things COMPLETELY wrong here please tell me the better way to do it. Also sorry for any bad formatting / bad question wording; I haven't really used SO before.
I'm making a maths app which will draw polygons and other shapes and work out the area.
I have a menustrip where you can choose the shape to draw:
I want to make it so when you click one of the buttons (polygon/circle) it will take you to the associated form (I will post the forms below):
I'm trying to control the currently displayed form with a C# class i've made called "MenuManager.cs" however it's giving me an error:
System.TypeInitializationException: 'The type initializer for 'MathsApp.PolygonCalculator' threw an exception.' ArgumentException: Delegate to an instance method cannot have null 'this'
. I will post the code below:
public class MenuManager
{
/* Reference to forms */
PolygonCalculator polygonCalculator = new PolygonCalculator();
CircleCalculator circleCalculator = new CircleCalculator();
/* I believe this is causing the errors. */
public void MenuClickPolygon(object sender, EventArgs e)
{
polygonCalculator.Show();
}
public void MenuClickCircle(object sender, EventArgs e)
{
circleCalculator.Show();
}
}
This is how i'm accessing the MenuManager class and trying to change the displayed form:
this.Menu = new MainMenu();
MenuItem item = new MenuItem("Shapes");
this.Menu.MenuItems.Add(item);
item.MenuItems.Add("Polygon", new EventHandler(menuManager.MenuClickPolygon));
item.MenuItems.Add("Circle", new EventHandler(menuManager.MenuClickCircle));
public partial class PolygonCalculator : Form
{
public static Color penColour = Color.Black;
public static Random rnd = new Random();
public static MenuManager menuManager = new MenuManager();
public PolygonCalculator()
{
InitializeComponent();
InstantiateMenu();
}
public void InstantiateMenu()
{
this.Menu = new MainMenu();
MenuItem item = new MenuItem("Shapes");
this.Menu.MenuItems.Add(item);
item.MenuItems.Add("Polygon", new EventHandler(menuManager.MenuClickPolygon));
item.MenuItems.Add("Circle", new EventHandler(menuManager.MenuClickCircle));
}
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new PolygonCalculator());
}

Related

how to access array from one class to another class in C# winforms?

I want to access array from one class to another class because my end-user enter the name list on one class. That list store into array in the same class. Then that name list access from another class. I'm not getting any errors in compile time. only I'm getting a run time error. I'm literally sorry to all coz I'm absolutely noob :(
public partial class custom : Form //class one which is end user enter the name list
{
public string PresentValue;
public string NormalValue;
public string[] PValue = new string[50];//public array
public string[] NValue = new string[50];//public array
}
public static int PresentArray = 0;// this line is used to increment the array index
private void cstmsvbtn_Click(object sender, EventArgs e)//this line enter the user namelist
{
PresentValue = cstmtst1.Text + "_PV";//concatinate '_PV'
NormalValue = cstmtst1.Text + "_NV";//concatinate '_NV'
PValue[PresentArray] = PresentValue;
NValue[PresentArray] = NormalValue;
PresentArray++;
}
public partial class print : Form // class to which is end user want to access that name list
{
custom customarray = new custom();// I instantiate the custom cass object
private void button1_Click(object sender, EventArgs e)//when i press this button message box show an empty white box only
{
MessageBox.Show(CustomArray.PValue[0],CustomArray.NValue[0]);
}
}
This is a common requirement and there are many ways to achieve this outcome (some of which might be considered "hacky"). Things I don't recommend:
Changing visibility to public for data fields that should be private
Creating tight dependencies of one form to the implementation details of another.
Creating "global" variables using the static keyword.
Since you claim to be a "noob" I'd like to suggest learning about the event keyword and using Events to communicate between forms. Yes, there is a small learning curve here, but chances are you'll use this a lot and it will be a good investment. I put a link in the Comments section so you can clone or browse this example and see if it does what you want it to (I recommend setting debugger break points so you can see why it does what it does).
What you have (according to your post) is a print form and a custom form. And though you don't really say, this example will have a MainForm that can show the other two:
PrintForm
The PrintForm requires the NValue and PValue arrays to do its printing. By declaring an event named ArrayRequest we give it the ability to request these arrays. Importantly, this class doesn't need to have any knowledge of where this information might be coming from.
public partial class PrintForm : Form
{
public PrintForm() => InitializeComponent();
This is how the class can initiate the request
public event ArrayRequestEventHandler ArrayRequest;
protected virtual void OnArrayRequest(ArrayRequestEventArgs e)
{
ArrayRequest?.Invoke(this, e);
}
When the button is clicked, try and get the information by callingOnArrayRequest
private void buttonShowArray_Click(object sender, EventArgs e)
{
ArrayRequestEventArgs req = new ArrayRequestEventArgs();
OnArrayRequest(req);
if(req.Count == 0)
{
MessageBox.Show("Invalid Request");
}
else
{
String[] allValues =
Enumerable.Range(0, req.Count)
.Select(index => $"{req.NValue[index]} | {req.PValue[index]}")
.ToArray();
MessageBox.Show(
text: string.Join(Environment.NewLine, allValues),
caption: "All Values"
);
}
}
}
// Defined outside the PrintForm class
public delegate void ArrayRequestEventHandler(Object sender, ArrayRequestEventArgs e);
public class ArrayRequestEventArgs : EventArgs
{
public int Count { get; set; }
public string[] PValue { get; set; }
public string[] NValue { get; set; }
}
CustomForm
The CustomForm as shown in your post is the class that contains the arrays.
public partial class CustomForm : Form
{
public CustomForm()
{
InitializeComponent();
}
We give this class the ability to fulfill a request for the arrays.
internal void ArraysRequested(object sender, ArrayRequestEventArgs e)
{
e.Count = _presentArray;
e.NValue = _nValue;
e.PValue = _pValue;
}
The data held in this class should be private.
// These should all be private
// See naming conventions: https://stackoverflow.com/a/17937309/5438626
// Set up visual studio to do this automatically: https://ardalis.com/configure-visual-studio-to-name-private-fields-with-underscore/
private string _normalValue;
private string _presentValue;
private int _presentArray = 0;
private string[] _pValue = new string[50];//public array
private string[] _nValue = new string[50];//public array
private void cstmsvbtn_Click(object sender, EventArgs e)
{
_presentValue = $"{cstmtst1.Text}_PV"; //concatinate '_PV'
_normalValue = $"{cstmtst1.Text}_NV"; //concatinate '_NV'
// Make sure index doesn't exceed the size of the array
if ((_presentArray < _pValue.Length) && (_presentArray < _nValue.Length))
{
_pValue[_presentArray] = _presentValue;
_nValue[_presentArray] = _normalValue;
_presentArray++;
}
else MessageBox.Show("Array is Full");
Text = $"Custom: Count={_presentArray}";
cstmtst1.Text = $"Hello {_presentArray + 1}";
}
}
MainForm
It is the MainForm class that oversees the operations and "knows" how the forms should interact. The constuctor method is where the connection is made between the event fired by PrintForm and the fulfillment by the CustomForm.
public partial class MainForm : Form
{
public MainForm()
{
InitializeComponent();
// THIS IS THE "GLUE"
_printForm.ArrayRequest += _customForm.ArraysRequested;
}
private CustomForm _customForm = new CustomForm();
private PrintForm _printForm = new PrintForm();
// In MainForm.Designer.cs
protected override void Dispose(bool disposing)
{
if (disposing)
{
if (components != null)
{
components.Dispose();
}
_customForm.Dispose();
_printForm.Dispose();
}
base.Dispose(disposing);
}
private void buttonShowCustom_Click(object sender, EventArgs e)
{
_customForm.ShowDialog(owner: this);
}
private void buttonShowPrint_Click(object sender, EventArgs e)
{
_printForm.ShowDialog(owner: this);
}
}
You will need to adapt this to your specific requirements but hopefully this will give you some basics to go on.

Where do I put an array so that It is accessible to windows form controls?

I am working on a C# windows forms app trading card game collection manager
I have a Card class and I'm trying to create an array of Card objects to represent every card in the set. From there, I would like to be able to search and display the Card details on the forms text boxes, labels, picturebox etc.
I have tried putting the array in form1.cs and program.cs both inside and outside different classes. The only place the array seems to be in scope for the click event handler is when it is declared inside it.
Where can I initialize this array so it accessible throughout the whole program and in scope for the form event handlers? I'm very new to object oriented programming with windows forms
The Constructor for Card class takes one int parameter and initializes the "collector number"
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
const int NumberOfCardsInSet = 3;
Card[] Set = new Card[NumberOfCardsInSet];
for (int iii = 0; iii < NumberOfCardsInSet; iii++)
{ Set[iii] = new Card(iii + 1); }
}
}
The constructor for Card takes an int parameter just to set the set number. After this, the card objects are accessible and I can set their fields.. however down here:
private void button1_Click(object sender, EventArgs e)
{
//test button
DisplayCard(Set[0]); //error: name "set" doesnt exist in current context
}
public void DisplayCard(Card selectedCard)
{
myDCollectorNoTB.Text = selectedCard.CollectorNum.ToString();
myDNameTB.Text = selectedCard.Name.ToString();
myDRarityTB.Text = selectedCard.CardRarity.ToString();
myDCostTB.Text = selectedCard.Cost.ToString();
}
The array needs to be declared within the scope of the class (as a member, just like the constructor or the button1_Click and DisplayCard methods). Basing this on your code:
public partial class Form1 : Form
{
private const int NumberOfCardsInSet = 3;
private Card[] Set = new Card[NumberOfCardsInSet];
public Form1()
{
InitializeComponent();
for (var i = 0; i < NumberOfCardsInSet; ++i)
{
Set[i] = new Card(i + 1);
}
}
//your button1_Click and DisplayCard methods go here
}

Repopulate a form field from a different class in C#

I'm trying to repopulate a textbox from a separate class. I've looked through a number of instances of this same question and found what I thought was a good solution. But, I can't make it work. I've tried to resolve the issue by creating a separate thread to send the data back. I don't know if this is a great idea or not. But, I know the data is getting back to the correct place without it because it shows up in the console. Any suggestions? Thanks!
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
public void updater(double value)
{
textBox1.Text = value.ToString(); // Trying to update here
Console.WriteLine(value); // The new multiple makes if back to here...
}
private void button1_Click(object sender, EventArgs e)
{
CALC c = new CALC();
c.valuecalculator(.0025);
}
}
public class CALC
{
public void valuecalculator(double multiplier)
{
for (int index = 0; index < 1000; index++)
{
Form1 f = new Form1();
double newMultiple = index * multiplier;
f.updater(newMultiple);
}
}
}
You're making a new copy of the form in your valuecalculator method, but you should be using the same form.
There are loads of ways to solve this.
You could pass an instance of the form into your valuecalculator method.
You could make the reference to the form static in your Program.cs or whatever startup file originally initialises it.
You could give the form a reference to itself
You could put the code to update the form in the button1 click event (this makes most sense) by making the valuecalculator return the result instead of returning void
Your components should only do one thing. The calculator should perform a calculation and return the result:
public static class Calc
{
public static double CalculateValue(double multiplier)
{
return 100 * multiplier;
}
}
Forms should be as simple as possible. Meaning they are only concerned with displaying form elements and passing events to event handlers. The actual logic that happens in these event handlers should be someone else's responsibility. I like to pass this logic in the constructor:
public partial class Form1 : Form
{
public Form1(Func<double, double>CalculateValue)
{
InitializeComponent();
button1.Click += (sender, eventArgs) => textBox1.Text = CalculateValue(.0025).ToString();
}
}
Constructing and connecting classes with each other is another responsibility. The simplest version is to use your Main() method:
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
var form = new Form1(Calc.CalculateValue);
Application.Run(form);
}
}

Track dynamic form automcatically in winforms

I have an application having 2 forms that opens on application start and 3rd form is getting added on runtime.
I have an another class library that currently monitors the activities of a single form. The below code snippet is of application :-
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Form[] f = new Form[2];
f[0] = new Form1();
f[1] = new Form2();
f[0].Show();
AppContext nvca = new AppContext(new Form1(), new Form2());
Application.Run(nvca);
}
AppContext class is in class library where I am trying to catch all the forms of the application whether it is static or comes at runtime :-
public class AppContext:ApplicationContext
{
public static Form[] AvailForms = null;
public AppContext(params Form[] forms)
{
AvailForms = forms;
UAction ua = new UAction();
foreach (Form f in forms)
{
for (int i = 0; i < f.Controls.Count; i++)
{
ua.setClickHandlerAsync(f.Controls[i]);
}
}
}
public void setClickHandlerAsync(Control item)
{
//Have to recursively get all the element to wrap the click listener.
item.MouseClick += ClickHandlerAsync;
}
private async void ClickHandlerAsync(object sender, MouseEventArgs e)
{
Console.WriteLine("Click Handler called");
}
}
I am searching for a way through which I can track all the forms that are either already added in application or added at runtime.
I have tried through ApplicationContext, but it failed to capture the win events like click, text change, form events even though all the event handlers has been set properly on it.
Any help would be appreciated.

How do I modify a form's text box from a separate class?

I am trying to create a form where I can update text in a text box without requiring interaction from the user. Right now I am trying to create a method in my form's class that updates the TextBox.Text field. Outside of the class I am not able to access the function.
Right now I am trying
static void Main()
{
Form status = new Window();
Application.Run(status);
status.UpdateTextBox("NewText");
}
My form class looks like (from the designer)
public partial class Window : Form
{
public Window()
{
InitializeComponent();
}
public void UpdateTextBox(string text)
{
textBox1.Text = text;
}
}
I have also tried making the Textbox a public property like this.
public string DisplayedText
{
get
{
return textbox1.Text;
}
set
{
textbox1.Text = value;
}
}
I am trying to edit the field during runtime. Is this possible?
You can access the function, but if you look at the code, there is a problem:
static void Main()
{
Form status = new Window();
Application.Run(status); // Blocks here!
status.UpdateTextBox("NewText");
}
When you call Application.Run(), it will not return control to your program until the status form has closed. At that point, setting the status is too late...
You can set it before you run, but after you've constructed:
static void Main()
{
Form status = new Window();
status.UpdateTextBox("NewText");
Application.Run(status);
}
It's this code...the Application.Run will block, status.UpdateTextBox isn't going to execute until you close the form.
static void Main()
{
Form status = new Window();
Application.Run(status);
status.UpdateTextBox("NewText");
}

Categories

Resources