Bagikan melalui


Membuat aplikasi data dasar dengan WPF dan Entity Framework 6

Nota

Jika Anda menjalankan Visual Studio 2022, pastikan Anda menggunakan versi 17.3 atau yang lebih baru untuk tutorial ini.

Tutorial ini menunjukkan cara membuat formulir dasar melalui aplikasi data di Visual Studio. Aplikasi ini menggunakan SQL Server LocalDB, database Northwind, Entity Framework 6 (bukan Entity Framework Core), dan Windows Presentation Foundation (WPF) untuk .NET Framework (bukan .NET Core atau .NET 5 atau yang lebih baru). Ini menunjukkan cara melakukan pengikatan data dasar dengan tampilan detail master, dan menyertakan kontrol BindingNavigator kustom dengan tombol yang melakukan hal berikut: pindah ke pertama, pindahkan sebelumnya, pindah berikutnya, pindah ke terakhir, hapus, tambahkan, atur urutan baru, perbarui, dan batalkan.

Tutorial ini berfokus pada penggunaan alat data di Visual Studio, dan tidak mencoba menjelaskan teknologi yang mendasar secara mendalam. Ini mengasumsikan bahwa Anda memiliki keakraban dasar dengan Extensible Application Markup Language (XAML), Entity Framework, dan SQL. Meskipun kode dalam tutorial ini tidak menunjukkan arsitektur Model-View-ViewModel (MVVM), yang merupakan standar untuk aplikasi WPF, Anda dapat menyalin kode dengan beberapa modifikasi ke dalam aplikasi MVVM Anda sendiri.

Untuk melihat kode akhir untuk tutorial ini, lihat Sampel tutorial Visual Studio - EF6.

Di tutorial ini, Anda akan:

  • Menginstal dan menyambungkan ke Northwind
  • Mengonfigurasi proyek aplikasi WPF
  • Membuat model data entitas ADO.NET
  • Data mengikat model ke halaman XAML
  • Menyesuaikan desain halaman dan menambahkan kisi
  • Menambahkan tombol untuk menavigasi, menambahkan, memperbarui, dan menghapus
  • Menjalankan aplikasi WPF

Prasyarat

  • Visual Studio dengan beban kerja Pengembangan Desktop .NET terinstal, dan komponen Windows Communication Foundation terinstal. Untuk menginstalnya:

    1. Buka aplikasi Alat Penginstal Visual Studio , atau pilih Alat>Dapatkan Alat dan Fitur dari menu Visual Studio.
    2. Di Penginstal Visual Studio, pilih Ubah di samping versi Visual Studio yang ingin Anda ubah.
    3. Pilih tab Komponen individual , lalu pilih Windows Communication Foundation di bawah Aktivitas pengembangan.
    4. Pilih Ubah.
  • SQL Server Express LocalDB. Jika Anda tidak memiliki SQL Server Express LocalDB, Anda dapat menginstalnya dari halaman unduhan SQL Server. Atau, Anda dapat menginstalnya dengan aplikasi Penginstal Visual Studio sebagai komponen individual.

  • SQL Server Object Explorer. Untuk menginstalnya, instal beban kerja Penyimpanan dan pemrosesan data di aplikasi Alat Penginstal Visual Studio .

  • Perangkat Entity Framework 6. Ini biasanya diinstal saat menginstal beban kerja Pengembangan .NET Desktop.

Menginstal dan menyambungkan ke Northwind

Contoh berikut menggunakan SQL Server Express LocalDB dan database sampel Northwind. Jika penyedia data ADO.NET untuk produk tersebut mendukung Kerangka Kerja Entitas, itu harus bekerja dengan produk database SQL lainnya juga.

Instal database sampel Northwind dengan mengikuti langkah-langkah berikut:

  1. Di Visual Studio, buka jendela SQL Server Object Explorer dari menu Tampilan . Perluas node SQL Server. Klik kanan instans LocalDB Anda dan pilih Kueri Baru.

    Jendela editor kueri terbuka.

  2. Salin skrip Northwind Transact-SQL (T-SQL) ke clipboard Anda.

  3. Tempelkan skrip T-SQL ke editor kueri, lalu pilih Jalankan.

    Kueri skrip T-SQL membuat database Northwind dan mengisinya dengan data.

  4. Tambahkan koneksi baru untuk database Northwind.

Mengonfigurasi proyek aplikasi WPF

Untuk mengonfigurasi proyek aplikasi WPF, ikuti langkah-langkah berikut:

  1. Di Visual Studio, buat proyek Aplikasi C# WPF (.NET Framework) baru.

  2. Tambahkan paket NuGet untuk Entity Framework 6. Di Penjelajah Solusi, pilih simpul proyek. Di menu utama, pilih Project>Kelola Paket NuGet.

  3. Di Manajer Paket NuGet, pilih tautan Telusuri . Cari dan pilih paket EntityFramework . Pilih Instal di panel kanan dan ikuti perintah.

    Jendela Output menampilkan kemajuan dan memberi tahu Anda ketika penginstalan selesai.

    Cuplikan layar yang memperlihatkan Paket NuGet Kerangka Kerja Entitas.

Sekarang Anda dapat menggunakan Visual Studio untuk membuat model berdasarkan database Northwind.

Membuat model data entitas ADO.NET

Untuk membuat model data entitas ADO.NET, ikuti langkah-langkah berikut:

  1. Klik kanan simpul proyek Aplikasi WPF di Penjelajah Solusi dan pilih Tambahkan>Item Baru. Di panel kiri, di bawah simpul C#, pilih Data dan di panel tengah, pilih ADO.NET Model Data Entitas.

    Cuplikan layar yang memperlihatkan jendela Tambahkan Item Baru dengan Model Data Entitas ADO.NET dipilih.

  2. Masukkan Northwind_model untuk Nama, lalu pilih Tambahkan.

  3. Di Wizard Model Data Entitas, pilih Desainer EF dari database, lalu pilih Berikutnya.

    Cuplikan layar yang memperlihatkan Perancang EF dari database yang dipilih dalam Wizard Model Data Entitas.

  4. Di Pilih Koneksi Data Anda, pilih koneksi LocalDB Northwind Anda (misalnya, (localdb)\MSSQLLocalDB), lalu pilih Berikutnya.

  5. Jika Anda tidak melihat koneksi:

    1. Pilih Koneksi Baru. Jika Microsoft SQL Server tidak dipilih sebagai Sumber data dalam dialog Properti Koneksi , pilih Ubah. Dalam dialog Pilih Sumber Data , pilih Microsoft SQL Server, lalu pilih OK.

    2. Dalam dialog Properti Koneksi , masukkan (localdb)\MSSQLLocalDB sebagai Nama server.

    3. Untuk Pilih atau masukkan nama database, pilih Northwind, lalu pilih OK.

    4. Di Pilih Koneksi Data Anda, pilih koneksi LocalDB Northwind Anda, dan pilih Berikutnya.

  6. Jika diminta, pilih versi Kerangka Kerja Entitas yang Anda gunakan, lalu pilih Berikutnya.

    Cuplikan layar yang memperlihatkan pilihan versi untuk Kerangka Kerja Entitas.

  7. Di halaman panduan berikutnya, pilih tabel mana, prosedur tersimpan, dan objek database lainnya yang akan disertakan dalam model Kerangka Kerja Entitas. Perluas simpul dbo di bawah simpul Tabel dalam tampilan pohon. Pilih Pelanggan, Detail Pesanan, dan Pesanan. Biarkan default dicentang dan pilih Selesai.

    Cuplikan layar yang memperlihatkan objek database yang dipilih dari model data.

    Wizard menghasilkan kelas C# yang mewakili model Entity Framework, dan mengikat data Visual Studio ke antarmuka pengguna WPF. Ini membuat file berikut di proyek Anda:

    • File .edmx menjelaskan hubungan dan metadata lain yang mengaitkan kelas dengan objek dalam database.

    • File .tt adalah templat T4 yang menghasilkan kode yang beroperasi pada model dan menyimpan perubahan pada database.

    File-file ini terlihat di Penjelajah Solusi di bawah simpul Northwind_model :

    Cuplikan layar yang memperlihatkan file model Kerangka Kerja Entitas Penjelajah Solusi.

    Perancang Formulir untuk .edmx file tidak digunakan dalam tutorial ini, tetapi Anda dapat menggunakannya untuk memodifikasi properti dan hubungan tertentu dalam model.

File .tt adalah tujuan umum dan Anda harus mengedit salah satunya untuk bekerja dengan pengikatan data WPF, yang memerlukan ObservableCollection objek. Ikuti langkah-langkah ini:

  1. Di Penjelajah Solusi, perluas simpul Northwind_model hingga Anda menemukan Northwind_model.tt. Klik dua kali file ini dan lakukan pengeditan berikut:

  2. Tekan F5 untuk membangun dan menjalankan proyek. Ketika aplikasi pertama kali dijalankan, kelas model dapat dilihat oleh penyihir pengaturan sumber data.

Sekarang Anda siap untuk menghubungkan model ini ke halaman XAML sehingga Anda dapat melihat, menavigasi, dan memodifikasi data.

Data mengikat model ke halaman XAML

Meskipun Anda dapat menulis kode pengikatan data Anda sendiri, lebih mudah untuk membiarkan Visual Studio melakukannya untuk Anda. Untuk melakukannya, ikuti langkah-langkah berikut:

  1. Dari menu utama, pilih Proyek>Tambahkan sumber data baru untuk menampilkan Panduan Konfigurasi Sumber Data. Karena Anda mengikat ke kelas model, bukan ke database, pilih Objek. Pilih Selanjutnya.

    Cuplikan layar yang memperlihatkan Panduan Konfigurasi Sumber Data dengan Objek dipilih sebagai sumber data.

  2. Perluas simpul untuk proyek Anda, pilih objek Pelanggan , lalu pilih Selesai. Sumber untuk objek Pesanan secara otomatis dihasilkan dari properti navigasi Pesanan di Pelanggan.

    Cuplikan layar yang memperlihatkan objek Pelanggan dipilih sebagai sumber data.

  3. Di Penjelajah Solusi, klik dua kali MainWindow.xaml di proyek Anda untuk mengedit XAML. Ubah Title dari MainWindow menjadi lebih deskriptif, dan tingkatkan Height serta Width menjadi 600 serta 800 (Anda dapat mengubah nilai-nilai ini nanti, jika perlu).

  4. Tambahkan tiga definisi baris ini ke kisi utama, satu baris untuk tombol navigasi, satu untuk detail pelanggan, dan satu untuk kisi yang menunjukkan pesanan mereka:

        <Grid.RowDefinitions>
            <RowDefinition Height="auto"/>
            <RowDefinition Height="auto"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>
    

Selanjutnya, Anda menampilkan setiap properti di Customers kelas dalam kotak teks individualnya sendiri. Ikuti langkah-langkah ini:

  1. Di Penjelajah Solusi, klik dua kali MainWindow.xaml untuk membukanya di perancang.

    Tab Sumber Data muncul di panel kiri Visual Studio di dekat Kotak Alat.

  2. Untuk membuka jendela Sumber Data, pilih tab Sumber Data atau pilih Tampilkan>Sumber Data> Lainnya dari menu.

  3. Di Sumber Data, pilih Pelanggan, lalu pilih Detail dari daftar drop-down.

  4. Seret simpul ke baris tengah area desain. Jika Anda salah menempatkannya, Anda nantinya dapat menentukan baris secara manual di XAML dengan memilih Grid.Row="1".

    Secara default, kontrol ditempatkan secara vertikal dalam elemen kisi, tetapi Anda dapat mengaturnya sesuka Anda pada formulir. Misalnya, Anda dapat menempatkan kotak teks Nama di atas alamat. Aplikasi sampel untuk tutorial ini mengatur ulang kolom dan menatanya menjadi dua kolom.

    Cuplikan layar yang memperlihatkan Pengikatan sumber data Pelanggan ke kontrol individual.

    Dalam tampilan XAML, Anda sekarang dapat melihat elemen baru Grid di baris 1 (baris tengah) grid induk. Kisi induk memiliki atribut DataContext yang mengacu pada elemen CollectionViewSource yang termasuk dalam Windows.Resources. Mengingat konteks data tersebut, ketika kotak teks pertama mengaitkan ke Alamat, nama tersebut dipetakan ke properti Address dalam objek Customer saat ini di CollectionViewSource.

    <Grid DataContext="{StaticResource customerViewSource}">
    
  5. OrderSeret properti objek kelas Customers ke bagian bawah formulir, sehingga perancang meletakkannya di baris 2.

    Ketika pelanggan terlihat di bagian atas formulir, Anda ingin melihat pesanan mereka di bagian bawah. Anda menampilkan pesanan dalam tampilan grid tunggal. Agar pengikatan data master-detail berfungsi sebagaimana mestinya, penting bagi Anda untuk mengikat ke properti Orders dalam kelas Customers, bukan ke node terpisah Orders.

    Cuplikan layar yang memperlihatkan kelas Pesanan diseret dan dijatuhkan sebagai kisi.

    Visual Studio sekarang menghasilkan semua kode pengikatan yang menghubungkan kontrol UI ke peristiwa dalam model.

  6. Untuk melihat beberapa data, tulis kode untuk mengisi model. Navigasi ke MainWindow.xaml.cs dan tambahkan anggota data ke MainWindow kelas untuk konteks data.

    Objek ini, yang dihasilkan untuk Anda, bertindak seperti kontrol yang melacak perubahan dan peristiwa dalam model.

  7. Tambahkan CollectionViewSource anggota data untuk pelanggan dan pesanan, serta logika inisialisasi konstruktor terkait ke konstruktor MainWindow() yang ada. Bagian pertama dari kelas akan terlihat seperti ini:

    public partial class MainWindow : Window
    {
        NorthwindEntities context = new NorthwindEntities();
        CollectionViewSource custViewSource;
        CollectionViewSource ordViewSource;
    
        public MainWindow()
        {
            InitializeComponent();
            custViewSource = ((CollectionViewSource)(FindResource("customerViewSource")));
            ordViewSource = ((CollectionViewSource)(FindResource("customerOrdersViewSource")));
            DataContext = this;
        }
    
  8. Jika tidak ada, tambahkan using direktif agar System.Data.Entity dapat membawa Load metode ekstensi ke dalam cakupan.

    using System.Data.Entity;
    
  9. Gulir ke bawah dan temukan Window_Loaded pengelola acara. Perhatikan bahwa Visual Studio menambahkan CollectionViewSource objek. Objek ini mewakili NorthwindEntities objek yang Anda pilih saat membuat model. Karena Anda sudah menambahkannya, Anda tidak membutuhkannya di sini. Ganti kode dalam Window_Loaded sehingga metode terlihat seperti ini:

    private void Window_Loaded(object sender, RoutedEventArgs e)
    {
        // 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.Customers.Load();
    
        // After the data is loaded, call the DbSet<T>.Local property    
        // to use the DbSet<T> as a binding source.   
        custViewSource.Source = context.Customers.Local;
    }
    
  10. Tekan F5.

    Anda akan melihat detail untuk pelanggan pertama yang telah diambil ke CollectionViewSource dan pesanan mereka di tabel data. Anda akan memperbaiki pemformatan di bagian berikutnya. Anda juga dapat membuat cara untuk melihat rekaman lain dan melakukan operasi dasar membuat, membaca, memperbarui, dan menghapus (CRUD).

Sesuaikan desain halaman dan tambahkan kisi untuk pelanggan dan pesanan baru

Pengaturan default yang dihasilkan oleh Visual Studio tidak ideal untuk aplikasi Anda, jadi kami menyediakan XAML akhir di sini untuk disalin ke dalam kode Anda. Anda juga memerlukan beberapa kisi untuk memungkinkan pengguna menambahkan pelanggan atau pesanan baru.

Untuk menambahkan pelanggan dan pesanan baru, buat sekumpulan kotak teks terpisah yang tidak terikat data ke CollectionViewSource. Anda memilih kisi mana yang dilihat pengguna pada waktu tertentu dengan mengatur properti Visible dalam metode handler. Terakhir, Anda menambahkan tombol Hapus ke setiap baris di tabel Pesanan untuk memungkinkan pengguna menghapus pesanan individual.

  1. Buka MainWindow.xaml dan tambahkan gaya berikut ke Windows.Resources elemen :

    <Style x:Key="Label" TargetType="{x:Type Label}" BasedOn="{x:Null}">
       <Setter Property="HorizontalAlignment" Value="Left"/>
       <Setter Property="VerticalAlignment" Value="Center"/>
       <Setter Property="Margin" Value="3"/>
       <Setter Property="Height" Value="23"/>
    </Style>
    <Style x:Key="CustTextBox" TargetType="{x:Type TextBox}" BasedOn="{x:Null}">
       <Setter Property="HorizontalAlignment" Value="Right"/>
       <Setter Property="VerticalAlignment" Value="Center"/>
       <Setter Property="Margin" Value="3"/>
       <Setter Property="Height" Value="26"/>
       <Setter Property="Width" Value="120"/>
    </Style>
    
  2. Gantilah seluruh grid luar dengan markup berikut:

    <Grid>
      <Grid.RowDefinitions>
          <RowDefinition Height="auto"/>
          <RowDefinition Height="auto"/>
          <RowDefinition Height="*"/>
      </Grid.RowDefinitions>
      <Grid x:Name="existingCustomerGrid" Grid.Row="1" HorizontalAlignment="Left" Margin="5" Visibility="Visible" VerticalAlignment="Top" Background="AntiqueWhite" DataContext="{StaticResource customerViewSource}">
          <Grid.ColumnDefinitions>
              <ColumnDefinition Width="Auto" MinWidth="233"/>
              <ColumnDefinition Width="Auto" MinWidth="397"/>
          </Grid.ColumnDefinitions>
          <Grid.RowDefinitions>
              <RowDefinition Height="Auto"/>
              <RowDefinition Height="Auto"/>
              <RowDefinition Height="Auto"/>
              <RowDefinition Height="Auto"/>
              <RowDefinition Height="Auto"/>
              <RowDefinition Height="Auto"/>
          </Grid.RowDefinitions>
          <Label Content="Customer ID:" Grid.Row="0" Style="{StaticResource Label}"/>
          <TextBox x:Name="customerIDTextBox" Grid.Row="0" Style="{StaticResource CustTextBox}"
                   Text="{Binding CustomerID, Mode=TwoWay, NotifyOnValidationError=true, ValidatesOnExceptions=true}"/>
          <Label Content="Company Name:" Grid.Row="1" Style="{StaticResource Label}"/>
          <TextBox x:Name="companyNameTextBox" Grid.Row="1" Style="{StaticResource CustTextBox}"
                   Text="{Binding CompanyName, Mode=TwoWay, NotifyOnValidationError=true, ValidatesOnExceptions=true}"/>
          <Label Content="Contact Name:" Grid.Row="2" Style="{StaticResource Label}"/>
          <TextBox x:Name="contactNameTextBox" Grid.Row="2" Style="{StaticResource CustTextBox}"
                   Text="{Binding ContactName, Mode=TwoWay, NotifyOnValidationError=true, ValidatesOnExceptions=true}"/>
          <Label Content="Contact title:" Grid.Row="3" Style="{StaticResource Label}"/>
          <TextBox x:Name="contactTitleTextBox" Grid.Row="3" Style="{StaticResource CustTextBox}"
                   Text="{Binding ContactTitle, Mode=TwoWay, NotifyOnValidationError=true, ValidatesOnExceptions=true}"/>
          <Label Content="Address:" Grid.Row="4" Style="{StaticResource Label}"/>
          <TextBox x:Name="addressTextBox" Grid.Row="4" Style="{StaticResource CustTextBox}"
                   Text="{Binding Address, Mode=TwoWay, NotifyOnValidationError=true, ValidatesOnExceptions=true}"/>
          <Label Content="City:" Grid.Column="1" Grid.Row="0" Style="{StaticResource Label}"/>
          <TextBox x:Name="cityTextBox" Grid.Column="1" Grid.Row="0" Style="{StaticResource CustTextBox}"
                   Text="{Binding City, Mode=TwoWay, NotifyOnValidationError=true, ValidatesOnExceptions=true}"/>
          <Label Content="Country:" Grid.Column="1" Grid.Row="1" Style="{StaticResource Label}"/>
          <TextBox x:Name="countryTextBox" Grid.Column="1" Grid.Row="1" Style="{StaticResource CustTextBox}"
                   Text="{Binding Country, Mode=TwoWay, NotifyOnValidationError=true, ValidatesOnExceptions=true}"/>
          <Label Content="Fax:" Grid.Column="1" Grid.Row="2" Style="{StaticResource Label}"/>
          <TextBox x:Name="faxTextBox" Grid.Column="1" Grid.Row="2" Style="{StaticResource CustTextBox}"
                   Text="{Binding Fax, Mode=TwoWay, NotifyOnValidationError=true, ValidatesOnExceptions=true}"/>
          <Label Content="Phone:" Grid.Column="1" Grid.Row="3" Style="{StaticResource Label}"/>
          <TextBox x:Name="phoneTextBox" Grid.Column="1" Grid.Row="3" Style="{StaticResource CustTextBox}"
                   Text="{Binding Phone, Mode=TwoWay, NotifyOnValidationError=true, ValidatesOnExceptions=true}"/>
          <Label Content="Postal Code:" Grid.Column="1" Grid.Row="4" VerticalAlignment="Center" Style="{StaticResource Label}"/>
          <TextBox x:Name="postalCodeTextBox" Grid.Column="1" Grid.Row="4" Style="{StaticResource CustTextBox}"
                   Text="{Binding PostalCode, Mode=TwoWay, NotifyOnValidationError=true, ValidatesOnExceptions=true}"/>
          <Label Content="Region:" Grid.Column="1" Grid.Row="5" Style="{StaticResource Label}"/>
          <TextBox x:Name="regionTextBox" Grid.Column="1" Grid.Row="5" Style="{StaticResource CustTextBox}"
                   Text="{Binding Region, Mode=TwoWay, NotifyOnValidationError=true, ValidatesOnExceptions=true}"/>
      </Grid>
      <Grid x:Name="newCustomerGrid" Grid.Row="1" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="5" DataContext="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}, Path=newCustomer, UpdateSourceTrigger=Explicit}" Visibility="Collapsed" Background="CornflowerBlue">
          <Grid.ColumnDefinitions>
              <ColumnDefinition Width="Auto" MinWidth="233"/>
              <ColumnDefinition Width="Auto" MinWidth="397"/>
          </Grid.ColumnDefinitions>
          <Grid.RowDefinitions>
              <RowDefinition Height="Auto"/>
              <RowDefinition Height="Auto"/>
              <RowDefinition Height="Auto"/>
              <RowDefinition Height="Auto"/>
              <RowDefinition Height="Auto"/>
              <RowDefinition Height="Auto"/>
          </Grid.RowDefinitions>
          <Label Content="Customer ID:" Grid.Row="0" Style="{StaticResource Label}"/>
          <TextBox x:Name="add_customerIDTextBox" Grid.Row="0" Style="{StaticResource CustTextBox}"
                   Text="{Binding CustomerID, Mode=TwoWay, NotifyOnValidationError=true, ValidatesOnExceptions=true}"/>
          <Label Content="Company Name:" Grid.Row="1" Style="{StaticResource Label}"/>
          <TextBox x:Name="add_companyNameTextBox" Grid.Row="1" Style="{StaticResource CustTextBox}"
                   Text="{Binding CompanyName, Mode=TwoWay, NotifyOnValidationError=true, ValidatesOnExceptions=true }"/>
          <Label Content="Contact Name:" Grid.Row="2" Style="{StaticResource Label}"/>
          <TextBox x:Name="add_contactNameTextBox" Grid.Row="2" Style="{StaticResource CustTextBox}"
                   Text="{Binding ContactName, Mode=TwoWay, NotifyOnValidationError=true, ValidatesOnExceptions=true}"/>
          <Label Content="Contact title:" Grid.Row="3" Style="{StaticResource Label}"/>
          <TextBox x:Name="add_contactTitleTextBox" Grid.Row="3" Style="{StaticResource CustTextBox}"
                   Text="{Binding ContactTitle, Mode=TwoWay, NotifyOnValidationError=true, ValidatesOnExceptions=true}"/>
          <Label Content="Address:" Grid.Row="4" Style="{StaticResource Label}"/>
          <TextBox x:Name="add_addressTextBox" Grid.Row="4" Style="{StaticResource CustTextBox}"
                   Text="{Binding Address, Mode=TwoWay, NotifyOnValidationError=true, ValidatesOnExceptions=true}"/>
          <Label Content="City:" Grid.Column="1" Grid.Row="0" Style="{StaticResource Label}"/>
          <TextBox x:Name="add_cityTextBox" Grid.Column="1" Grid.Row="0" Style="{StaticResource CustTextBox}"
                   Text="{Binding City, Mode=TwoWay, NotifyOnValidationError=true, ValidatesOnExceptions=true}"/>
          <Label Content="Country:" Grid.Column="1" Grid.Row="1" Style="{StaticResource Label}"/>
          <TextBox x:Name="add_countryTextBox" Grid.Column="1" Grid.Row="1" Style="{StaticResource CustTextBox}"
                   Text="{Binding Country, Mode=TwoWay, NotifyOnValidationError=true, ValidatesOnExceptions=true}"/>
          <Label Content="Fax:" Grid.Column="1" Grid.Row="2" Style="{StaticResource Label}"/>
          <TextBox x:Name="add_faxTextBox" Grid.Column="1" Grid.Row="2" Style="{StaticResource CustTextBox}"
                   Text="{Binding Fax, Mode=TwoWay, NotifyOnValidationError=true, ValidatesOnExceptions=true}"/>
          <Label Content="Phone:" Grid.Column="1" Grid.Row="3" Style="{StaticResource Label}"/>
          <TextBox x:Name="add_phoneTextBox" Grid.Column="1" Grid.Row="3" Style="{StaticResource CustTextBox}"
                   Text="{Binding Phone, Mode=TwoWay, NotifyOnValidationError=true, ValidatesOnExceptions=true}"/>
          <Label Content="Postal Code:" Grid.Column="1" Grid.Row="4" VerticalAlignment="Center" Style="{StaticResource Label}"/>
          <TextBox x:Name="add_postalCodeTextBox" Grid.Column="1" Grid.Row="4" Style="{StaticResource CustTextBox}"
                   Text="{Binding PostalCode, Mode=TwoWay, NotifyOnValidationError=true, ValidatesOnExceptions=true}"/>
          <Label Content="Region:" Grid.Column="1" Grid.Row="5" Style="{StaticResource Label}"/>
          <TextBox x:Name="add_regionTextBox" Grid.Column="1" Grid.Row="5" Style="{StaticResource CustTextBox}"
                   Text="{Binding Region, Mode=TwoWay, NotifyOnValidationError=true, ValidatesOnExceptions=true}"/>
      </Grid>
      <Grid x:Name="newOrderGrid" Grid.Row="1" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="5" DataContext="{Binding Path=newOrder, Mode=TwoWay}" Visibility="Collapsed" Background="LightGreen">
          <Grid.ColumnDefinitions>
              <ColumnDefinition Width="Auto" MinWidth="233"/>
              <ColumnDefinition Width="Auto" MinWidth="397"/>
          </Grid.ColumnDefinitions>
          <Grid.RowDefinitions>
              <RowDefinition Height="Auto"/>
              <RowDefinition Height="Auto"/>
              <RowDefinition Height="Auto"/>
              <RowDefinition Height="Auto"/>
              <RowDefinition Height="Auto"/>
              <RowDefinition Height="Auto"/>
              <RowDefinition Height="Auto"/>
          </Grid.RowDefinitions>
          <Label Content="New Order Form" FontWeight="Bold"/>
          <Label Content="Employee ID:"  Grid.Row="1" Style="{StaticResource Label}"/>
          <TextBox x:Name="add_employeeIDTextBox" Grid.Row="1" Style="{StaticResource CustTextBox}"
                   Text="{Binding EmployeeID, Mode=TwoWay, NotifyOnValidationError=true, ValidatesOnExceptions=true}"/>
          <Label Content="Order Date:"  Grid.Row="2" Style="{StaticResource Label}"/>
          <DatePicker x:Name="add_orderDatePicker" Grid.Row="2"  HorizontalAlignment="Right" Width="120"
                  SelectedDate="{Binding OrderDate, Mode=TwoWay, NotifyOnValidationError=true, ValidatesOnExceptions=true, UpdateSourceTrigger=PropertyChanged}"/>
          <Label Content="Required Date:" Grid.Row="3" Style="{StaticResource Label}"/>
          <DatePicker x:Name="add_requiredDatePicker" Grid.Row="3" HorizontalAlignment="Right" Width="120"
                   SelectedDate="{Binding RequiredDate, Mode=TwoWay, NotifyOnValidationError=true, ValidatesOnExceptions=true, UpdateSourceTrigger=PropertyChanged}"/>
          <Label Content="Shipped Date:"  Grid.Row="4"  Style="{StaticResource Label}"/>
          <DatePicker x:Name="add_shippedDatePicker"  Grid.Row="4"  HorizontalAlignment="Right" Width="120"
                  SelectedDate="{Binding ShippedDate, Mode=TwoWay, NotifyOnValidationError=true, ValidatesOnExceptions=true, UpdateSourceTrigger=PropertyChanged}"/>
          <Label Content="Ship Via:"  Grid.Row="5" Style="{StaticResource Label}"/>
          <TextBox x:Name="add_ShipViaTextBox"  Grid.Row="5" Style="{StaticResource CustTextBox}"
                   Text="{Binding ShipVia, Mode=TwoWay, NotifyOnValidationError=true, ValidatesOnExceptions=true}"/>
          <Label Content="Freight"  Grid.Row="6" Style="{StaticResource Label}"/>
          <TextBox x:Name="add_freightTextBox" Grid.Row="6" Style="{StaticResource CustTextBox}"
                   Text="{Binding Freight, Mode=TwoWay, NotifyOnValidationError=true, ValidatesOnExceptions=true}"/>
      </Grid>
      <DataGrid x:Name="ordersDataGrid" SelectionUnit="Cell" SelectionMode="Single" AutoGenerateColumns="False" CanUserAddRows="false" IsEnabled="True" EnableRowVirtualization="True" Width="auto" ItemsSource="{Binding Source={StaticResource customerOrdersViewSource}}" Margin="10,10,10,10" Grid.Row="2" RowDetailsVisibilityMode="VisibleWhenSelected">
          <DataGrid.Columns>
              <DataGridTemplateColumn>
                  <DataGridTemplateColumn.CellTemplate>
                      <DataTemplate>
                          <Button Content="Delete" Command="{StaticResource DeleteOrderCommand}" CommandParameter="{Binding}"/>
                      </DataTemplate>
                  </DataGridTemplateColumn.CellTemplate>
              </DataGridTemplateColumn>
              <DataGridTextColumn x:Name="customerIDColumn" Binding="{Binding CustomerID}" Header="Customer ID" Width="SizeToHeader"/>
              <DataGridTextColumn x:Name="employeeIDColumn" Binding="{Binding EmployeeID}" Header="Employee ID" Width="SizeToHeader"/>
              <DataGridTextColumn x:Name="freightColumn" Binding="{Binding Freight}" Header="Freight" Width="SizeToHeader"/>
              <DataGridTemplateColumn x:Name="orderDateColumn" Header="Order Date" Width="SizeToHeader">
                  <DataGridTemplateColumn.CellTemplate>
                      <DataTemplate>
                          <DatePicker SelectedDate="{Binding OrderDate, Mode=TwoWay, NotifyOnValidationError=true, ValidatesOnExceptions=true, UpdateSourceTrigger=PropertyChanged}"/>
                      </DataTemplate>
                  </DataGridTemplateColumn.CellTemplate>
              </DataGridTemplateColumn>
              <DataGridTextColumn x:Name="orderIDColumn" Binding="{Binding OrderID}" Header="Order ID" Width="SizeToHeader"/>
              <DataGridTemplateColumn x:Name="requiredDateColumn" Header="Required Date" Width="SizeToHeader">
                  <DataGridTemplateColumn.CellTemplate>
                      <DataTemplate>
                          <DatePicker SelectedDate="{Binding RequiredDate, Mode=TwoWay, NotifyOnValidationError=true, ValidatesOnExceptions=true, UpdateSourceTrigger=PropertyChanged}"/>
                      </DataTemplate>
                  </DataGridTemplateColumn.CellTemplate>
              </DataGridTemplateColumn>
              <DataGridTextColumn x:Name="shipAddressColumn" Binding="{Binding ShipAddress}" Header="Ship Address" Width="SizeToHeader"/>
              <DataGridTextColumn x:Name="shipCityColumn" Binding="{Binding ShipCity}" Header="Ship City" Width="SizeToHeader"/>
              <DataGridTextColumn x:Name="shipCountryColumn" Binding="{Binding ShipCountry}" Header="Ship Country" Width="SizeToHeader"/>
              <DataGridTextColumn x:Name="shipNameColumn" Binding="{Binding ShipName}" Header="Ship Name" Width="SizeToHeader"/>
              <DataGridTemplateColumn x:Name="shippedDateColumn" Header="Shipped Date" Width="SizeToHeader">
                  <DataGridTemplateColumn.CellTemplate>
                      <DataTemplate>
                          <DatePicker SelectedDate="{Binding ShippedDate, Mode=TwoWay, NotifyOnValidationError=true, ValidatesOnExceptions=true, UpdateSourceTrigger=PropertyChanged}"/>
                      </DataTemplate>
                  </DataGridTemplateColumn.CellTemplate>
              </DataGridTemplateColumn>
              <DataGridTextColumn x:Name="shipPostalCodeColumn" Binding="{Binding ShipPostalCode}" Header="Ship Postal Code" Width="SizeToHeader"/>
              <DataGridTextColumn x:Name="shipRegionColumn" Binding="{Binding ShipRegion}" Header="Ship Region" Width="SizeToHeader"/>
              <DataGridTextColumn x:Name="shipViaColumn" Binding="{Binding ShipVia}" Header="Ship Via" Width="SizeToHeader"/>
          </DataGrid.Columns>
       </DataGrid>
    </Grid>
    

Menambahkan tombol untuk menavigasi, menambahkan, memperbarui, dan menghapus

Dalam aplikasi Windows Forms, Anda diberi objek dengan BindingNavigator tombol untuk menavigasi baris dalam database dan melakukan operasi CRUD dasar. Meskipun WPF tidak menyediakan BindingNavigator, mudah untuk membuatnya dengan membuat tombol di dalam horizontal StackPanel, dan mengaitkan tombol dengan perintah yang terikat ke metode dalam file code-behind.

Ada empat bagian untuk logika perintah:

  • Perintah
  • Pengikat
  • Tombol
  • Pengendali perintah di code-behind

Menambahkan perintah, pengikatan, dan tombol di XAML

  1. Dalam file MainWindow.xaml, tambahkan perintah di dalam elemen Windows.Resources sebagai berikut:

    <RoutedUICommand x:Key="FirstCommand" Text="First"/>
    <RoutedUICommand x:Key="LastCommand" Text="Last"/>
    <RoutedUICommand x:Key="NextCommand" Text="Next"/>
    <RoutedUICommand x:Key="PreviousCommand" Text="Previous"/>
    <RoutedUICommand x:Key="DeleteCustomerCommand" Text="Delete Customer"/>
    <RoutedUICommand x:Key="DeleteOrderCommand" Text="Delete Order"/>
    <RoutedUICommand x:Key="UpdateCommand" Text="Update"/>
    <RoutedUICommand x:Key="AddCommand" Text="Add"/>
    <RoutedUICommand x:Key="CancelCommand" Text="Cancel"/>
    
  2. Memetakan CommandBindingRoutedUICommand peristiwa ke metode dalam kode di belakang. Tambahkan elemen ini CommandBindings setelah Windows.Resources tag penutup sebagai berikut:

    <Window.CommandBindings>
        <CommandBinding Command="{StaticResource FirstCommand}" Executed="FirstCommandHandler"/>
        <CommandBinding Command="{StaticResource LastCommand}" Executed="LastCommandHandler"/>
        <CommandBinding Command="{StaticResource NextCommand}" Executed="NextCommandHandler"/>
        <CommandBinding Command="{StaticResource PreviousCommand}" Executed="PreviousCommandHandler"/>
        <CommandBinding Command="{StaticResource DeleteCustomerCommand}" Executed="DeleteCustomerCommandHandler"/>
        <CommandBinding Command="{StaticResource DeleteOrderCommand}" Executed="DeleteOrderCommandHandler"/>
        <CommandBinding Command="{StaticResource UpdateCommand}" Executed="UpdateCommandHandler"/>
        <CommandBinding Command="{StaticResource AddCommand}" Executed="AddCommandHandler"/>
        <CommandBinding Command="{StaticResource CancelCommand}" Executed="CancelCommandHandler"/>
    </Window.CommandBindings>
    
  3. Tambahkan StackPanel bersama tombol navigasi, tambah, hapus, dan perbarui. Tambahkan gaya ini ke Windows.Resources:

    <Style x:Key="NavButton" TargetType="{x:Type Button}" BasedOn="{x:Null}">
        <Setter Property="FontSize" Value="24"/>
        <Setter Property="FontFamily" Value="Segoe UI Symbol"/>
        <Setter Property="Margin" Value="2,2,2,0"/>
        <Setter Property="Width" Value="40"/>
        <Setter Property="Height" Value="auto"/>
    </Style>
    
  4. Tempelkan kode ini tepat setelah RowDefinitions untuk elemen luar Grid , ke bagian atas halaman XAML:

     <StackPanel Orientation="Horizontal" Margin="2,2,2,0" Height="36" VerticalAlignment="Top" Background="Gainsboro" DataContext="{StaticResource customerViewSource}" d:LayoutOverrides="LeftMargin, RightMargin, TopMargin, BottomMargin">
         <Button Name="btnFirst" Content="|◄" Command="{StaticResource FirstCommand}" Style="{StaticResource NavButton}"/>
         <Button Name="btnPrev" Content="◄" Command="{StaticResource PreviousCommand}" Style="{StaticResource NavButton}"/>
         <Button Name="btnNext" Content="►" Command="{StaticResource NextCommand}" Style="{StaticResource NavButton}"/>
         <Button Name="btnLast" Content="►|" Command="{StaticResource LastCommand}" Style="{StaticResource NavButton}"/>
         <Button Name="btnDelete" Content="Delete Customer" Command="{StaticResource DeleteCustomerCommand}" FontSize="11" Width="120" Style="{StaticResource NavButton}"/>
         <Button Name="btnAdd" Content="New Customer" Command="{StaticResource AddCommand}" FontSize="11" Width="80" Style="{StaticResource NavButton}"/>
         <Button Content="New Order" Name="btnNewOrder" FontSize="11" Width="80" Style="{StaticResource NavButton}" Click="NewOrder_click"/>
         <Button Name="btnUpdate" Content="Commit" Command="{StaticResource UpdateCommand}" FontSize="11" Width="80" Style="{StaticResource NavButton}"/>
         <Button Content="Cancel" Name="btnCancel" Command="{StaticResource CancelCommand}" FontSize="11" Width="80" Style="{StaticResource NavButton}"/>
     </StackPanel>
    

Menambahkan penangan perintah pada kelas MainWindow

Code-behind pada MainWindow.xaml.cs minimal, kecuali untuk metode penambahan dan hapus.

  1. Untuk menavigasi, panggil metode dari properti View pada CollectionViewSource.

  2. Untuk melakukan penghapusan kaskade pada pesanan, gunakan DeleteOrderCommandHandler seperti yang ditunjukkan dalam sampel kode. Namun, Anda harus terlebih dahulu menghapus Order_Details yang terkait dengan pesanan tersebut.

  3. UpdateCommandHandler Gunakan untuk menambahkan pelanggan atau memesan ke koleksi, atau memperbarui pelanggan atau pesanan yang ada dengan perubahan yang dilakukan pengguna dalam kotak teks.

  4. Tambahkan metode handler ini ke MainWindow kelas di MainWindow.xaml.cs. Jika tabel Pelanggan Anda CollectionViewSource memiliki nama yang berbeda, Anda harus menyesuaikan nama di setiap metode:

    private void LastCommandHandler(object sender, ExecutedRoutedEventArgs e)
    {
        custViewSource.View.MoveCurrentToLast();
    }
    
    private void PreviousCommandHandler(object sender, ExecutedRoutedEventArgs e)
    {
        custViewSource.View.MoveCurrentToPrevious();
    }
    
    private void NextCommandHandler(object sender, ExecutedRoutedEventArgs e)
    {
        custViewSource.View.MoveCurrentToNext();
    }
    
    private void FirstCommandHandler(object sender, ExecutedRoutedEventArgs e)
    {
        custViewSource.View.MoveCurrentToFirst();
    }
    
    private void DeleteCustomerCommandHandler(object sender, ExecutedRoutedEventArgs e)
    {
        // If existing window is visible, delete the customer and all their orders.  
        // In a real application, you should add warnings and allow the user to cancel the operation.  
        var cur = custViewSource.View.CurrentItem as Customer;
    
        var cust = (from c in context.Customers
                    where c.CustomerID == cur.CustomerID
                    select c).FirstOrDefault();
    
        if (cust != null)
        {
            foreach (var ord in cust.Orders.ToList())
            {
                Delete_Order(ord);
            }
            context.Customers.Remove(cust);
        }
        context.SaveChanges();
        custViewSource.View.Refresh();
    }
    
    // Commit changes from the new customer form, the new order form,  
    // or edits made to the existing customer form.  
    private void UpdateCommandHandler(object sender, ExecutedRoutedEventArgs e)
    {
        if (newCustomerGrid.IsVisible)
        {
            // Create a new object because the old one  
            // is being tracked by EF now.  
            Customer newCustomer = new Customer
            {
                Address = add_addressTextBox.Text,
                City = add_cityTextBox.Text,
                CompanyName = add_companyNameTextBox.Text,
                ContactName = add_contactNameTextBox.Text,
                ContactTitle = add_contactTitleTextBox.Text,
                Country = add_countryTextBox.Text,
                CustomerID = add_customerIDTextBox.Text,
                Fax = add_faxTextBox.Text,
                Phone = add_phoneTextBox.Text,
                PostalCode = add_postalCodeTextBox.Text,
                Region = add_regionTextBox.Text
            };
    
            // Perform very basic validation  
            if (newCustomer.CustomerID.Length == 5)
            {
                // Insert the new customer at correct position:  
                int len = context.Customers.Local.Count();
                int pos = len;
                for (int i = 0; i < len; ++i)
                {
                    if (String.CompareOrdinal(newCustomer.CustomerID, context.Customers.Local[i].CustomerID) < 0)
                    {
                        pos = i;
                        break;
                    }
                }
                context.Customers.Local.Insert(pos, newCustomer);
                custViewSource.View.Refresh();
                custViewSource.View.MoveCurrentTo(newCustomer);
            }
            else
            {
                MessageBox.Show("CustomerID must have 5 characters.");
            }
    
            newCustomerGrid.Visibility = Visibility.Collapsed;
            existingCustomerGrid.Visibility = Visibility.Visible;
        }
        else if (newOrderGrid.IsVisible)
        {
            // Order ID is auto-generated so we don't set it here.  
            // For CustomerID, address, etc we use the values from current customer.  
            // User can modify these in the datagrid after the order is entered.  
    
            Customer currentCustomer = (Customer)custViewSource.View.CurrentItem;
    
            Order newOrder = new Order()
            {
                OrderDate = add_orderDatePicker.SelectedDate,
                RequiredDate = add_requiredDatePicker.SelectedDate,
                ShippedDate = add_shippedDatePicker.SelectedDate,
                CustomerID = currentCustomer.CustomerID,
                ShipAddress = currentCustomer.Address,
                ShipCity = currentCustomer.City,
                ShipCountry = currentCustomer.Country,
                ShipName = currentCustomer.CompanyName,
                ShipPostalCode = currentCustomer.PostalCode,
                ShipRegion = currentCustomer.Region
            };
    
            try
            {
                newOrder.EmployeeID = Int32.Parse(add_employeeIDTextBox.Text);
            }
            catch
            {
                MessageBox.Show("EmployeeID must be a valid integer value.");
                return;
            }
    
            try
            {
                // Exercise for the reader if you are using Northwind:  
                // Add the Northwind Shippers table to the model.
                
                // Acceptable ShipperID values are 1, 2, or 3.  
                if (add_ShipViaTextBox.Text == "1" || add_ShipViaTextBox.Text == "2"
                    || add_ShipViaTextBox.Text == "3")
                {
                    newOrder.ShipVia = Convert.ToInt32(add_ShipViaTextBox.Text);
                }
                else
                {
                    MessageBox.Show("Shipper ID must be 1, 2, or 3 in Northwind.");
                    return;
                }
            }
            catch
            {
                MessageBox.Show("Ship Via must be convertible to int");
                return;
            }
    
            try
            {
                newOrder.Freight = Convert.ToDecimal(add_freightTextBox.Text);
            }
            catch
            {
                MessageBox.Show("Freight must be convertible to decimal.");
                return;
            }
    
            // Add the order into the EF model  
            context.Orders.Add(newOrder);
            ordViewSource.View.Refresh();
        }
    
        // Save the changes, either for a new customer, a new order  
        // or an edit to an existing customer or order.
        context.SaveChanges();
    }
    
    // Sets up the form so that user can enter data. Data is later  
    // saved when user clicks Commit.  
    private void AddCommandHandler(object sender, ExecutedRoutedEventArgs e)
    {
        existingCustomerGrid.Visibility = Visibility.Collapsed;
        newOrderGrid.Visibility = Visibility.Collapsed;
        newCustomerGrid.Visibility = Visibility.Visible;
    
        // Clear all the text boxes before adding a new customer.  
        foreach (var child in newCustomerGrid.Children)
        {
            var tb = child as TextBox;
            if (tb != null)
            {
                tb.Text = "";
            }
        }
    }
    
    private void NewOrder_click(object sender, RoutedEventArgs e)
    {
        var cust = custViewSource.View.CurrentItem as Customer;
        if (cust == null)
        {
            MessageBox.Show("No customer selected.");
            return;
        }
    
        existingCustomerGrid.Visibility = Visibility.Collapsed;
        newCustomerGrid.Visibility = Visibility.Collapsed;
        newOrderGrid.UpdateLayout();
        newOrderGrid.Visibility = Visibility.Visible;
    }
    
    // Cancels any input into the new customer form  
    private void CancelCommandHandler(object sender, ExecutedRoutedEventArgs e)
    {
        add_addressTextBox.Text = "";
        add_cityTextBox.Text = "";
        add_companyNameTextBox.Text = "";
        add_contactNameTextBox.Text = "";
        add_contactTitleTextBox.Text = "";
        add_countryTextBox.Text = "";
        add_customerIDTextBox.Text = "";
        add_faxTextBox.Text = "";
        add_phoneTextBox.Text = "";
        add_postalCodeTextBox.Text = "";
        add_regionTextBox.Text = "";
    
        existingCustomerGrid.Visibility = Visibility.Visible;
        newCustomerGrid.Visibility = Visibility.Collapsed;
        newOrderGrid.Visibility = Visibility.Collapsed;
    }
    
    private void Delete_Order(Order order)
    {
        // Find the order in the EF model.  
        var ord = (from o in context.Orders.Local
                   where o.OrderID == order.OrderID
                   select o).FirstOrDefault();
    
        // Delete all the order_details that have  
        // this Order as a foreign key  
        foreach (var detail in ord.Order_Details.ToList())
        {
            context.Order_Details.Remove(detail);
        }
    
        // Now it's safe to delete the order.  
        context.Orders.Remove(ord);
        context.SaveChanges();
    
        // Update the data grid.  
        ordViewSource.View.Refresh();
    }
    
    private void DeleteOrderCommandHandler(object sender, ExecutedRoutedEventArgs e)
    {
        // Get the Order in the row in which the Delete button was clicked.  
        Order obj = e.Parameter as Order;
        Delete_Order(obj);
    }
    

Menjalankan aplikasi WPF

  1. Untuk memulai debug, tekan F5. Verifikasi bahwa data pelanggan dan pesanan diisi di kisi, dan tombol navigasi berfungsi seperti yang diharapkan.

  2. Pilih Terapkan untuk menambahkan pelanggan baru atau memesan ke model setelah Anda memasukkan data.

  3. Pilih Batal untuk keluar dari formulir pelanggan baru atau pesanan baru tanpa menyimpan data.

  4. Untuk mengedit pelanggan dan pesanan yang ada secara langsung, gunakan kotak teks, yang menulis perubahan tersebut ke model secara otomatis.