I'm new to c# and I think I want to do this but maybe I don't and don't know it!
I have a class called SyncJob. I want to be able to create an instance to backup files from My Documents (just an example). Then I'd like to create another instance of SyncJob to backup files in another folder. So, in other words, I could have multiple instances of the same class in memory.
I'm declaring the object var first in my code so it is accessible to all the methods below it.
My question is: while using the same instance name will create a new instance in memory for the object, how can I manage these objects? Meaning, if I want to set one of the properties how do I tell the compiler which instance to apply the change to?
As I said in the beginning, maybe this is the wrong scheme for managing multiple instances of the same class...maybe there is a better way.
Here is my prototype code:
Form1.cs
namespace Class_Demo
{
public partial class Form1 : Form
{
BMI patient; // public declarition
public Form1()
{
InitializeComponent();
}
private void btnCreateInstance1_Click(object sender, EventArgs e)
{
patient = new BMI("Instance 1 Created", 11); // call overloaded with 2 arguments
displayInstanceName(patient);
}
private void displayInstanceName(BMI patient)
{
MessageBox.Show("Instance:"+patient.getName()+"\nwith Age:"+patient.getAge());
}
private void btnCreateInstance2_Click(object sender, EventArgs e)
{
patient = new BMI("Instance 2 Created", 22); // call overloaded with 2 arguments
displayInstanceName(patient);
}
private void btnSetNameToJohn_Click(object sender, EventArgs e)
{
// this is the issue: which instance is being set and how can I control that?
// which instance of patient is being set?
patient.setName("John");
}
private void btnDisplayNameJohn_Click(object sender, EventArgs e)
{
// this is another issue: which instance is being displayed and how can I control that?
// which instance of patient is being displayed?
displayInstanceName(patient);
}
}
}
Class file:
namespace Class_Demo
{
class BMI
{
// Member variables
public string _newName { get; set; }
public int _newAge { get; set; }
// Default Constructor
public BMI() // default constructor name must be same as class name -- no void
{
_newName = "";
_newAge = 0;
}
// Overload constructor
public BMI(string name, int age)
{
_newName = name;
_newAge = age;
}
//Accessor methods/functions
public string getName()
{
return _newName;
}
public int getAge()
{
return _newAge;
}
public void setName(string name)
{
_newName = name;
}
}
}
You can have public List<BMI> PatientList { get; set; } instead of BMI patient;
if you have one patient you not sure which item accessing and when you assign it will replace previous one
public List<BMI> PatientList { get; set; }
public Form1()
{
InitializeComponent();
PatientList = new List<BMI>();
}
with list of BMI you can add items like below
PatientList.Add(new BMI("Instance 1 Created", 11));
PatientList.Add(new BMI("Instance 2 Created", 22));
if you need to set name of instance 1, you can get the item by index
PatientList[0].setName("John");
Or you can find the patient by one of the property by loop though the PatientList
if you need to display the patient details of "John", by using LINQ
displayInstanceName(PatientList.FirstOrDefault(p=>p.Name =="John"));
If you need to manage a collection of instances, use a List<BMI> or similar. The generic List<T> class can hold (almost) any type of object, is easy to work with, etc. It's also a vital part of the .NET toolkit that you will use many, many times.
Also, consider rewriting your BMI class to use properties more effectively:
class BMI
{
public string NewName { get; set; }
public int NewAge { get; protected set; }
public BMI()
: this("", 0)
{ }
public BMI(string name, int age)
{
NewName = name;
NewAge = age;
}
}
The accessor methods are not required unless you need them for interop with some other system. Using modifiers on the get and set accessors on the properties themselves you can make public-read/private-write properties, etc.
Related
I'm very new to c#, I started a few days ago, so please excuse me if it is basic.
I have two forms, the first one is like a login page, where someone enters their name. On my "Info.cs" class, it reads this name via a setter, into a variable, and my Getter called "GetCardName" returns this Name. I now made a new form where I want to access this name via the GetCardName getter, just dont know how too. Heres the code :
Here is some of the "info.cs" class code:
private string CardName { get; set; } = "";
public string GetCardName()
{
return this.CardName;
}
public void SetName(string name = "")
{
this.CardName = name;
}
And here is the code from the other form that is just trying to call GetCardName():
private void Form2_Load(object sender, EventArgs e)
{
lblWelcome.Text = Info.GetCardName();
}
When creating Form2 you need also pass it reference to the other form to get its properties.
So when creating and showing Form1 you should also create Form2 to pass that reference. Example (not tested) code:
var form1 = new Form1();
var form2 = new Form2(form1);
form1.Show();
and Form2 should be like:
public class Form2
{
private Form1 _form1;
public Form2(Form1 form1)
{
_form1 = form1;
// ... other initialization code
}
// ... other class declarations
}
General solution is: you need to persist reference to the Form1 being shown to the user and then pass that reference to Form2 whenever you create it.
You have two options :
you can create an instance of the class that you want to call
EX : Info infoVar = new Info(); (now you can use infoVar to call any methods of the Info.cs class)
you can make Info class a STATIC class (probably not what you want to do, but still helpful for the future perhaps) This makes it possible to call the info class directly without having to create a variable of that class but has some drawbacks. (more info here)
There is few ways to achieve what you want:
Public static property:
public class Info
{
public static string CardName { get; set; } = string.Empty;
}
You can access it or set value to it directly by:
private void Form2_Load(object sender, EventArgs e)
{
// Set
Info.CardName = "Some name";
// Get
lblWelcome.Text = Info.CardName;
}
Public non-static property:
public class Info
{
public string CardName { get; set; } = string.Empty;
}
You can access it or set value to it directly too, but need to create Info class instance before:
private void Form2_Load(object sender, EventArgs e)
{
Info info = new Info();
// Set
info.CardName = "Some name";
// Get
lblWelcome.Text = info.CardName;
}
Private static field with separated public static Get and Set methods:
public class Info
{
private static string cardName = string.Empty;
public static string GetCardName()
{
return cardName;
}
public static void SetCardName(string name = "")
{
cardName = name;
}
}
You can access GetCardName and SetCardName without creating Info class instance:
private void Form2_Load(object sender, EventArgs e)
{
// Set
Info.SetCardName("Some name");
// Get
lblWelcome.Text = Info.GetCardName();
}
Private non-static field with separated public non-static Get and Set methods:
public class Info
{
private string cardName = string.Empty;
public string GetCardName()
{
return cardName;
}
public void SetCardName(string name = "")
{
cardName = name;
}
}
You can access GetCardName and SetCardName after creating Info class instance:
private void Form2_Load(object sender, EventArgs e)
{
Info info = new Info();
// Set
info.SetCardName("Some name");
// Get
lblWelcome.Text = info.GetCardName();
}
Difference between fields and properties was pretty nice explained here: What is the difference between a field and a property?. In short, properties are "wrappers" over fields, which usually are private and you can't access to them directly or modify. It is a part of Member Design Guidelines. Also properties allow to add some validations through property setter to be sure that valid value is stored at cardName field, e.g.:
public class Info
{
private string cardName = string.Empty;
public string CardName
{
get => cardName;
set
{
// Check that value you trying to set isn't null
if (value != null)
cardName = value;
// Or check that name is not too short
if (value.Length >= 3) // Card name should be at least of 3 characters
cardName = value;
}
}
}
info myInfo=new info();
lblWelcome.Text = myInfo.GetCardName();
As the title states I'm looking for a way how to solve following:
namespace Test
{
public partial class SetVariable : Form
{
public string test = "";
public SetVariable()
{
InitializeComponent();
}
private void button2_Click(object sender, EventArgs e)
{
test = "test"
}
}
}
and in a second form I want to read it, but also want to restrict the user from making any changes to the variable (by accident or on purpose), as all the variables are only to be set in the SetVariable Form, and then be used across all other forms that are planned.
namespace Test
{
public partial class GetVariable : Form
{
public GetVariable()
{
InitializeComponent();
}
private void button2_Click(object sender, EventArgs e)
{
if (SetVariable.test == "test")
{ //doSomething;}
}
}
}
}
If I make the variable a public readonly, than I cant write to it in the form where its supposed to be written. Is there another way of initalizing a global variable which is only changable in the form where its created?
Thanks in advance.
Change:
public string test = "";
to:
public string test { get; private set; }
Also see https://stackoverflow.com/a/3847982/34092 .
Make public property private set.
public partial class SetVariable : Form
{
public string Test {get; private set;}
//Just in case if you want to set value to Test property from other class.
//If you want Test property readonly to other
//class you don't need this method.
public void SetTest(string test)
{
Test = test;
}
}
public class Main
{
SetVariable sv = new SetVariable();
sv.SetTest("Some Value"); //unwanted to scenario. Just in case if you want
//read Test value
string testValue = sv.Test; //allowed
//set Test value
sv.Test = "Other value"; //not allowed.
}
I have created a simple form "people" and there is another file "Information.cs"
In main for assign text box "txt_lname" value to a variable
String lastname = txt_lname.Text;
Then I want to use this value within "information class" (It is a thread class)
How can I use it ?
(I have commented the place I wanted to use that value)
Main Form
namespace users
{
public partial class people : Form
{
public people()
{
InitializeComponent();
}
private void btn_login_Click(object sender, EventArgs e)
{
String lastname = txt_lname.Text;
}
}
}
Information Class
namespace users
{
class Information
{
int[] idno = new int[10];
int[] age = new int[10];
string[] fname = new string[10];
// Here I want to assign txt_lname.Text value to a variable
lastname = txt_lname.Text; // This code is not working
public void run()
{
while (true)
{
for (int i = 0; i < 600; i++)
{
//Some code here
try
{
Thread.Sleep(100);
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString(), "Error", MessageBoxButtons.OKCancel, MessageBoxIcon.Error);
}
}
}
}
}
}
* Can I use value of a variable within run method in thread class ? if cannot then why ?
You have to create an instance of Information on your form and then pass the data to said instance. A classes' code won't magically be executed just because you added it to your project, you have to actually create an instance of the class.
So lets create and initialize an instance of Information on the form:
public partial class people : Form
{
private Information _information;
public people() {
InitializeComponent();
_information = new Information();
}
}
You can now pass stuff to your instance of Information. But to do that you need a way to pass it, or in this case Information needs a way to receive a LastName. There's more than one way to do this but a common approach is to expose a LastName property on Information:
public class Information
{
...
public string LastName { get; set; }
...
}
And now you can pass the value to the LastName property:
private void btn_login_Click(object sender, EventArgs e) {
_information.LastName = txt_lname.Text;
}
Note: When you want to execute the run method on Information you'll do it through the instance just like when you're setting the LastName:
private void btn_run_click(object sender, EventArgs e) {
_information.run();
}
Make the Information class Static
public static class Information
And in your main form you can use it like
Information.LastName = txt_lname.Text;
But you also need to declare LastName as property. So add
public string LastName {get; set;}
into your Information Class.
I'm working on a bank account program. I'm using a base class, an interface, and two derived classes in addition to my main window class. I am making a WPF application, and so far I am able to populate a ListBox with an ArrayList of class objects in that app just fine. However, when I go to modify any of the objects in the ArrayList, I'm running into difficulty repopulating the ListBox correctly. Any help will be greatly appreciated!
This is my MainWindow code:
public partial class MainWindow : Window
{
ArrayList bankAccountList = new ArrayList();
BankAccount savingsAccount = new SavingsAccount("New", "Account", "newaccount");
BankAccount checkingAccount = new CheckingAccount("New", "Account", "newaccount");
IAccount iAccount;
string typeOfAccount = "";
public MainWindow()
{
InitializeComponent();
}
//When the user pushes the "Add A Saving Account" button, a new savings account is added to the ArrayList and displayed in the app.
private void btnAddAccount_Click(object sender, RoutedEventArgs e)
{
iAccount = (IAccount)savingsAccount;
savingsAccount.Deposit(0.00m);
bankAccountList.Add(savingsAccount);
lbxExistingAccounts.Items.Add(iAccount.AccountInformation());
typeOfAccount = "savings";
}
//When the user pushes the "Add A Checking Account" button, a new checking account is added to the ArrayList and displayed in the app.
private void btnAddCheckingAccount_Click(object sender, RoutedEventArgs e)
{
iAccount = (IAccount)checkingAccount;
checkingAccount.Deposit(0.00m);
bankAccountList.Add(checkingAccount);
lbxExistingAccounts.Items.Add(iAccount.AccountInformation());
typeOfAccount = "checking";
}
//When the user pushes the "Delete Account" button, the account is removed, and this change is shown in the app.
private void btnDeleteAccount_Click(object sender, RoutedEventArgs e)
{
lbxExistingAccounts.Items.RemoveAt(lbxExistingAccounts.Items.IndexOf(lbxExistingAccounts.SelectedItem));
}
//The user can push the "Submit Changes" button to submit his or her changes to the number and name of the account.
private void btnSubmitChanges_Click(object sender, RoutedEventArgs e)
{
try
{
for (int index = 0; index < bankAccountList.Count; index++)
{
if (index == lbxExistingAccounts.SelectedIndex)
{
if (typeOfAccount == "savings")
{
savingsAccount.AccountNumber = tbxAccountNumber.Text;
savingsAccount.AccountOwnerFirstName = tbxFirstName.Text;
savingsAccount.AccountOwnerLastName = tbxLastName.Text;
}
else if (typeOfAccount == "checking")
{
checkingAccount.AccountNumber = tbxAccountNumber.Text;
checkingAccount.AccountOwnerFirstName = tbxFirstName.Text;
checkingAccount.AccountOwnerLastName = tbxLastName.Text;
}
}
}
lbxExistingAccounts.Items.Clear();
foreach (object accountObject in bankAccountList)
{
lbxExistingAccounts.Items.Add(accountObject);
}
}
catch (FormatException)
{
MessageBox.Show("You may enter changes as letters, numbers, or both.");
}
}
This is my Interface code:
interface IAccount
{
void SetAccountBalance(decimal accountBalance);
string AccountInformation();
}
This is my base class code:
abstract class BankAccount
{
public string AccountNumber { get; set; }
public string AccountOwnerFirstName { get; set; }
public string AccountOwnerLastName { get; set; }
public decimal AccountBalance { get; set; }
public decimal AnnualInteresetRate { get; set; }
public string TypeOfAccount { get; set; }
public BankAccount(string accountOwnerFirstName, string accountOwnerLastName, string accountNumber)
{
AccountOwnerFirstName = accountOwnerFirstName;
AccountOwnerLastName = accountOwnerLastName;
AccountNumber = accountNumber;
}
public abstract void Deposit(decimal amount);
public abstract void Withdraw(decimal amount);
public decimal CalculateInterest()
{
return (this.AccountBalance * this.AnnualInteresetRate) / 100;
}
}
This is one of my derived classes. I made both pretty much the same.
class SavingsAccount : BankAccount, IAccount
{
public SavingsAccount(string accountOwnerFirstName, string accountOwnerLastName, string accountNumber)
: base(accountOwnerFirstName, accountOwnerLastName, accountNumber)
{
AnnualInteresetRate = 0.95m;
}
public override void Deposit(decimal amount)
{
AccountBalance = AccountBalance + amount;
}
public override void Withdraw(decimal amount)
{
AccountBalance = AccountBalance - amount;
}
public void SetAccountBalance(decimal accountBalance)
{
AccountBalance = accountBalance;
}
public string AccountInformation()
{
return "Savings Account \n " + AccountOwnerFirstName + " " + AccountOwnerLastName + ", Account#: " + AccountNumber + ", Balance: $" + AccountBalance;
}
}
It looks like you are just starting out, which is good, because you are going to want to rework some things.
You don't need to cast your derived objects to their base types. This type of casting is called "upcasting" and automatically works without any casting whatsoever.
The code you posted is highly "WinForms-ish" and this is not a good approach in WPF. Start with making your account list an ObservableCollection and binding your ListBox's ItemsSource to it.
The property would look like:
public ObservableCollection<BankAccount> Accounts {get; set;}
Which should actually use INotifyPropertyChanged omitted for brevity, and the binding:
<ListBox "ItemsSource={Binding Accounts}"/>
Of course, that should go in a view model class, but you could start with your code-behind by setting:
DataContext = this;
Once that is working all your text boxes should be bound to properties of the data objects or of the view itself.
Here is a great tutorial on using MVVM with WPF (MSDN). Trust me, using this design pattern will make your work with WPF immensely easier, more extensible, and you will find that WPF was basically designed to use it. Using a WinForms approach is just going to cause you pain.
Please let me know if I can assist further or clarify anything!
I would recommend using ObservableCollection for your collection, so that any changes made to the items would be reflected visually without any additional work. It wouldn't require any significant changes. All you would do is switch your ArrayList for an ObservableCollection.
http://msdn.microsoft.com/en-us/library/ms668604(v=vs.110).aspx
I would also highly recommend implementation of INotiftyPropertyChanged interfrace for your objects, so that when account information is changed, appropriate subscribers are notified.
http://msdn.microsoft.com/en-us/library/vstudio/system.componentmodel.inotifypropertychanged
I've been using both of those extensively with my WPF applications.
I need to load a User Control in my panel1 inside Form1.cs, the problem is that the UserControl (AudioPlaybackPanel) contains an ImportingConstructor ([ImportMany]IEnumerable<>) and I can't figure out what two arguments I should have in the Form1 AudioPlaybackPanel(????).
The error I get is: 'NAudio.App.AudioPlaybackPanel' does not contain a constructor that takes 0 arguments
Here is the Form1.cs
namespace NAudio.App
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void panel1_Paint(object sender, PaintEventArgs e)
{
}
private void button1_Click(object sender, EventArgs e)
{
AudioPlaybackPanel myPanel = new AudioPlaybackPanel(????);
panel1.Controls.Add(myPanel);
}
}
}
And this is my User Control Panel (AudioPlaybackPanel.cs):
namespace NAudio.App
{
[Export]
public partial class AudioPlaybackPanel : UserControl
{
private IWavePlayer waveOut;
private string fileName = null;
private WaveStream fileWaveStream;
private Action<float> setVolumeDelegate;
[ImportingConstructor]
public AudioPlaybackPanel([ImportMany]IEnumerable<IOutputDevicePlugin> outputDevicePlugins)
{
InitializeComponent();
LoadOutputDevicePlugins(outputDevicePlugins);
}
[ImportMany(typeof(IInputFileFormatPlugin))]
public IEnumerable<IInputFileFormatPlugin> InputFileFormats { get; set; }
private void LoadOutputDevicePlugins(IEnumerable<IOutputDevicePlugin> outputDevicePlugins)
{
comboBoxOutputDevice.DisplayMember = "Name";
comboBoxOutputDevice.SelectedIndexChanged += new EventHandler(comboBoxOutputDevice_SelectedIndexChanged);
foreach (var outputDevicePlugin in outputDevicePlugins.OrderBy(p => p.Priority))
{
comboBoxOutputDevice.Items.Add(outputDevicePlugin);
}
comboBoxOutputDevice.SelectedIndex = 0;
}
void comboBoxOutputDevice_SelectedIndexChanged(object sender, EventArgs e)
{
panelOutputDeviceSettings.Controls.Clear();
Control settingsPanel;
if (SelectedOutputDevicePlugin.IsAvailable)
{
settingsPanel = SelectedOutputDevicePlugin.CreateSettingsPanel();
}
else
{
settingsPanel = new Label() { Text = "This output device is unavailable on your system", Dock=DockStyle.Fill };
}
panelOutputDeviceSettings.Controls.Add(settingsPanel);
}
private IOutputDevicePlugin SelectedOutputDevicePlugin
{
get { return (IOutputDevicePlugin)comboBoxOutputDevice.SelectedItem; }
}
// The rest of the code continues from here on...
}
}
Here is the Interface:
namespace NAudio.App
{
public interface IOutputDevicePlugin
{
IWavePlayer CreateDevice(int latency);
UserControl CreateSettingsPanel();
string Name { get; }
bool IsAvailable { get; }
int Priority { get; }
}
}
And just in case, here is one of the plugins:
DirectSoundOutPlugin.cs
namespace NAudio.App
{
[Export(typeof(IOutputDevicePlugin))]
class DirectSoundOutPlugin : IOutputDevicePlugin
{
private DirectSoundOutSettingsPanel settingsPanel;
private bool isAvailable;
public DirectSoundOutPlugin()
{
this.isAvailable = DirectSoundOut.Devices.Count() > 0;
}
public IWavePlayer CreateDevice(int latency)
{
return new DirectSoundOut(settingsPanel.SelectedDevice, latency);
}
public UserControl CreateSettingsPanel()
{
this.settingsPanel = new DirectSoundOutSettingsPanel();
return this.settingsPanel;
}
public string Name
{
get { return "DirectSound"; }
}
public bool IsAvailable
{
get { return isAvailable; }
}
public int Priority
{
get { return 3; }
}
}
}
Please help!
The error doesn't say it expects two arguments... it just says it doesn't take 0.
The constructor expects a single parameter - an IEnumerable<IOutputDevicePlugin>:
public AudioPlaybackPanel([ImportMany]IEnumerable<IOutputDevicePlugin> outputDevicePlugins)
{
...
}
You need to find something that implements the IOutputDevicePlugin interface and pass a collection of it, even if it's just an empty collection. (Passing null to the constructor will allow it to compile but will throw a runtime exception when you hit the loop in LoadOutputDevicePlugins.)
Considering the update to your question, something like this will get you up and running (although I doubt it means very much to pass an empty list):
var myPanel = new AudioPlaybackPanel(new List<DirectSoundOutPlugin>());
panel1.Controls.Add(myPanel);
It's worth asking whether you actually need to copy AudioPlaybackPanel.cs from the NAudio demo in its entirety. The reason it has this constructor is that it tries to demonstrate how you can use each and every one of NAudio's IWavePlayer implementations. But in a normal real-world application you would just select the one that was most appropriate for your use. e.g.
this.waveOut = new WaveOut();
waveOut.Init(new AudioFileReader("my file.mp3");
waveOut.Play();
So there's no need to incorporate the plug-in architecture from that particular demo, if all you want is just to play audio files.