Bagikan melalui


Pengikatan data dengan WPF

Penting

Dokumen ini berlaku untuk WPF pada .NET Framework saja

Dokumen ini menjelaskan pengikatan data untuk WPF pada .NET Framework. Untuk proyek .NET Core baru, kami sarankan Anda menggunakan EF Core alih-alih Entity Framework 6. Dokumentasi untuk pengikatan data di EF Core ada di sini: Memulai WPF.

Panduan langkah demi langkah ini menunjukkan cara mengikat jenis POCO ke kontrol WPF dalam formulir "detail master". Aplikasi ini menggunakan API Kerangka Kerja Entitas untuk mengisi objek dengan data dari database, melacak perubahan, dan menyimpan data ke database.

Model mendefinisikan dua jenis yang berpartisipasi dalam hubungan satu-ke-banyak: Kategori (utama\master) dan Produk (dependent\detail). Kemudian, alat Visual Studio digunakan untuk mengikat jenis yang ditentukan dalam model ke kontrol WPF. Kerangka kerja pengikatan data WPF memungkinkan navigasi antara objek terkait: memilih baris dalam tampilan master menyebabkan tampilan detail diperbarui dengan data anak yang sesuai.

Cuplikan layar dan daftar kode dalam panduan ini diambil dari Visual Studio 2013 tetapi Anda dapat menyelesaikan panduan ini dengan Visual Studio 2012 atau Visual Studio 2010.

Gunakan Opsi 'Objek' untuk Membuat Sumber Data WPF

Dengan versi Kerangka Kerja Entitas sebelumnya, kami menggunakan opsi Database saat membuat Sumber Data baru berdasarkan model yang dibuat dengan Desainer EF. Ini karena perancang akan menghasilkan konteks yang berasal dari ObjectContext dan kelas entitas yang berasal dari EntityObject. Menggunakan opsi Database akan membantu Anda menulis kode terbaik untuk berinteraksi dengan permukaan API ini.

Desainer EF untuk Visual Studio 2012 dan Visual Studio 2013 menghasilkan konteks yang berasal dari DbContext bersama dengan kelas entitas POCO sederhana. Dengan Visual Studio 2010, sebaiknya bertukar ke templat pembuatan kode yang menggunakan DbContext seperti yang dijelaskan nanti dalam panduan ini.

Saat menggunakan permukaan DbContext API, Anda harus menggunakan opsi Objek saat membuat Sumber Data baru, seperti yang ditunjukkan dalam panduan ini.

Jika diperlukan, Anda dapat kembali ke pembuatan kode berbasis ObjectContext untuk model yang dibuat dengan EF Designer.

Prasyarat

Anda harus menginstal Visual Studio 2013, Visual Studio 2012, atau Visual Studio 2010 untuk menyelesaikan panduan ini.

Jika Anda menggunakan Visual Studio 2010, Anda juga harus menginstal NuGet. Untuk informasi selengkapnya, lihat Menginstal NuGet.  

Membuat Aplikasi

  • Membuka Visual Studio
  • File -> Baru -> Proyek...
  • Pilih Windows di panel kiri dan WPFAplikasi di panel kanan
  • Masukkan WPFwithEFSample sebagai nama
  • Pilih OK

Menginstal paket Entity Framework NuGet

  • Di Penjelajah Solusi, klik kanan pada proyek WinFormswithEFSample
  • Pilih Kelola Paket NuGet...
  • Dalam dialog Kelola Paket NuGet, Pilih tab Online dan pilih paket EntityFramework
  • Klik Instal

    Catatan

    Selain rakitan EntityFramework, referensi ke System.ComponentModel.DataAnnotations juga ditambahkan. Jika proyek memiliki referensi ke System.Data.Entity, maka proyek akan dihapus saat paket EntityFramework diinstal. Rakitan System.Data.Entity tidak lagi digunakan untuk aplikasi Entity Framework 6.

Tentukan Model

Dalam panduan ini, Anda dapat memilih untuk menerapkan model menggunakan Code First atau EF Designer. Lengkapi salah satu dari dua bagian berikut.

Opsi 1: Tentukan Model menggunakan Kode Terlebih Dahulu

Bagian ini memperlihatkan cara membuat model dan database terkait menggunakan Kode Pertama. Lewati ke bagian berikutnya (Opsi 2: Tentukan model menggunakan Database First) jika Anda lebih suka menggunakan Database First untuk merekayasa balik model Anda dari database menggunakan perancang EF

Saat menggunakan pengembangan Code First, Anda biasanya mulai dengan menulis kelas .NET Framework yang menentukan model konseptual (domain) Anda.

  • Tambahkan kelas baru ke WPFwithEFSample:
    • Klik kanan pada nama proyek
    • Pilih Tambahkan, lalu Item Baru
    • Pilih Kelas dan masukkan Produk untuk nama kelas
  • Ganti definisi kelas Produk dengan kode berikut:
    namespace WPFwithEFSample
    {
        public class Product
        {
            public int ProductId { get; set; }
            public string Name { get; set; }

            public int CategoryId { get; set; }
            public virtual Category Category { get; set; }
        }
    }
  • Tambahkan kelas Kategori dengan definisi berikut:
    using System.Collections.ObjectModel;

    namespace WPFwithEFSample
    {
        public class Category
        {
            public Category()
            {
                this.Products = new ObservableCollection<Product>();
            }

            public int CategoryId { get; set; }
            public string Name { get; set; }

            public virtual ObservableCollection<Product> Products { get; private set; }
        }
    }

Properti Produk pada kelas Kategori dan properti Kategori pada kelas Produk adalah properti navigasi. Dalam Kerangka Kerja Entitas, properti navigasi menyediakan cara untuk menavigasi hubungan antara dua jenis entitas.

Selain menentukan entitas, Anda perlu menentukan kelas yang berasal dari DbContext dan mengekspos properti DbSet<TEntity> . Properti DbSet<TEntity> memberi tahu konteks jenis mana yang ingin Anda sertakan dalam model.

Instans jenis turunan DbContext mengelola objek entitas selama run time, yang mencakup mengisi objek dengan data dari database, pelacakan perubahan, dan menyimpan data ke database.

  • Tambahkan kelas ProductContext baru ke proyek dengan definisi berikut:
    using System.Data.Entity;

    namespace WPFwithEFSample
    {
        public class ProductContext : DbContext
        {
            public DbSet<Category> Categories { get; set; }
            public DbSet<Product> Products { get; set; }
        }
    }

Mengompilasi proyek.

Opsi 2: Tentukan model menggunakan Database First

Bagian ini memperlihatkan cara menggunakan Database First untuk merekayasa balik model Anda dari database menggunakan perancang EF. Jika Anda menyelesaikan bagian sebelumnya (Opsi 1: Tentukan model menggunakan Kode Pertama), lewati bagian ini dan langsung masuk ke bagian Pemuatan Malas .

Membuat Database yang Sudah Ada

Biasanya ketika Anda menargetkan database yang sudah ada, database tersebut sudah akan dibuat, tetapi untuk panduan ini kita perlu membuat database untuk diakses.

Server database yang diinstal dengan Visual Studio berbeda tergantung pada versi Visual Studio yang telah Anda instal:

  • Jika Anda menggunakan Visual Studio 2010, Anda akan membuat database SQL Express.
  • Jika Anda menggunakan Visual Studio 2012, Maka Anda akan membuat database LocalDB .

Mari kita lanjutkan dan hasilkan database.

  • Tampilan -> Penjelajah Server

  • Klik kanan pada Data Koneksi ions -> Tambahkan Koneksi ion...

  • Jika Anda belum tersambung ke database dari Server Explorer sebelum Anda harus memilih Microsoft SQL Server sebagai sumber data

    Change Data Source

  • Koneksi ke LocalDB atau SQL Express, tergantung pada mana yang telah Anda instal, dan masukkan Produk sebagai nama database

    Add Connection LocalDB

    Add Connection Express

  • Pilih OK dan Anda akan ditanya apakah Anda ingin membuat database baru, pilih Ya

    Create Database

  • Database baru sekarang akan muncul di Server Explorer, klik kanan padanya dan pilih Kueri Baru

  • Salin SQL berikut ke dalam kueri baru, lalu klik kanan pada kueri dan pilih Jalankan

    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])

    ALTER TABLE [dbo].[Products] ADD CONSTRAINT [FK_dbo.Products_dbo.Categories_CategoryId] FOREIGN KEY ([CategoryId]) REFERENCES [dbo].[Categories] ([CategoryId]) ON DELETE CASCADE

Reverse Engineer Model

Kita akan menggunakan Entity Framework Designer, yang disertakan sebagai bagian dari Visual Studio, untuk membuat model kita.

  • Project -> Tambahkan Item Baru...

  • Pilih Data dari menu sebelah kiri lalu ADO.NET Model Data Entitas

  • Masukkan ProductModel sebagai nama dan klik OK

  • Ini meluncurkan Wizard Model Data Entitas

  • Pilih Buat dari Database dan klik Berikutnya

    Choose Model Contents

  • Pilih koneksi ke database yang Anda buat di bagian pertama, masukkan ProductContext sebagai nama string koneksi dan klik Berikutnya

    Choose Your Connection

  • Klik kotak centang di samping 'Tabel' untuk mengimpor semua tabel dan klik 'Selesai'

    Choose Your Objects

Setelah proses insinyur terbalik menyelesaikan model baru ditambahkan ke proyek Anda dan dibuka untuk Anda lihat di Perancang Kerangka Kerja Entitas. File App.config juga telah ditambahkan ke proyek Anda dengan detail koneksi untuk database.

Langkah Tambahan di Visual Studio 2010

Jika Anda bekerja di Visual Studio 2010 maka Anda harus memperbarui perancang EF untuk menggunakan pembuatan kode EF6.

  • Klik kanan pada tempat kosong model Anda di Perancang EF dan pilih Tambahkan Item Pembuatan Kode...
  • Pilih Templat Online dari menu sebelah kiri dan cari DbContext
  • Pilih Generator EF 6.x DbContext untuk C#, masukkan ProductsModel sebagai nama dan klik Tambahkan

Memperbarui pembuatan kode untuk pengikatan data

EF menghasilkan kode dari model Anda menggunakan templat T4. Templat yang dikirim dengan Visual Studio atau diunduh dari galeri Visual Studio ditujukan untuk penggunaan tujuan umum. Ini berarti bahwa entitas yang dihasilkan dari templat ini memiliki properti T> ICollection<sederhana. Namun, ketika melakukan pengikatan data menggunakan WPF, diinginkan untuk menggunakan ObservableCollection untuk properti pengumpulan sehingga WPF dapat melacak perubahan yang dilakukan pada koleksi. Untuk akhir ini, kita akan memodifikasi templat untuk menggunakan ObservableCollection.

  • Buka Penjelajah Solusi dan temukan file ProductModel.edmx

  • Temukan file ProductModel.tt yang akan disarangkan di bawah file ProductModel.edmx

    WPF Product Model Template

  • Klik dua kali pada file ProductModel.tt untuk membukanya di editor Visual Studio

  • Temukan dan ganti dua kemunculan "ICollection" dengan "ObservableCollection". Ini terletak sekitar pada baris 296 dan 484.

  • Temukan dan ganti kemunculan pertama "HashSet" dengan "ObservableCollection". Kejadian ini terletak sekitar pada baris 50. Jangan ganti kemunculan kedua HashSet yang ditemukan nanti dalam kode.

  • Temukan dan ganti satu-satunya kemunculan "System.Collections.Generic" dengan "System.Collections.ObjectModel". Ini terletak sekitar pada baris 424.

  • Simpan file ProductModel.tt. Ini harus menyebabkan kode untuk entitas diregenerasi. Jika kode tidak diregenerasi secara otomatis, klik kanan pada ProductModel.tt dan pilih "Jalankan Alat Kustom".

Jika Anda sekarang membuka file Category.cs (yang disarangkan di bawah ProductModel.tt) maka Anda akan melihat bahwa koleksi Produk memiliki jenis Produk> ObservableCollection<.

Mengompilasi proyek.

Pemuatan Lambat

Properti Produk pada kelas Kategori dan properti Kategori pada kelas Produk adalah properti navigasi. Dalam Kerangka Kerja Entitas, properti navigasi menyediakan cara untuk menavigasi hubungan antara dua jenis entitas.

EF memberi Anda opsi untuk memuat entitas terkait dari database secara otomatis saat pertama kali Anda mengakses properti navigasi. Dengan jenis pemuatan ini (disebut pemuatan malas), ketahuilah bahwa pertama kali Anda mengakses setiap properti navigasi, kueri terpisah akan dijalankan terhadap database jika konten belum dalam konteks.

Saat menggunakan jenis entitas POCO, EF mencapai pemuatan malas dengan membuat instans jenis proksi turunan selama runtime lalu menimpa properti virtual di kelas Anda untuk menambahkan hook pemuatan. Untuk mendapatkan pemuatan objek terkait yang malas, Anda harus mendeklarasikan getter properti navigasi sebagai publik dan virtual (Dapat Diganti di Visual Basic), dan kelas Anda tidak boleh disegel (NotOverridable di Visual Basic). Saat menggunakan properti navigasi Database First secara otomatis dibuat virtual untuk mengaktifkan pemuatan malas. Di bagian Kode Pertama, kami memilih untuk membuat properti navigasi virtual karena alasan yang sama.

Mengikat Objek ke Kontrol

Tambahkan kelas yang didefinisikan dalam model sebagai sumber data untuk aplikasi WPF ini.

  • Klik dua kali MainWindow.xaml di Penjelajah Solusi untuk membuka formulir utama

  • Dari menu utama, pilih Proyek -> Tambahkan Sumber Data Baru ... (di Visual Studio 2010, Anda perlu memilih Data -> Tambahkan Sumber Data Baru...)

  • Di Typewindow Pilih Sumber Data, pilih Objek dan klik Berikutnya

  • Dalam dialog Pilih Objek Data, buka WPFwithEFSample dua kali dan pilih Kategori
    Tidak perlu memilih sumber data Produk, karena kita akan mendapatkannya melalui properti Produk pada sumber data Kategori

    Select Data Objects

  • Klik Selesai.

  • Jendela Sumber Data dibuka di samping jendela MainWindow.xaml Jika jendela Sumber Data tidak muncul, pilih Tampilkan -> Sumber Data Windows> Lainnya

  • Tekan ikon sematkan, sehingga jendela Sumber Data tidak disembunyikan secara otomatis. Anda mungkin perlu menekan tombol refresh jika jendela sudah terlihat.

    Data Sources

  • Pilih sumber data Kategori dan seret pada formulir.

Berikut ini terjadi ketika kami menyeret sumber ini:

  • Sumber daya categoryViewSource dan kontrol categoryDataGrid ditambahkan ke XAML
  • Properti DataContext pada elemen Grid induk diatur ke "{StaticResource categoryViewSource }". Sumber daya categoryViewSource berfungsi sebagai sumber pengikatan untuk elemen Grid luar\induk. Elemen Kisi dalam kemudian mewarisi nilai DataContext dari Kisi induk (properti ItemsSource categoryDataGrid diatur ke "{Binding}")
    <Window.Resources>
        <CollectionViewSource x:Key="categoryViewSource"
                                d:DesignSource="{d:DesignInstance {x:Type local:Category}, CreateList=True}"/>
    </Window.Resources>
    <Grid DataContext="{StaticResource categoryViewSource}">
        <DataGrid x:Name="categoryDataGrid" AutoGenerateColumns="False" EnableRowVirtualization="True"
                    ItemsSource="{Binding}" Margin="13,13,43,191"
                    RowDetailsVisibilityMode="VisibleWhenSelected">
            <DataGrid.Columns>
                <DataGridTextColumn x:Name="categoryIdColumn" Binding="{Binding CategoryId}"
                                    Header="Category Id" Width="SizeToHeader"/>
                <DataGridTextColumn x:Name="nameColumn" Binding="{Binding Name}"
                                    Header="Name" Width="SizeToHeader"/>
            </DataGrid.Columns>
        </DataGrid>
    </Grid>

Menambahkan Kisi Detail

Sekarang setelah kita memiliki kisi untuk menampilkan Kategori mari kita tambahkan kisi detail untuk menampilkan Produk terkait.

  • Pilih properti Produk dari bawah sumber data Kategori dan seret pada formulir.
    • Sumber daya categoryProductsViewSource dan kisi productDataGrid ditambahkan ke XAML
    • Jalur pengikatan untuk sumber daya ini diatur ke Produk
    • Kerangka kerja pengikatan data WPF memastikan bahwa hanya Produk yang terkait dengan Kategori yang dipilih yang muncul di productDataGrid
  • Dari Kotak Alat, seret Tombol ke formulir. Atur properti Nama ke tombolSimpan dan properti Konten ke Simpan.

Formulir akan terlihat mirip dengan ini:

Designer Form

Menambahkan Kode yang Menangani Interaksi Data

Saatnya untuk menambahkan beberapa penanganan aktivitas ke jendela utama.

  • Di jendela XAML, klik <elemen Window , ini memilih jendela utama

  • Di jendela Properti pilih Peristiwa di kanan atas, lalu klik ganda kotak teks di sebelah kanan label Dimuat

    Main Window Properties

  • Tambahkan juga peristiwa Klik untuk tombol Simpan dengan mengklik dua kali tombol Simpan di perancang.

Ini membawa Anda ke kode di belakang untuk formulir, kami sekarang akan mengedit kode untuk menggunakan ProductContext untuk melakukan akses data. Perbarui kode untuk MainWindow seperti yang ditunjukkan di bawah ini.

Kode mendeklarasikan instans ProductContext yang berjalan lama. Objek ProductContext digunakan untuk mengkueri dan menyimpan data ke database. Dispose() pada instans ProductContext kemudian dipanggil dari metode OnClosing yang ditimpa. Komentar kode memberikan detail tentang apa yang dilakukan kode.

    using System.Data.Entity;
    using System.Linq;
    using System.Windows;

    namespace WPFwithEFSample
    {
        public partial class MainWindow : Window
        {
            private ProductContext _context = new ProductContext();
            public MainWindow()
            {
                InitializeComponent();
            }

            private void Window_Loaded(object sender, RoutedEventArgs e)
            {
                System.Windows.Data.CollectionViewSource categoryViewSource =
                    ((System.Windows.Data.CollectionViewSource)(this.FindResource("categoryViewSource")));

                // Load is an extension method on IQueryable,
                // defined in the System.Data.Entity namespace.
                // This method enumerates the results of the query,
                // similar to ToList but without creating a list.
                // When used with Linq to Entities this method
                // creates entity objects and adds them to the context.
                _context.Categories.Load();

                // After the data is loaded call the DbSet<T>.Local property
                // to use the DbSet<T> as a binding source.
                categoryViewSource.Source = _context.Categories.Local;
            }

            private void buttonSave_Click(object sender, RoutedEventArgs e)
            {
                // When you delete an object from the related entities collection
                // (in this case Products), the Entity Framework doesn’t mark
                // these child entities as deleted.
                // Instead, it removes the relationship between the parent and the child
                // by setting the parent reference to null.
                // So we manually have to delete the products
                // that have a Category reference set to null.

                // The following code uses LINQ to Objects
                // against the Local collection of Products.
                // 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 use LINQ to Objects directly
                // against the Local property without using ToList first.
                foreach (var product in _context.Products.Local.ToList())
                {
                    if (product.Category == null)
                    {
                        _context.Products.Remove(product);
                    }
                }

                _context.SaveChanges();
                // Refresh the grids so the database generated values show up.
                this.categoryDataGrid.Items.Refresh();
                this.productsDataGrid.Items.Refresh();
            }

            protected override void OnClosing(System.ComponentModel.CancelEventArgs e)
            {
                base.OnClosing(e);
                this._context.Dispose();
            }
        }

    }

Menguji Aplikasi WPF

  • Kompilasi dan jalankan aplikasi. Jika Anda menggunakan Code First, maka Anda akan melihat bahwa database WPFwithEFSample.ProductContext dibuat untuk Anda.

  • Masukkan nama kategori di kisi atas dan nama produk di kisi bawah Jangan masukkan apa pun di kolom ID, karena kunci utama dihasilkan oleh database

    Main Window with new categories and products

  • Tekan tombol Simpan untuk menyimpan data ke database

Setelah panggilan ke SaveChanges()DbContext, ID diisi dengan nilai yang dihasilkan database. Karena kami disebut Refresh() setelah SaveChanges() kontrol DataGrid juga diperbarui dengan nilai baru.

Main Window with IDs populated

Sumber Daya Tambahan

Untuk mempelajari selengkapnya tentang pengikatan data ke koleksi menggunakan WPF, lihat topik ini dalam dokumentasi WPF.