单个 DataGridView 中的两个表的数据






4.71/5 (7投票s)
在一个 DataGridView 中显示、编辑和删除两个表中的数据
引言
可以对两个表之间存在 SQL join 关系的数据进行显示,并在单个 DataGridView 中执行 CRUD 操作。有很多场景可以证明这个概念的用处。
本文讨论了该实现的概念。我参考了微软 Beth Massi 在她的 MSDN 博客上发表的一篇类似的文章,该文章采用了不同的方法。
在本文中,我使用了一个数据传输对象 (DTO) 及其类。该类包含要显示在 DataGridView 上的两个表中所有字段的“union”。
然后,数据传输对象集合将作为相关 DataGridView 的 datasource。可以通过这个数据传输对象实现“编辑”、“更新”和“删除”操作。
背景
有关本文的背景信息,请参考以下链接,其中包含一些关于许多用户提到的问题的详细信息。
- http://www.vbforums.com/archive/index.php/t-253655.html
- http://aspalliance.com/148_Merging_two_Datasets_into_a_single_Datagrid
 
 
Using the Code
在本演示中,我使用了 Northwind.mdf 数据库作为示例。然后我使用 Entity Framework 作为 ORM 工具,并创建了一个泛型 List
//
// Composite class for the DTO
//
  public class OrdersDetails
  {
        public int OrderID { get; set; }
        public DateTime? OrderDate { get; set; }
        public int Quantity { get; set; }
        public float Discount { get; set; }
        public decimal UnitPrice { get; set; }
        public string ShipName { get; set; }
        public string ShipAddress { get; set; }
        public string ShipCity { get; set; }
        public string ShipCountry { get; set; }
        public DateTime? ShippedDate { get; set; }
  }
  public static class Operations
  {
        public static void FetchOrders()
        {
        }
  }
在 DataGridView 上显示数据的代码
最后,这段代码处理 DataGridView 上的添加、编辑、保存、删除和取消事件
//
// Load, Save, Delete and Cancel events & methods for the DataGridView.
//
//Method which loads data into the DataGridView control using the "OrdersDetails" object.
        private void frmMultiTable_Load(object sender, EventArgs e)
        {
            LoadDataIntoGrid();
            toolStripSave.Enabled = false;
            toolStripCancel.Enabled = false;
        }
        private void LoadDataIntoGrid()
        {
            List<OrdersDetails> ordersCollection = new List<OrdersDetails>();
            NORTHWINDEntities context = new NORTHWINDEntities();
            context.Connection.Open();
            int counter = context.Orders.Count();
            for (int i = 0; i < context.Orders.Count() - 1; i++)
            {
                // AutoMapper library can be used alternatively to perform this mapping.
                OrdersDetails item = new OrdersDetails();
                item.OrderID = context.Orders.ToList()[i].OrderID;
                item.OrderDate = context.Orders.ToList()[i].OrderDate;
                item.Quantity = context.Order_Details.ToList()[i].Quantity;
                item.Discount = context.Order_Details.ToList()[i].Discount;
                item.UnitPrice = context.Order_Details.ToList()[i].UnitPrice;
                item.ShipName = context.Orders.ToList()[i].ShipName;
                item.ShipAddress = context.Orders.ToList()[i].ShipAddress;
                item.ShipCity = context.Orders.ToList()[i].ShipCity;
                item.ShipCountry = context.Orders.ToList()[i].ShipCountry;
                item.ShippedDate = context.Orders.ToList()[i].ShippedDate;
                ordersCollection.Add(item);
                item = null;
            }
            gridbindingSource.DataSource = ordersCollection;
            multiTabledataGridView.DataSource = null;
            multiTabledataGridView.DataSource = gridbindingSource;
            multiTabledataGridView.Refresh();
            statusStrip1.Text = counter + " Item(s)";
            context.Connection.Close();
            context.Connection.Dispose();
            context.Dispose();
        }
//Save Event for the DataGridView control.
        private void toolStripSave_Click(object sender, EventArgs e)
        {
            multiTabledataGridView.EndEdit();
            SaveDatafromGrid();
            
            toolStripSave.Enabled = false;
            toolStripCancel.Enabled = false;
        }
//Method which saves the data from the DataGridView into the database 
//via the "OrdersDetails" object.
        private void SaveDatafromGrid()
        {
            List<OrdersDetails> ordersCollection = new List<OrdersDetails>();
            NORTHWINDEntities context = new NORTHWINDEntities();
            ordersCollection = (List<OrdersDetails>) gridbindingSource.DataSource;
            context.Connection.Open();
            for (int i = 0; i < context.Orders.Count() - 1; i++)
            {
                // AutoMapper library can be used alternatively to perform this mapping.
                OrdersDetails item = new OrdersDetails();
                context.Orders.ToList()[i].OrderDate = 
				ordersCollection.ToList()[i].OrderDate;
                context.Orders.ToList()[i].ShipName = 
				ordersCollection.ToList()[i].ShipName;
                context.Orders.ToList()[i].ShipAddress = 
				ordersCollection.ToList()[i].ShipAddress;
                context.Orders.ToList()[i].ShipCity = 
				ordersCollection.ToList()[i].ShipCity;
                context.Orders.ToList()[i].ShipCountry = 
				ordersCollection.ToList()[i].ShipCountry;
                context.Orders.ToList()[i].ShippedDate = 
				ordersCollection.ToList()[i].ShippedDate;
                context.Order_Details.ToList()[i].Quantity = 
				(short)ordersCollection.ToList()[i].Quantity;
                context.Order_Details.ToList()[i].Discount = 
				ordersCollection.ToList()[i].Discount;
                context.Order_Details.ToList()[i].UnitPrice = 
				ordersCollection.ToList()[i].UnitPrice;
            }
            gridbindingSource.DataSource = ordersCollection;
            multiTabledataGridView.DataSource = null;
            multiTabledataGridView.DataSource = gridbindingSource;
            multiTabledataGridView.Refresh();
            context.SaveChanges();
            context.Connection.Close();
            context.Connection.Dispose();
            context.Dispose();
        }
//Delete Event for the DataGridView control.
        private void toolStripDelete_Click(object sender, EventArgs e)
        {
            if (multiTabledataGridView.CurrentCell.RowIndex > -1)
            {
                DeleteDatafromGrid(multiTabledataGridView.CurrentCell.RowIndex);
            }
        }
//Method which deletes the data from the DataGridView and also from the database.
        private void DeleteDatafromGrid(int rowIndex)
        {
            List<OrdersDetails> ordersCollection; // = new List<OrdersDetails>();
            NORTHWINDEntities context = new NORTHWINDEntities();
            ordersCollection = (List<OrdersDetails>)gridbindingSource.DataSource;
            context.Connection.Open();
            int orderId;
            //Retrieve the OrderID
            orderId = ordersCollection.ElementAt(rowIndex).OrderID;
            var order = (from o in context.Orders
                         where o.OrderID == orderId
                         select o).First();
            //Delete the row from Order_Details child table first & 
            //then from the Orders table.
            if (!order.Order_Details.IsLoaded)
                order.Order_Details.Load();
            int counter = order.Order_Details.Count();
            for (int i = 0; i < counter; i++)
            {
                context.DeleteObject(order.Order_Details.First());
            }
            context.SaveChanges();
            context.DeleteObject(order);
            
            gridbindingSource.Remove(gridbindingSource.Current);
            multiTabledataGridView.Refresh();
            context.SaveChanges();
            context.Connection.Close();
            context.Connection.Dispose();
            context.Dispose();
        }
//Cancel Event of the DataGridView control.
        private void toolStripCancel_Click(object sender, EventArgs e)
        {
            multiTabledataGridView.CancelEdit();
            toolStripSave.Enabled = false;
            toolStripCancel.Enabled = false;
        }
在上述 Load、Save 和 Delete 方法中,可以使用 AutoMapper 库设置 "OrdersDetails" 对象的字段,从而减少您需要编写的代码量。
好了,这就是全部!一个多表 DataGridView 控件,显示具有 SQL join 关系的两个表中的数据。
关注点
从这段代码中学到的有趣概念是使用数据传输对象 (DTO) 或类似 ViewModel 的类来完成在单个 DataGridView 控件上显示两个表的任务。
历史
- 版本 1.0,包含初始概念


