my situation is that I need to add up all prices of items in a Listbox and have the total price in the textbox. I am using c# WinForms. This what I have so far but it is not working correctly by showing total, it is showing the price of the last item I press and not the total of all items
public class SelectedPizza
{
public string Size { get; set; }
public string Name { get; set; }
public string Price { get; set; }
public string Format() => $"{Size} {Name} {Price}"; // Format inside Listbox
}
private void ButtonSizeClick(object sender, EventArgs e)
{
var button = (Button)sender;
_selectedPizza.Size = button.Tag.ToString(); // Adds pizza size to order listbox
if (_selectedPizza.Name != null)
_selectedPizza.Price = GetPrice(_selectedPizza.Size);
}
private void ButtonNameClick(object sender, EventArgs e)
{
var button = (Button)sender;
_selectedPizza.Name = button.Tag.ToString();
if (_selectedPizza.Size != null)
_selectedPizza.Price = GetPrice(_selectedPizza.Size);
listBox1.Items.Add(_selectedPizza.Format()); // Adds pizza name to order listbox
}
private string GetPrice(string sSize)
{
string sPrice = "0.00";
if (sSize == "Large")
sPrice = "11.90";
TxtbxTotal.Text = sPrice; // Displays price in order total textbox
return sPrice;
}
I won't go deep into your problem but I will address the main question.
Finally, you want to sum up the Listbox items.
So to do that, add the below piece to your code:
for (int i = 0; i < listboxname.Items.Count; i++)
{
sum += Convert.ToDecimal(listboxname.Items[i].Text);
}
textBoxtotal.Text = Convert.ToString(sum);
I roughly read the entire code and couldn't find any event relating to total of the listbox. Maybe you missed to write the click event of the total button.
you seem writing a decent program but you are killing it doing this listBox1.Items.Add(_selectedPizza.Format());
you need to use this technology
public interface IFoodItem // all list box items must have certain properties
{
public int Id { get; set; }
string Name { get; set; }
string Price { get; set; }
public string Display { get; }
}
public class Pizza : FoodItem
{
public int Id { get; set; }
public string Size { get; set; }
public string Name { get; set; }
public string Price { get; set; }
public string Display
{
get { return $"{Size} {Name} {Price}"; }
}
}
// another example
public class Sandwhich : FoodItem
{
// . . . . .
}
//. . . . . .
// Get the list
var listData = new List<IFoodItem>();
listData.Add( . . . );
// . . . . .
// this is how you should set the list - DataSource always last
lstList.ValueMember = "Id";
lstList.DisplayMember = "Display";
lstList.DataSource = listData;
// Get selected item info
IFoodItem item = (IFoodItem)lstList.SelectedItem;
txtItemPrice.Text = item.Price;
// Get Total using LINQ
txtTOTALPrice.Text = ((List<IFoodItem>)lstList.DataSource).Sum(item => item.Price);
UPDATE
From OP: "i have to stick to a specification for school, so all I need to know is how to do it with the code that I have got already"
your value is public string Format() => $"{Size} {Name} {Price}"; added here listBox1.Items.Add(_selectedPizza.Format());
In this case you do this (your sum is third part of the string item in the list)
var sum = 0m;
foreach (object item in listBox1.Items)
{
string[] itemParts =
((string)item).Split(default(char[]), StringSplitOptions.RemoveEmptyEntries);
// price is last index
sum += decimal.Parse(itemParts[itemParts.Length - 1]); // get last item
}
txtTotal.Text = sum;
Here is the working unit test
using System;
using System.Collections.Generic;
public class Program
{
public static void Main()
{
var list = new List<object>();
list.Add("aaa xxx 5 10.99");
list.Add("bbb yyy 7 11.99");
list.Add("ccc zzz 8 12.99");
var sum = 0m;
foreach (object item in list)
{
string[] itemParts =
((string)item).Split(default(char[]), StringSplitOptions.RemoveEmptyEntries);
// price's index is 2
sum += decimal.Parse(itemParts[itemParts.Length - 1]);
}
Console.WriteLine(sum);
}
}
I have an object like so
public class Person{
public int ID { get; set; }
public string Name{ get; set; }
public string Surname{ get; set; }
public List<int> AltIDs { get; set; }
public List<string> AltNames{ get; set; }
}
I am setting data into the DataGridView like
dataGridView1.DataSource = null;
dataGridView1.DataSource = persons; // persons is List<Person>
However I only see 3 columns in the DataGridView that is of ID, Name and Surname, the properties with List<int> and List<string> seem to be ignored.
Is there a way to get theses properties showing up in the DataGridView? Probably like comma seperated values.
most probably the persons is null
you just change your code like this code
first Solution :
your model :
public class Person
{
public int ID { get; set; }
public string Name { get; set; }
public string Surname { get; set; }
public List<int> AltIDs { get; set; }
public List<string> AltNames { get; set; }
}
PersonViewModel :
public class PersonViewModel
{
public int ID { get; set; }
public string Name { get; set; }
public string Surname { get; set; }
public int AltID { get; set; }
public string AltName { get; set; }
}
formLoad :
private void Form1_Load(object sender, EventArgs e)
{
List<Person> people = new List<Person>()
{new Person(){ ID=1,Name="a",Surname="a",AltIDs=new List<int>(){1,2,3,4 },AltNames=new List<string>(){"a","b","c" } },
new Person(){ ID=2,Name="b",Surname="b",AltIDs=new List<int>(){10,20,30,40 },AltNames=new List<string>(){"a","b","c" }},
new Person(){ ID=3,Name="c",Surname="c",AltIDs=new List<int>(){100,200,300,400 },AltNames=new List<string>(){"a","b","c" }},
new Person(){ ID=4,Name="d",Surname="d",AltIDs=new List<int>(){1000,2000,3000,4000 },AltNames=new List<string>(){"a","b","c" }},
new Person(){ ID=3,Name="e",Surname="e",AltIDs=new List<int>(){10000,20000,30000,40000 },AltNames=new List<string>(){"a","b","c" }}
};
List<PersonViewModel> pwm = new List<PersonViewModel>();
foreach (var person in people)
{
foreach(var id in person.AltIDs)
{
foreach (var name in person.AltNames)
pwm.Add(new PersonViewModel() { ID = person.ID, Name = person.Name, Surname = person.Surname, AltID = id, AltName = name });
}
}
dgv.DataSource = pwm;
}
Result :
Second Solution :
your model :
public class Person
{
public int ID { get; set; }
public string Name { get; set; }
public string Surname { get; set; }
public List<int> AltIDs { get; set; }
public List<string> AltNames { get; set; }
}
PersonViewModel :
public class PersonViewModel
{
public int ID { get; set; }
public string Name { get; set; }
public string Surname { get; set; }
public string AltID { get; set; }
public string AltName { get; set; }
}
FormLoad :
private void Form1_Load(object sender, EventArgs e)
{
List<Person> people = new List<Person>()
{new Person(){ ID=1,Name="a",Surname="a",AltIDs=new List<int>(){1,2,3,4 },AltNames=new List<string>(){"a","b","c" } },
new Person(){ ID=2,Name="b",Surname="b",AltIDs=new List<int>(){10,20,30,40 },AltNames=new List<string>(){"a","b","c" }},
new Person(){ ID=3,Name="c",Surname="c",AltIDs=new List<int>(){100,200,300,400 },AltNames=new List<string>(){"a","b","c" }},
new Person(){ ID=4,Name="d",Surname="d",AltIDs=new List<int>(){1000,2000,3000,4000 },AltNames=new List<string>(){"a","b","c" }},
new Person(){ ID=3,Name="e",Surname="e",AltIDs=new List<int>(){10000,20000,30000,40000 },AltNames=new List<string>(){"a","b","c" }}
};
List<PersonViewModel> pwm = new List<PersonViewModel>();
StringBuilder sbIds;
StringBuilder sbNames;
foreach (var person in people)
{
sbIds = new StringBuilder();
sbNames = new StringBuilder();
person.AltIDs.ForEach(c=> sbIds.Append(c.ToString()).Append(","));
person.AltNames.ForEach(c=> sbNames.Append(c).Append(","));
pwm.Add(new PersonViewModel() { ID = person.ID, Name = person.Name, Surname = person.Surname, AltID = sbIds.ToString().TrimEnd(','), AltName = sbNames.ToString().TrimEnd(',') });
}
dgv.DataSource = pwm;
}
Result :
When DataGridView is bound to DataSource, it auto generates columns for it for the properties with primitive datatypes such as int, string, double etc.
For the datatypes which are collections, DataGridViewComboBoxColumn is used. This type of column is not autogenerated. You need to add such columns manually in the GridView columns collection.
For your use case following is the solution.
Add DataGridView to the form and add columns to it manually from the Form's designer view by click on Add Column. You will have to add 3 DataGridViewTextBoxColumn and 2 DataGridViewComboBoxColumn.
After adding columns, the columns would look as following.
Now while assigning DataSource to the DataGridView you need to write following code. Here IdColumn, NameColumn, and SurnameColumn are the names give to columns when they were created in above steps.
dataGridView1.AutoGenerateColumns = false;
IdColumn.DataPropertyName = "ID";
NameColumn.DataPropertyName = "Name";
SurnameColumn.DataPropertyName = "Surname";
dataGridView1.DataSource = persons;
With the above code you will see Id, Name and Surname columns populated for persons in the collection but the dropdown lists in last two columns are empty.
To populate the dropdown list columns you need to add event handler for CellClick event of the DataGridView. And write following code there.
Here AltIdsColumn and AltNamesColumn are the names given to column when they created manually in earlier steps.
private void dataGridView1_CellClick(object sender, DataGridViewCellEventArgs e)
{
var altIdIndex = dataGridView1.Columns["AltIdsColumn"].Index;
var altNameIndex = dataGridView1.Columns["AltNamesColumn"].Index;
if (altIdIndex == e.ColumnIndex || altNameIndex == e.ColumnIndex)
{
var altIdsCell = (DataGridViewComboBoxCell)dataGridView1.Rows[e.RowIndex].Cells[altIdIndex];
var altNamesCell = (DataGridViewComboBoxCell)dataGridView1.Rows[e.RowIndex].Cells[altNameIndex];
if (altIdsCell.DataSource == null || altNamesCell.DataSource == null)
{
var person = dataGridView1.Rows[e.RowIndex].DataBoundItem as Person;
if (person != null)
{
altIdsCell.DataSource = person.AltIDs;
altNamesCell.DataSource = person.AltNames;
}
}
}
}
With this code, the dropdown list of AldIDs and AltNames columns will be populated when you click on those dropdown lists on individual rows.
I hope this will help you resolve your issue.
From what I can tell, this appears as a fairly basic “Master/Slave” type UI. In the grid, there is a “Person” object with the ID, Name and Surname. Obviously from your question, the AltIDs and AltNames Lists are NOT displayed in the grid.
This is due to the fact that the grid has a problem trying to add a “list of multiple” values into a “single” cell. As suggested, it is possible for you to “combine” the values into a “single” string and use that. However, this is extra work on your part and may create more work if the cell is edited.
One issue in this example is that, the AltIDs list for each Person could and will have a different number of elements. One possible solution is to simply ADD theses as new rows where the Person info (ID, Name and Surname) are duplicated. This will work but IMHO not very user friendly. The combo box option will also work, however again it may be confusing to users since a combo box “usually” indicates that the users selects a “single” value from many.
It is also possible to “flatten” each person object and have a column for each AltID in the list. This will work but the possibility of large gaps in the grid are likely. Again, not very user friendly. This is all doubled by the fact that there is another list AltNames that we have to take into account.
Given this, it appears clear that YOU are going to have to do extra work UNLESS you use an advanced third-party grid OR add more grids to the picture… in this case three (3) total. One for the person, another for the IDs and a third for the Names. Proper arrangement of the grids may be a little work, however, with three grids, it will make all the issues described above… go away. In addition the coding will be much easier.
It would be such that the user “selects” a Person from the first grid, then the second grid list all the AltID values and the third grid lists all the AltName values. If the “same” data source is used for each grid then… when the user selects a different Person in the person grid, the AltID grid and AltName grid will “automatically” update/refresh with the proper values. This will also make CRUD operations on any grid/value much easier.
The only problem I seen in the current Person class is that the two Lists are of “primitive” types. Used this way, the lists won’t display properly, the grid wants a CLASS. Therefore, you need to make a wrapper class for the AltID and AltName lists. Then change the list values in the Person class. Something like…
public class AltID_C {
public int AltID { get; set; }
}
public class AltName_C {
public string AltName { get; set; }
}
public class Person {
public int ID { get; set; }
public string Name { get; set; }
public string Surname { get; set; }
public List<AltID_C> AltIDs { get; set; }
public List<AltName_C> AltNames { get; set; }
}
With this, all that is needed is to set each grid to the “same” data source, then set the AltID grids DataMember to the “AltIDs” property and set the AltNames grids DataMember to the “AltNames` property. Something like…
List<Person> AllPersons;
public Form1() {
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e) {
AllPersons = GetRandomData(25);
dgvPerson.DataSource = AllPersons;
dgvAltID.DataSource = AllPersons;
dgvAltID.DataMember = "AltIDs";
dgvAltNames.DataSource = AllPersons;
dgvAltNames.DataMember = "AltNames";
}
Additional code below to complete the example.
private List<Person> GetRandomData(int numberOfPersons) {
List<Person> listOPeople = new List<Person>();
Person curPerson;
Random rand = new Random();
for (int i = 0; i < numberOfPersons - 1; i++) {
curPerson = new Person() {
ID = rand.Next(1, 1000),
Name = "Name_" + i + 1,
Surname = "Sname_" + i + 1,
AltIDs = GetRandomNumberOfInts(rand),
AltNames = GetRandomNumberOfStrings(rand)
};
listOPeople.Add(curPerson);
}
return listOPeople;
}
private List<AltID_C> GetRandomNumberOfInts(Random rand) {
List<AltID_C> listOInts = new List<AltID_C>();
int numberOfInts = rand.Next(0, 10);
for (int i = 0; i < numberOfInts - 1; i++) {
listOInts.Add(new AltID_C { AltID = rand.Next(1, 10000) });
}
return listOInts;
}
private List<AltName_C> GetRandomNumberOfStrings(Random rand) {
List<AltName_C> listOStrings = new List<AltName_C>();
int numberOfStrings = rand.Next(0, 10);
for (int i = 0; i < numberOfStrings - 1; i++) {
listOStrings.Add(new AltName_C { AltName = "RandString: " + rand.Next(1, 1000) });
}
return listOStrings;
}
Hope that helps.
i'm using ObjectListview to display checkboxes for columns but there is a problem.
My model is like this:
public class HocVienLopDTO
{
public HocVienDTO HocVien { get; set; }
public double Diem { get; set; }
public List<NgayHocDTO> DSNgayHoc { get; set; }
}
public class NgayHocDTO
{
public DateTime Ngay { get; set; }
public bool CoHoc { get; set; }
}
I want to create a listview like this: (Diem, DSNgayHoc[0], DSNgayHoc[1], ...). And i want to use checkbox for all the DSNgayHoc column to present value of it's CoHoc property. So i dynamically generate columns like this:
this.lstvDiemDanh.UseSubItemCheckBoxes = true;
List<OLVColumn> colList = new List<OLVColumn>();
for (int i = 0; i < this.lop.DSNgayHoc.Count; i++)
{
OLVColumn col = new OLVColumn();
col.IsHeaderVertical = true;
col.CheckBoxes = true;
col.AspectName = string.Format(string.Format("DSNgayHoc[{0}].CoHoc", i));
col.Text = this.lop.DSNgayHoc[i];
col.Width = 20;
col.IsEditable = true;
colList.Add(col);
}
this.lstvDiemDanh.AllColumns.AddRange(colList);
this.lstvDiemDanh.RebuildColumns();
All the checkbox was displayed fine but their state is not changed when i clicked them. (Always square box). I tried to handle ChangingSubItem event to change the currentValue and newValue but no luck. Please help!
Sorry about my english.
The OLV is using reflection to search for a property with the AspectName name. This won't work in this case, because it does not know that you are accessing a list index.
Instead of using the AspectName
// ...
col.AspectName = string.Format(string.Format("DSNgayHoc[{0}].CoHoc", i));
// ...
you have to use the AspectGetter and AspectPutter callbacks to access the DSNgayHoc list as desired.
// ...
int listIndex = i;
col.AspectGetter = delegate(object rowObject) {
HocVienLopDTO model = rowObject as HocVienLopDTO;
if (model.DSNgayHoc.Count > listIndex)
return model.DSNgayHoc[listIndex].CoHoc;
else
return false;
};
col.AspectPutter = delegate(object rowObject, object value) {
HocVienLopDTO model = rowObject as HocVienLopDTO;
if (model.DSNgayHoc.Count > listIndex)
model.DSNgayHoc[listIndex].CoHoc = (bool)value;
};
// ...
I am trying to update a List which is a List of Interfaces to concrete classes.
I add to the List each Market type i am interested in, for this Example these Markets are A and B
I loop over all the markets, (sample provided with 3 markets A B & C, we are only interested in A and B) And determine which is of interest to us.
Once found we pass this to an extraction method too do its work and create an instance of the Correct Market_ class type.
This all works fine, but when i try to update the list with the Updates it does not get reflected in the List.
Code below, any Suggestions?
Thanks
public class Test
{
public Test()
{
TheMarkets MarketsToUpdate = new TheMarkets();
List<SpecificCompanyMarket> lstMarks = new List<SpecificCompanyMarket>();
lstMarks.Add(new SpecificCompanyMarket(1234, "A", "Some HTML DATA HERE"));
lstMarks.Add(new SpecificCompanyMarket(5874, "B", "Some HTML DATA HERE"));
lstMarks.Add(new SpecificCompanyMarket(2224, "C", "Some HTML DATA HERE"));
foreach (var item in lstMarks)
{
if (MarketsToUpdate.IsMarketWeAreInterestedIn(item.MarketName))
{
ITheMarkets MarkToUpdate = ExtractMarketData(item);
var obj = MarketsToUpdate.MarketsWeAreInterestedIn.FirstOrDefault(x => x.MarketName() == "A");
if (obj != null)
{
obj = MarkToUpdate;
}
}
}
//Look At MarketsToUpdate Now and the item has not changed, still original values
//I was expecting to see the new values for the fields in A, not the default 0's
}
public ITheMarkets ExtractMarketData(SpecificCompanyMarket item)
{
ITheMarkets market = null;
if (item.MarketName.ToUpper() == "A")
{
Market_A marketType = new Market_A();
marketType.SomeValue1 = 123;
marketType.SomeValue2 = 158253;
market = marketType;
}
//Other Market extractions here
return market;
}
}
public class SpecificCompanyMarket
{
public int MarketId { get; set; }
public string MarketName { get; set; }
public string MarketDataHTML { get; set; }
public SpecificCompanyMarket(int MID, string MName, string MData)
{
MarketId = MID;
MarketName = MName;
MarketDataHTML = MData;
}
}
public class TheMarkets
{
public List<ITheMarkets> MarketsWeAreInterestedIn = new List<ITheMarkets>();
public TheMarkets()
{
Market_A A = new Market_A();
Market_B B = new Market_B();
MarketsWeAreInterestedIn.Add(A);
MarketsWeAreInterestedIn.Add(B);
}
public bool IsMarketWeAreInterestedIn(string strMarketName)
{
bool blnRetVal = false;
foreach (var item in MarketsWeAreInterestedIn)
{
if (item.MarketName().ToUpper().Trim().Equals(strMarketName.ToUpper().Trim()))
{
blnRetVal = true;
break;
}
}
return blnRetVal;
}
}
public interface ITheMarkets
{
string MarketName();
}
public class Market_A : ITheMarkets
{
public string LabelType { get; private set; }
public double SomeValue1 { get; set; }
public double SomeValue2 { get; set; }
public double SomeValue3 { get; set; }
public Market_A()
{
LabelType = "A";
}
public string MarketName()
{
return LabelType;
}
}
public class Market_B : ITheMarkets
{
public string LabelType { get; private set; }
public List<string> SomeList { get; set; }
public double SomeValue { get; set; }
public Market_B()
{
LabelType = "B";
}
public string MarketName()
{
return LabelType;
}
}
This is a short example to get you going. Loop through your list, find the object you want to update, create a new object of that type and then find the original objects index in the list and overwrite it in place. You are essentially just replacing the object in the list with a new one not mutating the existing one.
foreach (var item in lstMarks)
{
//your code to get an object with data to update
var yourObjectToUpdate = item.GetTheOneYouWant();
//make updates
yourObjectToUpdate.SomeProperty = "New Value";
int index = lstMarks.IndexOf(item);
lstMarks[index] = yourObjectToUpdate;
}
You are extracting an obj from marketWeAreInterestedIn list using LINQ's firstOrDefault extension. This is a new object and not a reference to the obj in that list. Therefore, no updates will be reflected in the object inside that list. Try using 'indexof'
You are not storing "list of interfaces" in your list. List<T> stores an array of pointers to objects that support T interface. Once you enumerate (with Linq in your case) your list, you copy a pointer from list, which is not associated with list itself in any way. It is just a pointer to your instance.
To do what you want, you will have to build new list while enumerating the original one, adding objects to it, according to your needs, so the second list will be based on the first one but with changes applied that you need.
You can also replace specific instance at specific index instead of building new list in your code, but to do this you will need to enumerate your list with for loop and know an index for each item:
list[index] = newvalue;
But there is a third solution to update list item directly by Proxying them. This is an example
class ItemProxy : T { public T Value { get; set; } }
var list = new List<ItemProxy<MyClass>>();
list.Insert(new ItemProxy { Value = new MyClass() });
list.Insert(new ItemProxy { Value = new MyClass() });
list.Insert(new ItemProxy { Value = new MyClass() });
foreach(var item in list)
if(item // ...)
item.Value = new MyClass(); // done, pointer in the list is updated.
Third is the best case for perfomance, but it will be better to use this proxying class for something more than just proxying.
I am iterating through a List of objects of Type "prvEmployeeIncident".
The object has the following properties:
public DateTime DateOfIncident { get; set; }
public bool IsCountedAsAPoint;
public decimal OriginalPointValue;
public bool IsFirstInCollection { get; set; }
public bool IsLastInCollection { get; set; }
public int PositionInCollection { get; set; }
public int DaysUntilNextPoint { get; set; }
public DateTime DateDroppedBySystem { get; set; }
public bool IsGoodBehaviorObject { get; set; }
My List is sorted by the DateOfIncident property. I would like to find the next object up the list where IsCounted == true and change it to IsCounted = false.
One question:
1) How do I find this object up the list ?
If I understand your question correctly, you can use LINQ FirstOrDefault:
var nextObject = list.FirstOrDefault(x => x.IsCountedAsAPoint);
if (nextObject != null)
nextObject.IsCountedAsAPoint = false;
If I understand correctly this can be solved with a simple foreach loop. I don't exactly understand your emphasis on "up" as you don't really move up a list, you traverse it. Anyways, the following code snippet finds the first Incident where IsCounted is true and changes it to false. If you're starting from a given position change the for each loop to a for loop and start at i = currentIndex with the exit condition being i < MyList.Count. Leave the break statement to ensure you only modify one Incident object.
foreach (prvEmployeeIncident inc in MyList)
{
if (inc.IsCountedAsAPoint)
{
inc.IsCountedAsAPoint = false;
break;
}
}
You can use List(T).FindIndex to search up the list.
Example:
public class Foo
{
public Foo() { }
public Foo(int item)
{
Item = item;
}
public int Item { get; set; }
}
var foos = new List<Foo>
{
new Foo(1),
new Foo(2),
new Foo(3),
new Foo(4),
new Foo(5),
new Foo(6)
};
foreach (var foo in foos)
{
if(foo.Item == 3)
{
var startIndex = foos.IndexOf(foo) + 1;
var matchedFooIndex = foos.FindIndex(startIndex, f => f.Item % 3 == 0);
if(matchedFooIndex >= startIndex) // Make sure we found a match
foos[matchedFooIndex].Item = 10;
}
}
Just be sure you do not modify the list itself since that will throw an exception.