WPF and C#: Trouble with Classes and Interfaces - c#

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.

Related

DataGridView Showing Empty Rows when assigned to a List of Type containing internal properties

I experienced an issue which kind of opposed my basic C# concepts on Access Modifiers. So i build a Sample application reproducing Same Scenario.
There is a Parent Form with Click Button on the event of which a new Form spawns with a DataGridView. The Data Source is static string,string and the properties are internal as i am using everything in the same project.
To my surprise, there were 5 rows made since i had 5 data items bind to that grid but were just empty Rows. Then i made the properties as public and i was able to get them. All the Rows were populated with correct Data the second Time.
Parent Form
namespace SampleApp{
public partial class Form1 : Form
{
List<ItemModel> modelList = new List<ItemModel>();
public Form1()
{
InitializeComponent();
}
private void btnClick_Click(object sender, EventArgs e)
{
LoadData();
using (SeperateWindow window = new SeperateWindow(modelList))
{
window.PopulateGrid();
if(window.ShowDialog() == DialogResult.OK)
{
}
}
}
public void LoadData()
{
for(int i= 0; i < 5; i++)
{
ItemModel item = new ItemModel($"Name { i}", i.ToString());
modelList.Add(item);
}
}
}
Model Class
namespace SampleApp.Model{
sealed class ItemModel
{
internal string Textvalue { get; set; }
internal string ID { get; set; }
internal ItemModel(string text,string id)
{
Textvalue = text;
ID = id;
}
}
Child Form Containing GridView
namespace SampleApp.Model{
public partial class SeperateWindow : Form
{
List<ItemModel> _modelList = new List<ItemModel>();
internal SeperateWindow(List<ItemModel> modelList)
{
_modelList = modelList;
InitializeComponent();
}
public void PopulateGrid()
{
dataGridView1.DataSource = _modelList;
}
}
My issue is since everything is in the same project, and model class and child form are in the same Folder too, then also why am i getting Empty Rows? Given that it is working fine if i make them public.
From MSDN
The properties you use as binding source properties for a binding must be public properties of your class. Explicitly defined interface properties cannot be accessed for binding purposes, nor can protected, private, internal, or virtual properties that have no base implementation.
Also please refer to this question, in which the difference between public and internal keywords in databinding has been discussed.

Trouble passing new object back to main form - C# .NET

I am new to C# and the .NET framework. I am trying to create an array of chocolate orders that shows up as a list on the main screen(MainNavigation). I have a chocolate class and a form(form1) where the user can select which type of chocolate and a new chocolate object is created. I am stuck on trying to pass the new object back to the main form and then showing it in a list on the main form.
MainNavigation form.... I would like value to be the orders that the user creates in form1.
using System;
using System.Windows.Forms;
namespace GatesCandyStore
{
public partial class MainNavigation : Form
{
public MainNavigation()
{
InitializeComponent();
Chocolate[] chocolates = new Chocolate[100];
for (int runs = 0; runs < 100; runs++)
{
chocolates[runs] = value;
}
}
private void btnProcessCandySelection_Click(object sender, EventArgs e)
{
string candy = comboBoxCandySelection.SelectedItem.ToString();
Form1 aForm1 = new Form1(textBoxName.Text, candy);
aForm1.ShowDialog();
}
}
}
Form1 where the user creates a new chocolate order.... Close(newChocolate); does not work.
using System;
using System.Windows.Forms;
namespace GatesCandyStore {
public partial class Form1 : Form
{
Chocolate newChocolate = new Chocolate();
public Form1(string name, string candy)
{
InitializeComponent();
string str = name + " selected : ";
label1.Text = str;
Console.WriteLine(name + " selected : " + candy);
}
private void Form1_Load(object sender, EventArgs e)
{
newChocolate.Flavor = comboBoxChocolateSelection.SelectedItem.ToString();
newChocolate.Cost = 12.5;
newChocolate.GiftWrap = true;
newChocolate.Quantity = 2;
}
private void button1_Click(object sender, EventArgs e)
{
Close(newChocolate);
}
} }
Chocolate Class
namespace GatesCandyStore
{
public class Chocolate
{
#region Fields
public string flavor;
public double cost;
public bool giftWrap;
public int quantity;
#endregion End of Fields
#region Constructors
public Chocolate(string flavor, double cost, bool giftWrap, int quantity)
{
Flavor = flavor;
Cost = cost;
GiftWrap = giftWrap;
Quantity = quantity;
}
#endregion End of Constructors
#region Properties
public string Flavor { get; set; }
public double Cost { get; set; }
public bool GiftWrap { get; set; }
public int Quantity { get; set; }
#endregion End Properties
}
}
here In your question, I'm still not clear what you are doing candy and name in your Form1's constructor.
Assuming you know what you are doing with them and taking your main concern "How to pass newly created object back to main form", I'm suggesting you some correction.
Create A Model class
public delegate void ChocolateAddedEventHander(Chocolate newChocolate);
public class Model
{
//An Event which will be raised when you add new chocolate
public event ChocolateAddedEventHander ChocolateAdded;
//If at any point of program you need whole list of added chocolates
public List<Chocolate> ChocolateList = new List<Chocolate>();
public void AddChocolateInList (Chocolate chocolate)
{
ChocolateList.Add(chocolate);
if (ChocolateAdded != null)
ChocolateAdded(chocolate);
}
}
Your Chocolate class will same as you have shown here.
Make following changes in Program.cs
static void Main()
{
Model modelObj = new Model();
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new MainNavigation(modelObj));
}
As you can see in above code, MainNavigation is accecpting an object of Model class now.
MainNavigation form.
In code you shown in question, you seem to be doing something in constructor of MainNavigation , if array of Chocolate is containing all added chocolates you can use m_modelObj.ChocolateList there. But as I'm not clear what is value there. I'm not adding that part in my answer.
public partial class MainNavigation : Form
{
private Model m_modelObj;
public MainNavigation(Model modelObj)
{
InitializeComponent();
m_modelObj = modelObj;
//subscribing an even of Model class,
//this will handle your logic what you want to perform on adding new Chocolate
m_modelObj.ChocolateAdded += m_modelObj_ChocolateAdded;
}
void m_modelObj_ChocolateAdded(Chocolate newChocolate)
{
//perform your task what you want to do with newly added chocolate
//if you want whole list of chocolates
List<Chocolate> chocolateList = m_modelObj.ChocolateList;
}
private void btnProcessCandySelection_Click(object sender, EventArgs e)
{
string candy = comboBoxCandySelection.SelectedItem.ToString();
Form1 aForm1 = new Form1(textBoxName.Text, candy, m_modelObj);
aForm1.ShowDialog();
}
}
Note that, now Form1 will accept three parameters, third as in object of Model class
Form1 where you are adding chocolate.
public partial class Form1 : Form
{
Model m_model;
public Form1(string name, string candy, Model modelObj)
{
InitializeComponent();
m_model = modelObj;
//Not sure what you are doing here, but it will work
string str = name + " selected : ";
label1.Text = str;
Console.WriteLine(name + " selected : " + candy);
}
private void button1_Click(object sender, EventArgs e)
{
//adding new chocolate to list;
Chocolate newChocolate = new Chocolate(comboBoxChocolateSelection.SelectedItem.ToString(), 12.5, true, 2);
m_model.AddChocolateInList(newChocolate);
this.Close();
}
}
One more thing I will suggest, naming of class and member should be proper, It will help a lot while debugging and code reviewing.

How to pass through data from a child form into the parent form without refreshing the list

I am doing a school project in creating a POS system and i need to pass in through the same order list throughout the different form.The problem is everytime i return to the main menu, the list refreshes although i still need the data is there anyway to prevent this from happening?
public partial class Main : Form
{
List<string> order = new List<string>();
public Main()
{
InitializeComponent();
}
public Main(List<string> order)
{
InitializeComponent();
this.order = order;
}
this is the start code for the main menu
And this is the code to add the items from thelistbox to the list
private void confirmbtn_Click(object sender, EventArgs e)
{
order.AddRange(temp_order);
if (pendinglist.Items.Count != 0)
{
MessageBox.Show("Your Items have been added! yay!", "That Sushi", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
else
{
MessageBox.Show("No items was selected.", "That Sushi", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
pendinglist.Items.Clear();
}
Thank you in advance for your help.Please let me know if anything else is wrong with the code too. again thank you for the help
You are probably closing the form and re-creating it every time you open it again, and since these orders are inside it, obviously the previous content is lost.
Solution : that list of orders should be outside main form
Here's a really simple pattern for you, it's really primitive but it works and should be enough/plausible for your homework : a static class
public static class MyModel
{
public static Order Order { get; set; }
public static void NewOrder()
{
Order = new Order();
}
}
public class Order
{
public Order()
{
Products = new List<Product>();
}
public List<Product> Products { get; set; }
public void AddProduct(Product product)
{
Products.Add(product);
}
}
public class Product
{
public string Name { get; set; }
public decimal Price { get; set; }
}
And here's how you'd use it from a form:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void buttonNewOrder_Click(object sender, EventArgs e)
{
MyModel.NewOrder();
}
private void buttonAddProduct_Click(object sender, EventArgs e)
{
var product = new Product();
product.Name = "PS4";
product.Price = 1000000000.0m;
MyModel.Order.AddProduct(product);
}
private void buttonShowProductsInOrder_Click(object sender, EventArgs e)
{
var order = MyModel.Order;
var products = order.Products;
var builder = new StringBuilder();
foreach (var product in products)
{
var format = string.Format("Name: {0}, Price: {1}", product.Name, product.Price);
builder.AppendLine(format);
}
MessageBox.Show(builder.ToString());
}
}
Order will not vanish and will be accessible from many forms.
Obviously it's a pretty bad practice, gurus would use MVP (model view presenter), MVC (model view controller) or whatever pattern along a database behind, but there's little point to roll out such design for your question. You can/should take a look at what these concepts are, however.
Good luck !

ASP.Net using RadioButtonList / CheckBoxList

I am finishing up a program for my ASP.Net class for an ice cream shop done as a web app, the main page looks like THIS. I need two classes for the Ice Cream ordering:
IceCreamOrder
IceCreamOrderList
currently the only code in my IceCreamOrderList class is
public class IceCreamOrderList
{
List<IceCreamOrder> ICorders = new List<IceCreamOrder>();
public List<IceCreamOrder> ListofOrders { get { return ICorders; } }
public void Add(IceCreamOrder data) { ICorders.Add(data); }
and nothing in the IceCreamOrder class. My code on/around the Submit Order button IS
I've done a simpler program that was just taking text entered into a textbox and adding to a list, but my biggest problem is figuring out how to determine what control is set to true and then add them into a list. I have names for each of the controls as follows:
FlavorsList
ToppingsList
ServeList
Any help and guidance toward finishing this would be most greatly appreciated. and Thanks in advance
If you already using RadioButtonList and CheckBoxList into your order page, you should change event handler btnSubmit_Click as follows:
protected void btnSubmit_Click(object sender, EventArgs e)
{
IceCreamOrder order = new IceCreamOrder();
order.Flavor = FlavorsList.SelectedValue;
foreach (ListItem listItem in ToppingsList.Items)
{
if (listItem.Selected)
order.Toppings.Add(listItem.Value);
}
order.Serve = ServeList.SelectedValue;
// add order to order list
}
public class IceCreamOrder
{
public string Flavor { get; set; }
public List<string> Toppings { get; set; }
public string Serve { get; set; }
public IceCreamOrder()
{
Toppings = new List<string>();
}
}

How to use same instance name with multiple classes

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.

Categories