consolidate items in a list c# - c#

I now have two list box, lstStock and lstSupply. lstSupply consists of
-Pen
-Pencil
-Staple
-Stapler
-Folder
-Notebook
-Eraser
-Marker
and i have two button, one button named btnAdd and another btnRemove.
when i click on btnAdd i want the selected item in lstSupply to be added into lstStock which i have done by
lstStock.Item.Add(lstSupply.SelectedItem);
if i select the same item in lstSupply twice, I want it to be consolidated in lstStock.
for instance, if i select Pen twice, the list box shall give me "Pen x 2" instead of Pen in a line and another Pen in another line.
I got the feeling this is dealing with foreach but i dont really know how to use that.

Okay, so first of all, you're going to need to store something else in lstStock. I'd suggest something like this:
public class StockItem {
public string Name { get; set; }
public int Count { get; set; }
}
Next, I'd suggest that you don't use the .Items field as the actual container where you're storing your stock items. Instead, I'd have a List<StockItem> that you use to populate lstSupply when you refresh.
private List<StockItem> _items = new List<StockItem>();
Then, when you click "add", you do two things:
Iterate through _items to see if there is already a stock item in there of the matching supply. Create one, and add it, if it does not exist.
Find the matching stock item in _items and increment its Count.
To get it to display the way you'd like, we can override ToString() in our StockItem class.:
public class StockItem {
public string Name { get; set; }
public int Count { get; set; }
public override string ToString() {
if (Count > 1)
return Name + " x" + Count;
return Name;
}
}
Finally, whenever any changes are made, you simply re-populate lstStock:
private void RefreshGui() {
lstStock.Items.Clear();
lstStock.Items.AddRange(_items.ToArray());
}
There are plenty of other ways of going about this, but for this simple exercise, this is how I think I would do it.

If you want the code to work without extra coding, you could simply do this:
if (lstSupply.SelectedIndex > -1 && !lstStock.Items.Contains(lstSupply.SelectedItem))
lstStock.Items.Add(lstSupply.SelectedItem);
this way you are sure that you don't add null items and add each item once max.

Related

get data from dynamically created controls in winform

I have a winform application that allows a user(customer) to place their order. In order to so,when the user clicks "place your order" button then another form is shown up, which includes a number of different controls (numericUpDown, TextBox, Combobox, Calendar and CheckBoxes). The user then has to fill all the fields and then confirm the process.
Now this scenario is just for placing a single order, what if want to allow the user to place a number of orders at once?
The scenario I want to apply is that the user firstly has to determine how many orders they want, then based on that number another form is created and filled by controls that are created dynamically according to the number of order specified.
I mean, the user wants 8 orders, so eight controls of each type is dynamically created.
Actually, till this point, I've managed to do so, but what I'm stuck on is that how could I get the data from the controls?
Back to my example:
If the customer wanted 8 orders, then I shall have:
8 textboxes
8 comboboxes
8 calendars
and so on
To be more clear, Now I want to get the inserted data from textbox1, calender1, combobox1 .... and so on, then store them in a temp list to be stored later into the database,
Anyone could give me a clue?
thanks in advance
I would recommend you to make your own user control for each order (each line).
Pick Add->User control... in project context menu then enter control name "MyOrderControl" and put all your order's controls on it.
Also you will need to create spectial class to hold order's data (if ouy have no such class). And add to MyOrderControl special method to return filled instance of that class.
public partial class MyOrderControl : UserControl
{
public MyOrderControl()
{
InitializeComponent();
}
public class Order
{
public string Name { get; set; }
public int Quantity { get; set; }
}
public Order GetFilledOrder()
{
//Validate data
return new Order()
{
Name = textBox1.Text,
Quantity = (int)numericUpDown1.Value
};
}
}
On form initialization you can create any amount of your control instances. You can use FlowLayoutPanel to place them automaticaly.
public Form1()
{
InitializeComponent();
const int nOrders = 10;
for (int n = 0; n < nOrders; n++)
{
flowLayoutPanel1.Controls.Add(new MyOrderControl());
}
}
And then add spectial method to retrieve all filled orders (using System.Collections.Generic, System.Linq):
public IEnumerable<MyOrderControl.Order> GetOrders()
{
foreach (var order in flowLayoutPanel1.Controls.OfType<MyOrderControl>())
{
yield return order.GetFilledOrder();
}
}
It will allow you to access all orders from parent form:
public void AcquireOrders()
{
var form = new Form1();
if (form.ShowDialog() != DialogResult.OK)
return;
foreach (var order in form.GetOrders())
{
System.Diagnostics.Debug.Print("Order: {0} Qty={1}",
order.Name, order.Quantity);
}
}

Side effects on collection items or return a new collection?

Let's say I have a WriteItem class that looks like this:
public class WriteItem
{
public string Name { get; set; }
public object Value { get; set; }
public int ResultCode { get; set; }
public string ErrorMessage { get; set;}
}
I need to process each item and set its ResultCode and ErrorMessage properties and I though about defining a method similar to this:
public void ProcessItems(WriteItemCollection items)
{
foreach(var item in items)
{
// Process each item and set its result.
}
}
The processing of each item is done by another class.
Is this the best way to do it?
Or is it better to have the method return a collection of a custom Result class?
Both options have their advantages and disadvantages. Both are "fine" in the sense that there is nothing wrong with them and they are commonly used in C#.
Option 1 has the big advantage of being simple and easy. You can even keep a reference to a WriteItem instance and check its status after processing.
Option 2 has a clearer separation of concerns: In Option 1, you need to add comments to your WriteItem class to define which are "input" and which are "output" properties. Option 2 does not need that. In addition, Option 2 allows you to make WriteItem and ProcessingResult immutable, which is a nice property.
Option 2 is also more extensible: If you want to process something else than WriteItems (with the same return options), you can define a class
class ProcessingResult<T>
{
public T Item { get; set; }
public int ResultCode { get; set; }
public string ErrorMessage { get; set; }
}
and use it as ProcessingResult<WriteItem> as well as ProcessingResult<SomeOtherItem>.
What you wrote will work. You can modify the object properties without having side effects while iterating in the collection.
I wouldn't return a new collection unless you need to keep a copy of the original collection untouched.
I think it all comes down to readability.
When you call ProcessItems, is it obvious that the collection has changed? If you call the method like this:
var items = GetItemsFromSomewhere();
ProcessItems(items);
versus calling it like this:
var items = GetItemsFromSomewhere();
items = ProcessItems(items);
or simply changing your methodname:
var items = GetItemsFromSomewhere();
items = UpdateItemStatuses(items);
In the end there is no right answer to this question in my book. You should do what feels right for your application. And consider: what if another developer was looking at this piece of code? Can he surmise what is happening here, or would he have to dive into the ProcessItems-function to get the gist of the application.
It is better to return a new results class.
why?
As others have said you are modifying the collection and its not really clear. But for me this is not the main reason. You can have processes which modify objects.
For me its because you have had to add extra properties to your WriteItem object in order to support the processor. This in effect creates a strong coupling between the model and a processor which should not exist.
Consider you have another method ProcessItems_ForSomeOtherPurpose(List<WriteItem> items) do you expand your ResultCode int to have more meaningful values? do you add another property ResultCode_ForSomeOtherPurpose? what if you need to process the same item mutiple times with multiple processors?
I would give your Model an Id. Then you can log multiple processes against it
eg.
item 1 - loaded
item 1 - picking failed!
item 1 - picked
item 1 - delivered

How can I still keep my old sessions after I override them?

I'm creating a product and checkout page. In the product page when a button is pressed I do this
Session["code"] = productLabel.Text;
Session["description"] = descriptionTextBox.Text;
Session["price"] = priceLabel.Text;
Response.Redirect("cart.aspx");
Then in the cart page I have
if ((Session["code"] != null))
{code = (Session["code"]).ToString();}
if ((Session["description"] != null))
{ description = (Session["description"]).ToString(); }
if ((Session["price"] != null))
{price = (Session["price"]).ToString(); }
string item = code + " " + description + " " + price;
cartList.Items.Add(item);
This works, however my problem is when I then add another product to the list, it overrides my first item so theirs only one item in there at a time. How can I keep track of whats currently/previously in there?
Thanks a lot!
You'll probably want to rethink the entire concept and store some custom class instead, one thing you can do is create a list of items in the cart and store that list in the Session.
[Serializable]
public class Item
{
public string Code {get;set;}
public string Description {get;set;}
public string Price {get;set;}
}
List<Item> cart=new List<Item>();
Item item=new Item();
item.Code=productLabel.Text;
item.Description=descriptionTextBox.Text;
item.Price=priceLabel.Text;
cart.Add(item);
Session["cart"]=cart;
//then later pull it out...
List<Item> cart=Session["cart"] as List<Item>; //youll want to check for null etc
//and add another item
Item newItem=new Item();
newItem.Code=productLabel.Text;
newItem.Description=descriptionTextBox.Text;
newItem.Price=priceLabel.Text;
cart.add(newItem);
There's a ton wrong with your architecture. For instance, an enterprising individual could use their browser's tools to change the value in priceLabel.Text and potentially pay less (or nothing!) for their order. But hopefully this gives you an idea about how to proceed.
public class Item
{
public string Code {get;set;}
public string Description {get;set;}
public string Price {get;set;}
}
you can create kind of like a in memory cache to store your items:
public static readonly ConcurrentDictionary<string,List<Item>> myMemoryCache = new ConcurrentDictionary<string,List<Item>>();
and use this as the data source.
You can use the same "key" for multiple entries or you an change this to whatever you like.
Be sure to initialize this in your App_Start

Remove one occurrence of an item in a list containing many occurrence

In my app I have a class that contains a list of these items:
public class Order
{
(...)
public List<OrderDetailInfo> mListOrderDetail { get; set; }
}
And an orderDetail if shaped like this:
public class OrderDetailInfo
{
public int mOrderDetailID { get; set; }
public int mOrderDetailName { get; set; }
(...)
}
In a place in my app I have to fill this list with empty stuff. So I create a bunch of "false" orderDetails object which all have the same id: 9999. I do this because this order needs to be confirmed later on.
Precisely, when this order is confirmed, I need to remove a set number of items based on the user's input and replace them with other he created. So I tried to do this:
for (int i = 0; i < _itemQuantity; i++)
{
var emptyJug = order.mListOrderDetail.SingleOrDefault(
_item => _item.mInventoryID == 9999);
order.mListOrderDetail.Remove(emptyJug);
}
But the line crashes when the "var emptyJug" line is hit because there can be many occurrences . I need to remove one unit of emptyJug from the list until the _itemQuantity is reached. How could I do that?
Call FirstOrDefault insted of SingleOrDefault.
FirstOrDefault() is for when zero or more results are expected to be present in the collection. Call to this method returns the first item if there are multiple results, Default if none.
SingleOrDefault() is for when zero or one result is expected in the input collection. Call to this method returns the one result if exactly one result is present, Default if no results and exception if more than one result.
Try using "FirstOrDefault" instead of "SingleOrDefault".

Using 2 maps on the same source?

I have 2 maps that I want to throw into the same source. But it seems one source overrides the second source even though I am targeting different fields.
public class FormViewModel
{
public List<SelectListItem> Items { get; set; }
public string SelectedItem { get; set; }
public string SomeField {get; set;}
// I have many more
}
List<Items> items = Service.GetItems();
FormViewModel viewModel = new FormViewModel()
{
Items = Mapper.Map<List<Items>, List<SelectListItem>>(courses);
};
var fields = Service.GetFields();
viewModel = Mapper.Map<Fields, FormViewModel>(fields);
So now when I do the second map. It will wipe out my first map. How can I stop this?
Edit
I guess I can see why it is happening. I thought it was just filling in those the fields but now I am looking at it and seeing that it is return a new FormViewModel.
I guess I can rearrange my code so that I first do the last map first then add my other map after.
List<CalendarAppointmentReminderVM> vm = Mapper.Map<List<CalendarAppointment>, List<CalendarAppointmentReminderVM>>(taskReminders.Select(x => x.CalendarAppointment).ToList());
Mapper.Map<List<AppointmentReminder>, List<CalendarAppointmentReminderVM>>(taskReminders, vm);
Separately they work. But together the first result gets wiped out by the first one.
pass the output of the first mapping into the second mapping so it doesn't create a new one...
viewModel = Mapper.Map<Fields, FormViewModel>(fields, viewModel);

Categories

Resources