How to copy rows from one DataGridView to another?

Collapse
X
 
  • Time
  • Show
Clear All
new posts
  • BRawn
    New Member
    • Jul 2010
    • 28

    How to copy rows from one DataGridView to another?

    Hi guys,

    I'm struggling to copy rows from one DataGridView to another. This may sound redundant but it's necessary for my Orders project. I have 3 DataGridViews on one form.

    The first DataGridView gets populated from my database with the product and total available stock. The second DataGridView is sort of a basket...so, when an item is selected from the first DataGridView, the item gets added to the second DataGridView and there's an editable column in the second DataGridView in which the user can enter an amount they want of the product in question.

    The third one is just for show. It displays the product and the remaining stock quantity (what there was minus the amount entered by the user).

    My problem is this: My item adds fine from the 1st DataGridView to the second, but when I add more than one item and edit the quantities, it doesn't subtract from the correct product, so I'm now trying to copy the rows of my 2nd DataGridView to my 3rd one and try in baby steps, but that's not going too well. Here's what I've tried already:

    Code:
    private void copyDGVRows()
            {
                dgvProductsAfterOrders.Rows.Clear();
                for (int i = 0; i <= dgvLineItems.Rows.Count; i++)
                {
                    dgvProductsAfterOrders.Rows[i].Cells[i].Value = dgvLineItems.Rows[i].Cells[i].Value;
                    //dgvProductsAfterOrders.Rows.Add(i + 1);
                }
            }
    
            private void copyDGVRows()
            {
                dgvProductsAfterOrders.Rows.Clear();
                if (dgvLineItems.Rows.Count == 0)
                {
                    return;
                }
                else
                {
                    foreach (DataGridViewRow row in dgvLineItems.Rows)
                    {
                        row.Cells[1].Clone();
                        for (int i = 0; i < row.Cells.Count; i++)
                        {
                            dgvProductsAfterOrders.Rows[i].Cells[1].Value = row;
                            //dgvProductsAfterOrders.Rows.Add(row);
                        }
                    }
                }
            }
    
            private void copyDGVRows(DataGridView dataGridView, ArrayList rowsToCopy)
            {
                for (int i = 0; i < rowsToCopy.Count; i++)
                {
                    DataGridViewRow row = new DataGridViewRow();
                    row = (DataGridViewRow)dataGridView.Rows[Convert.ToInt32(rowsToCopy[i])].Clone();
    
                    for (int j = 0; j < row.Cells.Count; j++)
                    {
                        row.Cells[j].Value = dataGridView.Rows[Convert.ToInt32(rowsToCopy[i])].Cells[j].Value;
                        dataGridView.Rows.Add(row);
                    }
                }
            }
    
            private void populate3rdGrid()
            {
                dgvProductsAfterOrders.Rows.Clear();
                for (int i = 0; i < dgvLineItems.Rows.Count; i++)
                {
                    if (dgvProductsAfterOrders.Rows[i].Cells[0].Value != null)
                    {
                        dgvProductsAfterOrders.Rows.Add();
                        for (int j = 0; j < dgvLineItems.Rows.Count; j++)
                        {
                            dgvProductsAfterOrders.Rows[i].Cells[j].Value = dgvLineItems.Rows[i].Cells[j].Value;
                        }
                    }
                }
            }
    Nothing seems to be working...any ideas?
  • Christian Binder
    Recognized Expert New Member
    • Jan 2008
    • 218

    #2
    Maybe it's better thinking about this from the beginning.

    First of all we define what we want:
    • read available products from database
    • put products into basket
    • set/change quantity from products in basket
    • show remaining products (available minus basket)


    So we need 3 DGVs, calling them dataGridViewAva ilable, dataGridViewBas ket, dataGridViewRem aining.

    Then we need a Product, so we define a class for it, having a property Name which represents e.g. the product-name.
    Code:
    public class Product {
      public string Name { get; set; }
    
      public Product(string name) {
        Name = name;
      }
    
      public override string ToString() {
        return Name;
      }
    }
    The next thing we need is a representation of quantities of products, therefore we create another class, having Products and Quantities.
    Code:
    public class OrderItem {
      public Product Product { get; set; }
      public int Quantity { get; set; }
    
      public OrderItem(Product product, int quantity) {
        Product = product;
        Quantity = quantity;
      }
    }
    After that, we need collections, which hold the available, selected/basket and remaining OrderItems. BindingList<Ord erItem> are proper for that.
    Code:
        BindingList<OrderItem> _availableProducts = new BindingList<OrderItem>();
        BindingList<OrderItem> _basketProducts = new BindingList<OrderItem>();
        BindingList<OrderItem> _remainingProducts = new BindingList<OrderItem>();
    To show the contents of this lists to the user, we bind them to the DGVs (at initialization/constructor), using the DataSource-property.
    Code:
    dataGridViewAvailable.DataSource = _availableProducts;
    dataGridViewBasket.DataSource = _basketProducts;
    dataGridViewRemaining.DataSource = _remainingProducts;
    This forces the DataGridView to create a column for each property (of OrderItem), so we have two columns Product and Quantity.

    Now it's time to fill our _availableProdu cts from database-data. I've created a test-method which I call in constructor after data-binding.
    Code:
    void FillAvailable() {
      //Here the data would come from database
      _availableProducts.Add(new OrderItem(new Product("P1"), 100));
      _availableProducts.Add(new OrderItem(new Product("P2"), 250));
    }
    At this stage it should work that in the first DGV the right values are showing.

    When I double-click a cell in my first DGV, I want to add a OrderItem to my second DGV.
    Each dgv-row has a corresponding DataBoundItem thich is in our case of type OrderItem.
    We take this order-item, extract the product and create a new order-item with that product and a default quantity of zero
    which we than add to the basket.
    Code:
    //designer-created event-handler for CellDoubleClick
    void dataGridViewAvailable_CellDoubleClick(object sender, DataGridViewCellEventArgs e) {
      if (e.RowIndex != -1) {
        OrderItem order = dataGridViewAvailable.Rows[e.RowIndex].DataBoundItem as OrderItem;
        _basketProducts.Add(new OrderItem(order.Product, 0));
      }
    }
    Now the whole work is nearly done. Looking back to the list on this thread's beginning, there is just one point missing, namely showing the remaining products.
    For this purpose we create an update-method. This method summarizes all basket-orderitem's quantities (per Product) and deduct this sum
    from the available product-quantity.
    This can be done using some for/foreach loops and temporary lists (to group equal products).
    For reasons of lazyness, I used some Linq.
    Code:
    void UpdateRemaining() {
      _remainingProducts.Clear();
      foreach (var remainingProduct in from order in _basketProducts
                                       group order by order.Product into grp
                                       join availableProduct in _availableProducts on grp.Key equals availableProduct.Product
                                       select new OrderItem(grp.Key, availableProduct.Quantity - grp.Select(o => o.Quantity).Sum()))
        _remainingProducts.Add(remainingProduct);
    }
    The very last thing is to call this update-method when the user changes the amount of products in basket,
    simply we can use the CellValueChange d-event of dataGridViewBas ket after creating it with the designer.

    Code:
    private void dataGridViewBasket_CellValueChanged(object sender, DataGridViewCellEventArgs e) {
      UpdateRemaining();
    }
    Now the base-functionality is provided. The next steps would be increase user-friendlyness, stability and so on.
    Hope this helps for the beginning :-)

    Comment

    • BRawn
      New Member
      • Jul 2010
      • 28

      #3
      Thanks Chris. I'm gonna give this a go...tomorrow though coz it's almost hometime for me :)

      In the meantime I've devised another way around this to refresh the 3rd DataGridView's data every time the "basket's" amount cells were changed. This is what I did:

      Code:
      private void CalculateAvailableStock()
              {
                  DataTable dataTable = new DataTable();
                  DataColumn datacolumn1 = new DataColumn("Product Name", typeof(System.String)); // string FirstC=”column1″
                  DataColumn datacolumn2 = new DataColumn("Available Quantity (After Order)", typeof(System.String)); // string SecondC=”column2″
                  dataTable.Columns.Add(datacolumn1);
                  dataTable.Columns.Add(datacolumn2);
      
                  foreach (DataGridViewRow lineItemsRow in dgvLineItems.Rows)
                  {
                      int total = 0;
                      DataRow dr = dataTable.NewRow();
      
                      foreach (DataGridViewRow productsBeforeOrdersRow in this.dgvProductsBeforeOrders.Rows)
                      {
                          if (lineItemsRow.Cells[1].Value.ToString() == productsBeforeOrdersRow.Cells[1].Value.ToString())
                          {
                              int orderedQuantity = 0;
                              int availableQuantity = 0;
      
                              orderedQuantity = int.Parse(lineItemsRow.Cells[2].Value.ToString());
                              availableQuantity = int.Parse(productsBeforeOrdersRow.Cells[2].Value.ToString());
                              total = availableQuantity - orderedQuantity;
                          }
                      }
                      dataTable.Rows.Add(lineItemsRow.Cells[1].Value.ToString(), total);
                  }
                  dgvProductsAfterOrders.DataSource = dataTable;
              }
      It's probably not best practices but it works, although I think for practicality I'll give your method a go tomorrow as I like the way you use your objects.

      Thanks again :)

      Comment

      Working...