Is it possible to sum up all of the item's price inside a ListBox? I have this ListBox that displays items from a DataGridView, and each of their prices are in priceTextBox Please refer from the picture below.
What I want to do is to display the sum of all the item's price and display it at the totalTextBox.
I have already done this code but I think this won't work.
private void menuListBox_SelectedValueChanged(object sender, EventArgs e)
{
int x = 0;
string Str;
foreach (DataGridViewRow row in menuDataGrid.Rows) //this part displays the price of each item to a textbox so this is good.
{
if (row.Cells[3].Value.ToString().Equals(menuListBox.SelectedItem.ToString()))
{
pricetxtbox.Text = row.Cells[5].Value.ToString();
break;
}
}
foreach (string Str in row.Cells[5].Value.ToString().Equals(menuListBox.SelectedItem.ToString())) //now this is the part where I want to happen the adding the prices of all items in the listbox. this also gives me an error at row saying it doesn't exist in the context
{
x = x + Convert.ToInt32(Str);
totaltxtbox.Text = x;
}
}
Will appreciate any help! Thanks!
Try this out...
Func<string, DataGridViewRow> getRow = (menuCode) =>
{
return menuDataGrid.Rows.Cast<DataGridViewRow>()
.First(r => ((string)r.Cells[3].Value).Equals(menuCode));
};
var selected = menuListBox.SelectedItem.ToString();
pricetxtbox.Text = getRow(selected).Cells[5].Value.ToString();
totaltxtbox.Text = menuListBox.Items.Cast<object>()
.Select(o => o.ToString())
.Select(i => (int)getRow(i).Cells[5].Value)
.Sum()
.ToString();
I think you should change your approach, by separating completely data and display.
To do that, you could create a class which will contain data for each row of your DataGrid :
public class MyItem
{
public string Caption { get; set; }
public int Price { get; set; }
}
And then in your codebehind, you store a list of these items :
private List<MyItem> AllItems = new List<MyItem>();
Finally, you set this collection as the source of your DataGrid :
menuDataGrid.DataSource = AllItems;
Then, all your data is stored in a collection you own, and to sum prices it's much simpler :
using System.Linq;
private int ComputeSum()
{
return (AllItems.Sum(item => item.Price));
}
The next step is to use Binding and a BindingList, wich allows the DataGrid to refresh automatically when new items are added in "AllItems": see this well explained post.
Related
In my DataGridView I have 3 columns: Bank, DataBuy, and DataSell.
I found Max and Min values using this code:
double[] columnDataBuy = (
from DataGridViewRow row
in DataGridView1.Rows
where row.Cells[1].FormattedValue.ToString() != string.Empty
select Convert.ToDouble(row.Cells[1].FormattedValue))
.ToArray();
// Assigning value to TextBox
textBoxBuy.Text=columnDataBuy.Max().ToString();
double[] columnDataSell = (
from DataGridViewRow row
in DataGridView1.Rows
where row.Cells[2].FormattedValue.ToString() != string.Empty
select Convert.ToDouble(row.Cells[2].FormattedValue))
.ToArray();
textBoxSell.Text=columnDataSell.Min().ToString();
This assigns the min or max value to the TextBox, but what I want instead is the value in the Bank column to go there. How can I obtain the value for Bank from my DataGridView?
Looking at your code sample, you have a DataGridView with columns [Bank|DataBuy|DataSell] and you are looking for a way to assign a string value for Bank to textBoxBuy and textBoxSell after obtaining Max from DataBuy and Min from DataSell. One approach that would make this much simpler would be using Data Binding for your DataGridView1.
If you make a class to represent the properties that you name in your post:
class Record
{
public string Bank { get; set; }
public double DataBuy { get; set; }
public double DataSell { get; set; }
}
And then make a BindingList of records:
BindingList<Record> DataSource = new BindingList<Record>();
Then this will generate the DataGridView columns using the public properties of your Record class. (This occurs by adding the first Record.) You attach the binding source in this way:
public MainForm()
{
InitializeComponent();
}
protected override void OnHandleCreated(EventArgs e)
{
base.OnHandleCreated(e);
if(!DesignMode)
{
DataGridView1.DataSource = this.DataSource;
DataGridView1.AllowUserToAddRows = false;
DataSource.Add(new Record { Bank = "Bank-1", DataBuy = 2.00, DataSell=1.0});
DataSource.Add(new Record { Bank = "Bank-2", DataBuy = 1.00, DataSell=0.0});
TryItThisWay();
}
}
BindingList<Record> DataSource = new BindingList<Record>();
And this makes it very easy to obtain the strings you want for your TextBoxes by using System.Linq:
void TryItThisWay()
{
// Using Linq to sort the DataBuy and choose the record with the highest
Record recordMax = DataSource.OrderByDescending(record=>record.DataBuy).First();
// Now you can obtain the Bank property from the Record.
textBoxBuy.Text = recordMax.Bank;
// Using Linq to sort the DataSell and choose the record with the lowest
Record recordMin = DataSource.OrderBy(record=>record.DataSell).First();
textBoxSell.Text = recordMin.Bank;
}
In this way, you can achieve the outcome in your post.
I am using a ListBox that stores items such as camping gear. The ListBox will have tent, camping chairs, coffee and so on.
Then I created a list that has the prices for each item called lstprices.
When I double click on the entry it will appear in another ListBox while pulling the price from the lstprices and add it to a subtotal Label.
try
{
if (lstItems.Items.Count > 0)
{
lstOrder.Items.Add(lstItems.SelectedItem.ToString());
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
I am assuming I would somehow add information to the lstOrder.Items since I know the element numbers but don't understand how to display it in my Label.
I hope this makes sense.
Also for this statement:
decimal STotal = decimal.Parse(lblSubtotal.Text.ToString());
it errors stating that its in the wrong format when I double click the item.
Sorry i cannot comment so have to post an answer:
if you have to keep track of multiple properties of an object then you should create a class of it for example (Object Oriented Programming):
public class Product
{
public string Name {get;set;}
public double Price {get;set;}
public Product(string name, double price)
{
Name = name;
Price = price
}
}
this way you can just do: Product chair = new Product("Camping Chair", 12.99);
or you can fill a list
List<Product> products = new List<Product>()
{
new Product("Camping Chair", 12.99),
new Product("Tent", 25.99)
}
and then display your products in a list or datagridview etc. it will be a better approach then maintaining two lists with names and prices.
this will also make work with the listbox easier it has a property called SelectedItem which you can cast to your class and then reference it properties for example:
Product selectedProduct = myListbox.SelectedItem as Product;
myLabel.Text = selectedProduct?.Price.ToString();
EDIT:
since classes have not been covered by, what you can do is use a Dictionary<string,double> to keep track of your prices and keys (product name) or at least a List<KeyValuePair<string,double>> :
Below a quick example to get you started:
Dictionary<string, double> products = new Dictionary<string, double>()
{
{"Camping Chair",33.0},
{"Stool",12.99},
};
//accessing the price via the key, which
var price = products.Where(x => x.Key == "Stool").SingleOrDefault();
// you could then pass in the selected item of your list box as string
var price = products.Where(x => x.Key == listbox.SelectedItem.ToString()).SingleOrDefault();
More about dictionary at msdn
More about KeyValuePair at msdn
I have populated a listBox with items of type:
Tuple<List<Point>,List<int>>
Now when I run my application the listBox window display items like so:
(System.Collection.Generic.List'1[System.Drawing.Point].Systen,Collection...)
(System.Collection.Generic.List'1[System.Drawing.Point].Systen,Collection...)
(System.Collection.Generic.List'1[System.Drawing.Point].Systen,Collection...)
Instead I want listBox to display each item with string "Region" + item's index.
Like so:
Region0
Region1
Region2
...
To populate the listBox I use the following code:
listBoxPossibleCandidates.DataSource = possibleCandidates;
And possibleCandidates are populated like this with a loop:
possibleCandidates.Add(Tuple.Create(regionPoints, regionIntensities));
Okay, I found how to do this now.
It's done through ListControl.DisplayMember Property.
The reason it was displaying (System.Collection.Generic.List`1[System.Drawing.Point].Systen,Collection...) is because of this:
"If the specified property does not exist on the object or the value of DisplayMember is an empty string (""), the results of the object's ToString method are displayed instead."
Source: http://msdn.microsoft.com/en-us/library/system.windows.forms.listcontrol.displaymember(v=vs.110).aspx
Code I used:
class Region
{
private int myIndex;
private List<Point> regionCoordinates;
private List<int> regionIntensitiesDistinct;
public Region(List<Point> regionCoordinates, List<int> regionIntensities, int index)
{
this.regionCoordinates = regionCoordinates;
this.regionIntensitiesDistinct = regionIntensities.Distinct().ToList();
this.myIndex = index;
}
public string MyDescription
{
get
{
return "Region-" + myIndex;
}
}
public List<Point> getRegionCoordinates()
{
return regionCoordinates;
}
public List<int> getRegionIntensitiesDistinct()
{
return regionIntensitiesDistinct;
}
}
Now this is how I populate my listBox:
List<Region> possibleCandidates = new List<Region>();
//using loop I add all the regions:
possibleCandidates.Add(new Region(regionPoints, regionIntensities, possibleCandidates.Count));
//after possibleCandidates are populated I pass them to the listBox for display
listBoxPossibleCandidates.DataSource = possibleCandidates;
listBoxPossibleCandidates.DisplayMember = "MyDescription";
The listBox output is now:
* Region-0
* Region-1
* Region-2
* ...
I'm working with a ListView control in Windows Forms using C# and .NET 3.5. I have several items that I'm adding to this ListView control, and I have functionality in place that is supposed to allow the user to select different ways to group the items in the list. However, sometimes items get dumped into the automatically generated "Default" group for no explicable reason.
The code is too large to just put here, but let me explain what I'm doing algorithmically. Suppose we're starting with a ListView that contains items and may or may not already contain groups.
Cycle through every item and set
it's Group property to null.
Empty the ListView's Groups
collection.
Add new Groups to the ListView's
Group collection.
Cycle through every item and set the
Group property using a value
obtained from the ListView's Group
collection via index.
I've stepped through the code and observed everything as it should be. With each item it obtains the appropriate group from the ListView's Group collection and sets the Group property of the item, yet sometimes they end up listed under the "Default" group.
Has anyone else ever observed this or have any theories as to why it might be happening?
It doesn't sound familiar, and I can't reproduce (see below, which follows your approach). Can you post anything related to your "update the groups" code?
using System;
using System.Windows.Forms;
static class Program {
[STAThread]
static void Main() {
Application.EnableVisualStyles();
ListView lv;
Button btn;
Form form = new Form {
Controls = {
(lv = new ListView { Dock = DockStyle.Fill,
ShowGroups = true}),
(btn = new Button { Dock = DockStyle.Bottom,
Text = "Scramblle" })
}
};
Random rand = new Random();
for (int i = 0; i < 40; i++) {
lv.Items.Add("Item " + i);
}
btn.Click += delegate {
// 1: Cycle through every item and set it's Group
// property to null.
foreach (ListViewItem item in lv.Items) {
item.Group = null;
}
// 2: Empty the ListView's Groups collection.
lv.Groups.Clear();
// 3: Add new Groups to the ListView's Group collection.
int groupCount = rand.Next(lv.Items.Count) + 1;
for (int i = 0; i < groupCount; i++) {
lv.Groups.Add("grp" + i, "Group " + i);
}
// 4: Cycle through every item and set the Group property
// using a value obtained from the ListView's Group collection
// via index.
foreach (ListViewItem item in lv.Items) {
item.Group = lv.Groups[rand.Next(groupCount)];
}
};
Application.Run(form);
}
}
Is this happening on multiple threads?
It sounds like you might be adding some ListViewItems with Groups taken before the the groups were cleared.
Yes... It happened to me too. Try to set the Group before you add the item. I mean when you initialize the ListViewItem you add to the constructor the group it takes part of. That way is going to work.
Yes, I have seen similar behavior. The solution I followed is based on the code here.
public partial class ListViewExtended : ListView
{
private Collection<Dictionary<string, ListViewGroup>> groupTables = new Collection<Dictionary<string,ListViewGroup>>();
public ListViewExtended()
{
InitializeComponent();
}
/// <summary>
/// Create groups for each column of the list view.
/// </summary>
public void CreateGroups()
{
CreateGroups(false);
}
/// <summary>
/// Create groups for each column of the list view.
/// </summary>
public void CreateGroups(bool reset)
{
if (OSFeature.Feature.IsPresent(OSFeature.Themes))
{
if (reset)
{
this.groupTables.Clear();
}
for (int column = 0; column < this.Columns.Count; column++)
{
Dictionary<string, ListViewGroup> groups = new Dictionary<string, ListViewGroup>();
foreach (ListViewItem item in this.Items)
{
string subItemText = item.SubItems[column].Text;
// Use the initial letter instead if it is the first column.
if (column == 0)
{
subItemText = subItemText.Substring(0, 1).ToUpperInvariant();
}
if (!groups.ContainsKey(subItemText))
{
groups.Add(subItemText, new ListViewGroup(subItemText) { Name = subItemText });
}
}
this.groupTables.Add(groups);
}
}
}
/// <summary>
/// Sets the list view to the groups created for the specified column.
/// </summary>
/// <param name="column"></param>
public void SetGroups(int column)
{
if (OSFeature.Feature.IsPresent(OSFeature.Themes))
{
try
{
this.BeginUpdate();
this.Groups.Clear();
if (column == -1)
{
this.ShowGroups = false;
}
else
{
this.ShowGroups = true;
Dictionary<string, ListViewGroup> groups = groupTables[column];
this.Groups.AddRange(groups.Values.OrderBy(g => g.Name).ToArray());
foreach (ListViewItem item in this.Items)
{
string subItemText = item.SubItems[column].Text;
// For the Title column, use only the first letter.
if (column == 0)
{
subItemText = subItemText.Substring(0, 1).ToUpperInvariant();
}
item.Group = groups[subItemText];
}
groups.Values.ForEach<ListViewGroup>(g => g.Header = String.Format(CultureInfo.CurrentUICulture, "{0} ({1})", g.Name, g.Items.Count));
}
}
finally
{
this.EndUpdate();
}
}
}
}
In the code that actually adds the items to the listview, you want to do something like this:
this.itemsListView.Items.AddRange(items.ToArray());
this.itemsListView.CreateGroups(true);
this.itemsListView.SetGroups(0); // Group by the first column by default.
Creating an item(Under the key) is easy,but how to add subitems(Value)?
listView1.Columns.Add("Key");
listView1.Columns.Add("Value");
listView1.Items.Add("sdasdasdasd");
//How to add "asdasdasd" under value?
You whack the subitems into an array and add the array as a list item.
The order in which you add values to the array dictates the column they appear under so think of your sub item headings as [0],[1],[2] etc.
Here's a code sample:
//In this example an array of three items is added to a three column listview
string[] saLvwItem = new string[3];
foreach (string wholeitem in listofitems)
{
saLvwItem[0] = "Status Message";
saLvwItem[1] = wholeitem;
saLvwItem[2] = DateTime.Now.ToString("dddd dd/MM/yyyy - HH:mm:ss");
ListViewItem lvi = new ListViewItem(saLvwItem);
lvwMyListView.Items.Add(lvi);
}
Like this:
ListViewItem lvi = new ListViewItem();
lvi.SubItems.Add("SubItem");
listView1.Items.Add(lvi);
Suppose you have a List Collection containing many items to show in a ListView, take the following example that iterates through the List Collection:
foreach (Inspection inspection in anInspector.getInspections())
{
ListViewItem item = new ListViewItem();
item.Text=anInspector.getInspectorName().ToString();
item.SubItems.Add(inspection.getInspectionDate().ToShortDateString());
item.SubItems.Add(inspection.getHouse().getAddress().ToString());
item.SubItems.Add(inspection.getHouse().getValue().ToString("C"));
listView1.Items.Add(item);
}
That code produces the following output in the ListView (of course depending how many items you have in the List Collection):
Basically the first column is a listviewitem containing many subitems (other columns). It may seem strange but listview is very flexible, you could even build a windows-like file explorer with it!
I've refined this using an extension method on the ListViewItemsCollection. In my opinion it makes the calling code more concise and also promotes more general reuse.
internal static class ListViewItemCollectionExtender
{
internal static void AddWithTextAndSubItems(
this ListView.ListViewItemCollection col,
string text, params string[] subItems)
{
var item = new ListViewItem(text);
foreach (var subItem in subItems)
{
item.SubItems.Add(subItem);
}
col.Add(item);
}
}
Calling the AddWithTextAndSubItems looks like this:
// can have many sub items as it's string array
myListViewControl.Items.AddWithTextAndSubItems("Text", "Sub Item 1", "Sub Item 2");
Hope this helps!
I think the quickest/neatest way to do this:
For each class have string[] obj.ToListViewItem() method and then do this:
foreach(var item in personList)
{
listView1.Items.Add(new ListViewItem(item.ToListViewItem()));
}
Here is an example definition
public class Person
{
public string Name { get; set; }
public string Address { get; set; }
public DateTime DOB { get; set; }
public uint ID { get; set; }
public string[] ToListViewItem()
{
return new string[] {
ID.ToString("000000"),
Name,
Address,
DOB.ToShortDateString()
};
}
}
As an added bonus you can have a static method that returns ColumnHeader[] list for setting up the listview columns with
listView1.Columns.AddRange(Person.ListViewHeaders());
Create a listview item
ListViewItem item1 = new ListViewItem("sdasdasdasd", 0)
item1.SubItems.Add("asdasdasd")
ListViewItem item = new ListViewItem();
item.Text = "fdfdfd";
item.SubItems.Add ("melp");
listView.Items.Add(item);
add:
.SubItems.Add("asdasdasd");
to the last line of your code so it will look like this in the end.
listView1.Items.Add("sdasdasdasd").SubItems.Add("asdasdasd");
Generally:
ListViewItem item = new ListViewItem("Column1Text")
{ Tag = optionalRefToSourceObject };
item.SubItems.Add("Column2Text");
item.SubItems.Add("Column3Text");
myListView.Items.Add(item);
Great !! It has helped me a lot. I used to do the same using VB6 but now it is completely different.
we should add this
listView1.View = System.Windows.Forms.View.Details;
listView1.GridLines = true;
listView1.FullRowSelect = true;