IListSource 實作集合
- 這個類別會啟用雙向資料繫結,以及排序。
- 類別衍生自 ObservableCollection<T>並新增明確的 IListSource 實作。
- IListSource getlist() 方法實作傳回 IBindingList 實作,它與 ObservableCollection 保持同步。
- 產生 ToBindingList IBindingList 實作支援排序。
- EntityFramework 組件中定義 ToBindingList 擴充方法。
// ObservableCollection<T> 所在 namespace
using System.Collections.ObjectModel;
// IListSource 所在 namespace
using System.ComponentModel;
// IList 所在 namespace
using System.Collections;
// EntityFramework 組件中定義 ToBindingList 擴充方法
using System.Data.Entity;
namespace WinFormswithEFSample
{
public class ObservableListSource<T> : ObservableCollection<T>, IListSource
where T : class
{
private IBindingList _bindingList;
public bool ContainsListCollection
{
get { return false; }
}
public IList GetList()
{
return _bindingList ?? (_bindingList = this.ToBindingList());
}
}
}
定義 Model
namespace WinFormswithEFSample.Models
{
using System.ComponentModel.DataAnnotations;
public partial class Products
{
[Key]
public int ProductId { get; set; }
public string Name { get; set; }
public int CategoryId { get; set; }
public virtual Categories Categories { get; set; }
}
}
namespace WinFormswithEFSample.Models
{
using System.ComponentModel.DataAnnotations;
public partial class Categories
{
private readonly ObservableListSource<Products> _products =
new ObservableListSource<Products>();
[Key]
public int CategoryId { get; set; }
public string Name { get; set; }
public virtual ObservableListSource<Products> Products { get { return _products; } }
}
}
namespace WinFormswithEFSample.Models
{
using System.Data.Entity;
public partial class DataBindingContext : DbContext
{
public DataBindingContext()
: base("name=DataBindingContext")
{
}
public virtual DbSet<Categories> Categories { get; set; }
public virtual DbSet<Products> Products { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
}
}
}
CREATE TABLE [dbo].[Categories] (
[CategoryId] [int] NOT NULL IDENTITY,
[Name] [nvarchar](max),
CONSTRAINT [PK_dbo.Categories] PRIMARY KEY ([CategoryId])
)
CREATE TABLE [dbo].[Products] (
[ProductId] [int] NOT NULL IDENTITY,
[Name] [nvarchar](max),
[CategoryId] [int] NOT NULL,
CONSTRAINT [PK_dbo.Products] PRIMARY KEY ([ProductId])
)
CREATE INDEX [IX_CategoryId] ON [dbo].[Products]([CategoryId])
-- 有啟用 FK 的 DELETE CASCADE 功能
ALTER TABLE [dbo].[Products] ADD CONSTRAINT [FK_dbo.Products_dbo.Categories_CategoryId] FOREIGN KEY ([CategoryId]) REFERENCES [dbo].[Categories] ([CategoryId]) ON DELETE CASCADE
物件繫結至控制項
在資料來源視窗內新增
利用 [物件] 來新增
選擇 Categories Class 為資料來源,萬一 WinFormswithEFSample 沒有出現,請先 build project 後就會看見啦
資料來源設定完成
直接拖曳進 WinForm 就會產生 DataGridView 和對應的 BindingSource
WinForm Code
using WinFormswithEFSample.Models;
using System.Data.Entity;
namespace WinFormswithEFSample
{
public partial class Form1 : Form
{
private DataBindingContext _context;
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
}
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
_context = new DataBindingContext();
// Call the Load method to get the data for the given DbSet
// from the database.
// The data is materialized as entities. The entities are managed by
// the DbContext instance.
// 利用 Load() 載進 Categories 內資料
// Load() 和 ToList() 差異
// ToList() 會產生 List<T>
// Load 不會產生 List<T>,只會把資料載進 Local 內而已
_context.Categories.Load();
// Bind the categoryBindingSource.DataSource to
// all the Unchanged, Modified and Added Category objects that
// are currently tracked by the DbContext.
// Note that we need to call ToBindingList() on the
// ObservableCollection<TEntity> returned by
// the DbSet.Local property to get the BindingList<T>
// in order to facilitate two-way binding in WinForms.
this.categoriesBindingSource.DataSource =
_context.Categories.Local.ToBindingList();
}
private void categoriesBindingNavigatorSaveItem_Click(object sender, EventArgs e)
{
this.Validate();
// Currently, the Entity Framework doesn’t mark the entities
// that are removed from a navigation property (in our example the Products)
// as deleted in the context.
// The following code uses LINQ to Objects against the Local collection
// to find all products and marks any that do not have
// a Category reference as deleted.
// The ToList call is required because otherwise
// the collection will be modified
// by the Remove call while it is being enumerated.
// In most other situations you can do LINQ to Objects directly
// against the Local property without using ToList first.
// 導覽屬性並不會把 EntityState 設為 deleted,因此在 SaveChange 前必須手動移除
foreach (var product in _context.Products.Local.ToList())
{
if (product.Categories == null)
{
_context.Products.Remove(product);
}
}
// Save the changes to the database.
this._context.SaveChanges();
// Refresh the controls to show the values
// that were generated by the database.
this.categoriesDataGridView.Refresh();
this.productsDataGridView.Refresh();
}
protected override void OnClosing(CancelEventArgs e)
{
base.OnClosing(e);
_context.Dispose();
}
}
}
執行結果- Master 會自動顯示 Detail 資料
- 執行 SaveChange 時 Category、Product 資料都會一併存進 DB 內
沒有留言:
張貼留言