C# loading specific class based on user choice - c#

So I am a beginner in software development and practicing C# in my time at home.
I have a project that I am working on and have reached a point where I am not sure how to code the functionality.
Imagine in my solution I have a winform UI with a dropdown. Inside that dropdown the user can make a choice and click a button to run a procedure. Depending on what the user has picked, it should initialize the class/object that is picked.
So the dropdown will have options such as; runOptionOne, runOptionTwo. If runOptionTwo is picked in the dropdown, upon clicking the button it will do:
runOptionOne runoptionone = new runOptionOne();
runoptionone.Doaction();
I do not want to have string checks on the dropdown as that will be loads of if statements.
Is there a technique or method to initialize a specific class based on user choice.

Combobox.Items accepts objects. For displaying, their ToString() method will be used.
This makes it possible to access the object directly via SelectedItem. As long as they share a common interface, it's easy to call a method on them.
private void button1_Click(object sender, EventArgs e)
{
var obj = (Interface) comboBox1.SelectedItem;
obj.DoSomething();
}
The other classes:
internal interface Interface
{
void DoSomething();
}
class Class1:Interface
{
public void DoSomething() { }
public override string ToString() => "Option 1"; // TODO: make translatable via resource
}
class Class2:Interface
{
public void DoSomething() { }
public override string ToString() => "Option 2"; // TODO: make translatable via resource
}
And initialization like
private void Form1_Load(object sender, EventArgs e)
{
comboBox1.Items.Add(new Class1());
comboBox1.Items.Add(new Class2());
}
I don't like this approach too much, since I consider the ToString() method to be rather developer oriented.
IMHO, a better approach is to have a dictionary with display strings as keys and objects as values. That way you also get rid of the if-statements and reduce cyclomatic complexity:
private readonly IDictionary<string, Interface> _displayItems = new Dictionary<string, Interface>
{
{"Option 1", new Class1()},
{"Option 2", new Class2()}
};
private void Form1_Load(object sender, EventArgs e)
{
foreach (var item in _displayItems)
{
comboBox1.Items.Add(item.Key);
}
}
private void button1_Click(object sender, EventArgs e)
{
var key = (string) comboBox1.SelectedItem;
_displayItems[key].DoSomething();
}

Related

How do I pass variables between methods in C#? [duplicate]

This question already has answers here:
Use a variable from another method in C#
(2 answers)
Closed 2 years ago.
I am trying to build a program, but I realized I can't access a certain variable because it's created in another method.
How do I transfer a variable to another method?
This is an example of what I'm trying to do:
namespace Example
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
string text = textBox1.Text;
}
private void label1_Click(object sender, EventArgs e)
{
// Get the "text" variable and use it
label1.Text = text;
}
}
}
Your example has a Form named Form1 that has a Button named button1, a TextBox named textbox1 and a Label named label1.
The scenario you are attempting is:
user enters some text into textbox1
user clicks on button1, this will save the current value from textbox1
user clicks on label1, this will display the value that was stored in the previous step
It is important to understand that in this scenario we are not trying to pass a value between 2 methods because the button click and the label click can occur independently of each other, so really this is like the memory store (M+) and memory recall (MR) buttons on calculator.
To achieve this storage you should create an instance variable (sometimes referred to as a member variable) on the Form1 class, this will be accessible to the other methods on the same instance of the Form1 class.
See Working with Instance and Local variables for a practical explanation
Create a field or a property to store the value, for your specific example either would work, however to become familiar with C# techniques I would recommend you start with a property, as that better encapsulates your scenario of storing the value for later use and later to potentially augment how and where the value is actually stored.
See What is the difference between a field and a property?
for a healthy discussion
Until you need to change the implementation, you can simply use an Auto-Property
public string StoredText { get; set; }
Now in the click event handler of button1 we can set the value of the StoredText property on the Form1 instance
private void button1_Click(object sender, EventArgs e)
{
this.StoredText = textBox1.Text;
}
set is a term we use for saving a value into a property in c#
Note the use of the this keyword, it is optional in this case, or can be inferred by the compliler, it indicates that we want to reference a member on the instance of the class, not a variable that might have the same name within the same method scope of the line of code that is executing.
Finally in the click event handler of label1 we can get the value that was previously stored in the StoredText property in the Form1 instance.
private void label1_Click(object sender, EventArgs e)
{
// Get the "StoredText" variable and use it
label1.Text = this.StoredText;
}
get is a term we use for accessing a value from a property in c#
this is not required, but can be helpful to understand that we are accessing a member that is outside of the current method scope.
Together this looks something like:
namespace Example
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
/// <summary>Saved value from see <see href="textBox1"/></summary>
public string StoredText { get; set; }
private void button1_Click(object sender, EventArgs e)
{
this.StoredText = textBox1.Text;
}
private void label1_Click(object sender, EventArgs e)
{
// Get the "StoredText" variable and use it
label1.Text = this.StoredText;
}
}
}
What you may not have noticed is that textBox1 and label1 are themselves infact instance variables that are initialized in a separate code file when InitializeComponent() is executed in the constructor.
For this reason you do not need to store the value at all and you could simply re-write the client event handler for button1 to write directly to label:
label1.Text = textBox1.Text;
It is possible to pass variables directly between methods without an intermediary store, this is a lesson for another day and will involve return statements and/or parameters on your methods.
In this scenario however, neither return or additional parameters on these methods cannot be used because these are event handlers that need a specific method signature to operate as expected.
You are almost there. It is a common practice in object-oriented programming to have private variables in a class, in order to share states. Add a variable in your class. It will be available in all methods and can be used to shared data between them (this is one approach of many):
namespace Example
{
public partial class Form1 : Form
{
private string inputText { get; set; }
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
inputText = textBox1.Text;
}
private void label1_Click(object sender, EventArgs e)
{
// Get the "text" variable and use it
label1.Text = inputText;
}
}
}

Creating a template double-linked list C#

I need to implement a program that receives various input data( I do the program in WindowsForms, I made the type selection via button), adds them to a double-linked list, sorts it and outputs it to the listBox). However, when creating a double-linked list object in the button class, this list is not seen by other methods from the form class.(this is logical). I would like to create a template list object in the form and then bring it to a specific type after clicking the button. Is there any way to implement this? For now, all I can think of is creating lists of various types that will end up empty. And the sorting/output call will have to be rewritten for each list.
D_List<int> massiv1;
D_List<int> massiv2;
D_List<string> massiv3;
D_List<string> massiv4;
D_List<double> massiv5;
D_List<double> massiv6;
private void button5_Click(object sender, EventArgs e)
{
massiv1 = new D_List<int>();
massiv2 = new D_List<int>();
Gen<int>(ref massiv1, ref massiv2);
}
//... for each list
private void button7_Click(object sender, EventArgs e)
{
M5<string> sort2 = new M5<string>();
D_List<string> sortedd = new D_List<string>();
string s;
Optim<string>(massiv4, sort2, out sortedd, out s);
listBox1.Items.Clear();
utility.Vivod(ref sortedd, listBox1);
label11.Text = s;
label12.Text = sort2.kolvo_srav.ToString();
label13.Text = sort2.kolvo_perest.ToString();
}
//... for each list
Welcome to stackoverflow!
It's hard to say without code but it seems like you should make use of inheritance for this. Polymorphism is designed to solve this kind of problem.
Your data presumably shares some features (for example, to be sorted it would need some kind of order to be defined). Put these into a base class and inherit that in each of subclasses hat represents the data.
As an example:
class BaseItem: IComparable
{
public int CompareTo (object obj);
}
class DecimalItem: BaseItem
{
public decimal Value { get; set; }
//override CompareTo if necessary
}
Now instead of making a separate list for each type of data, you would make a single list of BaseItems and add whichever type the data actually is to this list.
If you are not using subclasses for your data (eg. they are primitive types) you could probably just make your single list of type IComparable.

c# winforms array items gets deleted

I know this might look silly but I got a strange problem in my winforms. I have a windows application in which after a particular set of operations are completed I want to populate a Checked ComboBox. I am doing this using two classes. I want to copy a array from helper class to the form class. Array gets copied when AddArrayItems method is called. But when I see the ComboBox in the form, its null. After debugging with watch variables I got to know that the problem is after copying the array to Form1 array, as soon the control goes back to the caller, the array items are deleted. I tried to replicate my stuff, not exactly but still similar to what I am doing.
My code looks like this:
using System;
using System.Windows.Forms;
namespace DemoApp
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
string[] cboxAr;
public void AddCmboBoxItems(string[] cbArry)
{
cboxAr = new string[cbArry.Length];
Array.Copy(cbArry, 0, cboxAr, 0, cbArry.Length);
//cbArry.CopyTo(cboxAr, 0);
//foreach (string s in cboxAr)
//comboBox1.Items.Add(s);
comboBox1.Show();
}
private void button1_Click(object sender, EventArgs e)
{
HelperClass.DoSomething();
}
}
public class HelperClass
{
public HelperClass()
{
}
public void HelperMethod()
{
SomeMethod();
}
private void SomeMethod()
{
string[] partnrName = new string[5] { "str1", "str2", "str3", "str4", "str5"};
Form1 f = new Form1();
f.AddCmboBoxItems(partnrName);
}
public static void DoSomething()
{
new HelperClass().HelperMethod();
}
}
}
I don't understand what exactly the problem is here. Can anyone please push me in the right direction. Thanks in advance.
You're never showing the form after modifying its controls:
Form1 f = new Form1();
f.AddCmboBoxItems(partnrName);
But you're calling this from within an existing form:
private void button1_Click(object sender, EventArgs e)
{
HelperClass.DoSomething();
}
Presumably you want to modify the controls on that form? Then you'll need a reference to that form. Pass one to the method:
private void button1_Click(object sender, EventArgs e)
{
HelperClass.DoSomething(this);
}
And accept it in the method definition:
public static void DoSomething(Form1 form)
{
new HelperClass().HelperMethod(form);
}
And so until the point where you need to use it. (Side note: You have a lot of weird indirection happening here with a seemingly random mix of static and instance methods and classes. You can simplify a lot, which will make this involve fewer code changes.)
Ultimately, SomeMethod needs the instance of the form to modify:
private void SomeMethod(Form1 form)
{
string[] partnrName = new string[5] { "str1", "str2", "str3", "str4", "str5"};
form.AddCmboBoxItems(partnrName);
}
To illustrate the overall point, consider an analogy...
A car rolls off of an assembly line. You open the trunk and put a suitcase inside. Moments later another car rolls off of the same assembly line. It is identical to the first car in every way. When you open the trunk of the second car, do you expect to find your suitcase inside it?
A Form is an object like any other. Changes made to one instance of an object are not reflected in other instances of the same object. Each instance maintains its own state. In order to modify a particular instance, you need a reference to that instance.

How do I change the value of form controls without triggering any event?

I want to change the value that is assign to control of a form in c# (visual studio 2010), while the form is loaded.
I want my form should display to the end user, but at the same time as I get the data from server, I want it to reflect the same data onto the controls. (without any using timer, thread or any event).
Example : textBox1.text ="abc";
if server is sending "xyz" than while form is already loaded testbox's value should automatically change to xyz.
without any click or any kind of event.
You have to look at how Properties in c# work:
If we decompile a simple class on sharplab.io
public class C {
public int foo
{get;set;}
}
You will see that the compile will always generate backing fields and a getter and setter method.
So if you don't want to trigger an event you will have to bypass these methods as most likely the event will be triggered in there.
This should be doable with an reflection which is normally pretty easy to do.
BUT Textbox doesn't seem to have a backing field which is easily accessible for its Text-Property. Most likely it is set by its private StringSource field. Which is from the internal type StringSource. So first we have to get the type. Get a reference to the constructor then call this and set the private field.
This is how far i've come:
private int number = 0;
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
number++;
this.textBox1.Text = number.ToString();
}
private void button2_Click(object sender, EventArgs e)
{
number++;
Type cTorType = typeof(string[]);
string[] cTorParams = new string[] { number.ToString() };
Type type = this.textBox1.GetType().GetRuntimeFields().ElementAt(11).FieldType;
ConstructorInfo ctor = type.GetConstructor(new[] { cTorType });
object stringSourceInstance = ctor.Invoke(new[] { cTorParams });
this.textBox1.GetType().GetRuntimeFields().ElementAt(11).SetValue(this.textBox1, stringSourceInstance);
}
private void textBox1_TextChanged(object sender, EventArgs e)
{
MessageBox.Show("Changed!");
}
I'd recommend digging a bit more into reflection and see what you can find in the TextBox class by using typeof(TextBox).GetFields / .GetProperties because somewhere there must be a field or property which you can change to bypass your setter method triggering the event.
Hope this helps.

C# Hashtable of FileSystemWatchers

I'm trying to create a hashtable of filesystem watchers. This is to keep a running record of active filesystemwatchers with the directories they are watching as keys. Then via a form the user can add and delete folders to watch, which are visible in a listview or something.
My main problem is how to "keep" the hash table between methods and classes. I'm a bit of a novice to C# and it doesn't seem to work the way I'm used to in VB.NET.
So I have (stripped down to simplify):
public partial class MainForm : Form
{
public static Hashtable globalHashTable;
public MainForm()
{
InitializeComponent();
}
public void Button1Click(object sender, EventArgs e)
{
FileSystemWatcher watcher1 = new FileSystemWatcher(#"C:\");
globalHashTable.Add(#"C:\",watcher1);
}
}
}
So that a filesystemwatcher is added to the hashtable. However since the globalhashtable is static (?) this won't work. Making it non static means I have to create an instance of it when the buttton is pressed, so I have a new one each time as it's not "kept". My problem is how to keep a table in memory between methods and classes.
I'm fairly sure I've majorly misunderstood something as I'm new to all of this. Also I doubt this is even a half decent way to do this, if anyone has a better way, then please go ahead!
Thanks,
Matt
Expanding on #Ron Beyer's suggestion you can do something like this:
private Dictionary<string, FileSystemWatcher> _fileSystemWatcherMap;
public MainForm()
{
InitializeComponent();
_fileSystemWatcherMap = new Dictionary<string, FileSystemWatcher>();
}
public void Button1Click(object sender, EventArgs e)
{
string pathToWatch = #"C:\"; // Must be a different path each time otherwise will throw
var watcher = new FileSystemWatcher(pathToWatch);
_fileSystemWatcherMap.Add(pathToWatch, watcher);
}
This way all methods in the MainForm can access the file watchers.
If you need to share this among forms that are created from MainForm you can simply pass this data before showing the dialog.
If the other form is created in a different way the you can create a static class like this:
public static class FileWatcherMap
{
private static Dictionary<string, FileSystemWatcher> _fileSystemWatcherMap = new Dictionary<string,FileSystemWatcher>();
public static void AddWatcher(string path, FileSystemWatcher fsw)
{
_fileSystemWatcherMap.Add(path, fsw);
}
public static void RemoveWatcher(string path)
{
_fileSystemWatcherMap.Remove(path);
}
}
then in the click handler you add the watcher to this list:
public void Button1Click(object sender, EventArgs e)
{
string pathToWatch = #"C:\"; // Must be a different path each time otherwise will throw
var watcher = new FileSystemWatcher(pathToWatch);
FileWatcherMap.AddWatcher(pathToWatch, watcher);
}
Now FileWatcherMap class would be accessible from any other form

Categories

Resources