添加链接
link管理
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接

本教學課程說明如何在 Visual Studio 中 透過資料 應用程式建立基本表單。 應用程式使用 SQL Server LocalDB、Northwind 資料庫、Entity Framework 6(而非 Entity Framework Core),以及適用於 .NET Framework 的 Windows Presentation Foundation (WPF)(不是 .NET Core 或 .NET 5 或更新版本)。 它示範如何使用主數據檢視來執行基本數據系結,並包含具有執行下列動作之按鈕的自定義 BindingNavigator 控件:移至第一個、移動上一步、移至下一步、移至最後一個、刪除、新增、設定新順序、更新和取消。

本教學課程著重於在 Visual Studio 中使用數據工具,而且不會嘗試在任何深度說明基礎技術。 它假設您已熟悉 Extensible Application Markup Language (XAML)、Entity Framework 和 SQL。 雖然本教學課程中的程式代碼不會示範 Model-View-ViewModel (MVVM) 架構,這是 WPF 應用程式的標準,但您可以使用一些修改將程式代碼複製到您自己的 MVVM 應用程式。

若要檢視本教學課程的最終程序代碼,請參閱 Visual Studio教學課程範例 - EF6

在本教學課程中,您會:

  • 安裝並連線到 Northwind 資料庫
  • 設定 WPF 應用程式專案
  • 建立 ADO.NET 實體數據模型
  • 數據會將模型系結至 XAML 頁面
  • 調整頁面設計並新增網格線
  • 新增按鈕以巡覽、新增、更新和刪除
  • 執行 WPF 應用程式
  • 已安裝 .NET 桌面開發 工作負載的 Visual Studio ,以及已安裝 Windows Communication Foundation 元件。 若要安裝:

  • 開啟 Visual Studio 安裝程式 應用程式,或從 Visual Studio 功能表中選取 工具 > 取得工具與功能
  • Visual Studio 安裝程式 中,選擇您想要修改之 Visual Studio 版本旁的 [修改 ]。
  • 選取 [ 個別元件] 索引卷標,然後選擇 [開發活動 ] 底下的 [Windows Communication Foundation ]。
  • 選取 修改
  • SQL Server Express LocalDB。 如果您沒有 SQL Server Express LocalDB,您可以從 SQL Server 下載頁面 加以安裝。 或者,您可以將 Visual Studio 安裝程式 應用程式安裝為個別元件。

  • SQL Server 物件總管。 若要安裝,請在 Visual Studio 安裝程式 應用程式中安裝 資料儲存和處理 工作負載。

  • Entity Framework 6 工具。 當您安裝 .NET Desktop 開發 工作負載時,通常會安裝此程式。

    安裝並連線到 Northwind 資料庫

    下列範例使用 SQL Server Express LocalDB 和 Northwind 範例資料庫。 如果該產品 ADO.NET 數據提供者支援 Entity Framework,它也應該與其他 SQL 資料庫產品搭配使用。

    依照下列步驟安裝 Northwind 範例資料庫:

  • 在 Visual Studio 中,從 [檢視] 功能表開啟 [SQL Server 物件總管] 視窗。 展開 SQL Server 節點。 以滑鼠右鍵按下您的 LocalDB 實體例,然後選取 [新增查詢 ]。

    查詢編輯器視窗隨即開啟。

  • Northwind Transact-SQL (T-SQL) 文稿 複製到剪貼簿。

  • 將 T-SQL 腳本貼到查詢編輯器中,然後選擇 [ 執行 ]。

    T-SQL 腳本查詢會建立 Northwind 資料庫,並填入數據。

  • 為 Northwind 資料庫 新增連線

    設定 WPF 應用程式專案

    若要設定 WPF 應用程式專案,請遵循下列步驟:

  • 在 Visual Studio 中,建立新的 C# WPF 應用程式 (.NET Framework) 專案。

  • 新增 Entity Framework 6 的 NuGet 套件。 在 [ 方案總管] 中,選取項目節點。 在主選單中,選擇「 專案 > 管理 NuGet 套件 」。

  • NuGet 套件管理員 中,選取 [流覽] 連結。 搜尋並選取 EntityFramework 套件。 選取右窗格中的 [安裝 ],然後遵循提示。

    [ 輸出 ] 視窗會顯示進度,並在安裝完成時通知您。

  • [選擇您的數據連線] 中,選取您的 LocalDB Northwind 連線(例如 ,localdb)\MSSQLLocalDB ),然後選取 [ 下一步 ]。

  • 如果您沒有看到連線:

  • 選擇 [新增連線 ]。 如果未選取 Microsoft SQL Server 作為 [ 連接屬性 ] 對話方塊中的 [資料源 ],請選取 [ 變更 ]。 在 [ 選擇數據源] 對話框中,選擇 [Microsoft SQL Server ],然後選取 [ 確定 ]。

  • 在 [ 連線屬性] 對話框中,輸入 (localdb)\MSSQLLocalDB 作為 [伺服器名稱 ]。

  • 針對 [選取或輸入資料庫名稱 ],選取 [Northwind ],然後選取 [ 確定 ]。

  • [選擇您的數據連線 ] 中,選取您的 LocalDB Northwind 連線,然後選取 [ 下一步 ]。

  • 如果出現提示,請選擇您使用的 Entity Framework 版本,然後選取 [ 下一步 ]。

    檔案 .tt 是一般用途,您必須編輯其中一個檔案,才能使用需要 ObservableCollection 物件的 WPF 數據系結。 請遵循下列步驟:

  • 方案總管 中,展開 Northwind_model 節點,直到您找到 Northwind_model.tt 為止。 按兩下此檔案並進行下列編輯:

  • 將出現的兩個 ICollection 替換為 ObservableCollection<T>

  • 將第一個出現的 HashSet<T> 取代為 ObservableCollection<T> ,位置接近第 51 行。 請勿取代HashSet的第二次出現。

  • 將唯一出現的 System.Collections.Generic (接近第 431 行) 取代為 System.Collections.ObjectModel

  • F5 以組建及執行專案。 應用程式第一次執行時,數據源精靈可以看到模型類別。

    現在您已準備好將此模型連結至 XAML 頁面,以便檢視、巡覽及修改數據。

    數據會將模型系結至 XAML 頁面

    雖然您可以撰寫自己的數據系結程序代碼,但更容易讓Visual Studio為您執行。 若要這樣做,請遵循下列步驟:

  • 從主功能表中,選擇 [ 專案 > 新增數據源 ] 以顯示 [數據源組態精靈 ]。 由於您要系結至模型類別,而不是系結至資料庫,請選擇 [物件 ]。 選取 下一步

  • [方案總管] 中 ,按兩下專案中的 MainWindow.xaml 以編輯 XAML。 將 Title 從 MainWindow 變更為更具描述性的名稱,並將 Height Width 分別增加到 600 和 800(您可以視需要稍後更改這些值)。

  • 將這三個數據列定義新增至主要方格、一個用於瀏覽按鈕的數據列、一個用於客戶詳細數據,另一個用於顯示其訂單的方格:

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

    接下來,您會在 Customers 類別中的每個屬性以個別文本框顯示。 請遵循下列步驟:

  • [方案總管] 中,按兩下 MainWindow.xaml ,在設計工具中開啟它。

    [數據源] 索引標籤會出現在 [工具箱] 附近的 Visual Studio 左窗格中。

  • 若要開啟 [數據源 ] 視窗,請選取 [ 數據源 ] 索引卷標,或從功能表中選擇 [ 檢視>其他 Windows>數據源 ]。

  • [數據源] 中,選取 [ 客戶],然後從下拉式清單中選取 [ 詳細數據 ]。

  • 將節點拖曳至設計區域的中間行。 如果您無法找到它,您可以選擇Grid.Row="1",然後在 XAML 中手動指定資料列。

    根據預設,控件會垂直放置在網格線元素中,但您可以依您喜歡的方式排列這些控件。 例如,您可以將 [ 名稱] 文字框放在位址上方。 本教學課程的範例應用程式會將字段重新排序,並將其重新排列成兩個數據行。

    在 XAML 檢視中,您現在可以在父方格的第 1 列(中間數據列)中看到新 Grid 元素。 父方格具有 DataContext 屬性,該屬性參考的是屬於 Windows.Resources 元素的 CollectionViewSource。 鑒於該數據內容,當第一個文本框系結至 Address 時,該名稱會對應至 Address 中目前 Customer 物件中的 CollectionViewSource屬性。

    <Grid DataContext="{StaticResource customerViewSource}">
    
  • Order類別的 Customers 屬性拖曳到窗體的下半部,讓設計工具將它放在第 2 列。

    當客戶顯示在表單的上半部時,您想要在下半部看到他們的訂單。 您可以在單一方格檢視控件中顯示訂單。 若要讓主數據系結如預期般運作,請務必系結至 Orders 類別中的 Customers 屬性,而不是系結至個別 Orders 節點。

    Visual Studio 現在會產生將 UI 控件連線到模型中事件的所有系結程序代碼。

  • 若要查看某些數據,請撰寫程式代碼以填入模型。 導航至 MainWindow.xaml.cs 並新增一個數據成員至 MainWindow 類別以供數據內容使用。

    為您產生的這個物件,就像控制項一樣,能夠追蹤模型中的變更和事件。

  • CollectionViewSource 客戶和訂單的數據成員,以及相關聯的建構函式初始化邏輯新增至現有的建構函式 MainWindow()。 課程的第一部分看起來應該像這樣:

    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;
    
  • 如果不存在,請新增 using 指示詞以將 System.Data.Entity 擴充方法帶入 Load 範圍。

    using System.Data.Entity;
    
  • 向下捲動並尋找 Window_Loaded 事件處理程式。 請注意,Visual Studio 已新增 CollectionViewSource 物件。 此物件代表 NorthwindEntities 您在建立模型時選取的物件。 因為您已經新增它,所以這裡不需要它。 取代 中的 Window_Loaded 程式代碼,讓 方法看起來像這樣:

    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;
    
  • F5

    您應該會看到第一個擷取到 CollectionViewSource 的數據格中客戶及其訂單的詳細數據。 您將在下一節中修正格式設定。 您也可以建立方法來檢視其他記錄,並執行基本建立、讀取、更新和刪除 (CRUD) 作業。

    調整頁面設計,並新增新客戶和訂單的方格

    Visual Studio 所產生的預設排列不適合您的應用程式,因此我們會在這裡提供最終的 XAML,以複製到您的程式代碼。 您也需要一些方格,讓用戶能夠新增客戶或訂單。

    若要新增客戶和訂單,請建立一組未系結至 CollectionViewSource的數據個別文本框。 您可以在處理程式方法中設定 Visible 屬性,以控制使用者在任何指定時間看到的方格。 最後,您會將 [刪除] 按鈕新增至 [訂單] 方格中的每個數據列,讓用戶能夠刪除個別訂單。

  • 開啟 MainWindow.xaml ,並將下列樣式新增至 Windows.Resources 專案:

    <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>
    
  • 以這個標記取代整個外部方格:

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

    新增按鈕以巡覽、新增、更新和刪除

    在 Windows Forms 應用程式中,您有一個 BindingNavigator 物件,其中包含用來巡覽資料庫中數據列並執行基本 CRUD 作業的按鈕。 雖然 WPF 不提供 BindingNavigator,但是只要在水準 StackPanel內建立按鈕,並將按鈕與系結至程式代碼後置檔案中方法的命令產生關聯,即可輕鬆地建立一個按鈕。

    命令邏輯有四個部分:

  • 程序代碼後置中的命令處理程式
  • 在 XAML 中新增命令、系結和按鈕

  • MainWindow.xaml檔案中,在Windows.Resources元素內新增命令,如下所示:

    <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"/>
    
  • 會將 CommandBinding 事件映射到程式碼後端中的方法。 將此CommandBindings元素新增在Windows.Resources結尾標記之後,如下所示:

    <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>
    
  • StackPanel上新增導覽、新增、刪除和更新按鈕。 將此樣式新增至 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>
    
  • 將此程式碼直接貼在 XAML 頁面接近頂端、外層Grid元素的RowDefinitions之後:

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

    將命令處理程式新增至 MainWindow 類別

    MainWindow.xaml.cs 中的程式代碼後置非常簡單,除了新增和刪除方法之外:

  • 若要巡覽,請在 的CollectionViewSource屬性上View呼叫 方法。

  • 若要在訂單上執行串聯刪除,請使用 DeleteOrderCommandHandler ,如程式代碼範例所示。 不過,您必須先刪除訂單的相關 Order_Details

    UpdateCommandHandler使用 將客戶或訂單新增至集合,或使用使用者在文本框中所做的變更來更新現有的客戶或訂單。

  • 將這些處理程式方法新增至 MainWindow 中的 MainWindow.xaml.cs類別。 如果您的 CollectionViewSource 客戶資料表的名稱不同,您必須在每個方法中調整名稱:

    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);
                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
                newOrder.EmployeeID = Int32.Parse(add_employeeIDTextBox.Text);
            catch
                MessageBox.Show("EmployeeID must be a valid integer value.");
                return;
                // 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);
                    MessageBox.Show("Shipper ID must be 1, 2, or 3 in Northwind.");
                    return;
            catch
                MessageBox.Show("Ship Via must be convertible to int");
                return;
                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);
    

    執行 WPF 應用程式

  • 若要開始偵錯,請按 F5。 確認客戶和訂單數據已填入方格中,且導覽按鈕會如預期般運作。

  • 選取 提交,以將新客戶或訂單新增至模型,前提是您已輸入數據。

  • 選取 取消 以退出新的客戶或新訂單窗體,而不儲存資料。

  • 若要直接對現有客戶和訂單進行編輯,請使用文本框,以自動將這些變更寫入模型。

    適用於 .NET 的 Visual Studio 資料工具
  • Entity Framework 文件
  •