---
tags: 110-2視窗程式設計
---
# 實作:操作資料庫
上次帶領同學實地對資料庫做連線,並且應用在會員登入的情境之下,接下來幫助同學實地進行資料庫系統操作工作,讓你能夠透過視窗程式來做資料的**查詢(或取得)、新增、修改(或更新)與刪除**。
## 程式功能
這次我們會設計一個簡單的操作,使用者能夠連線資料庫,並且查詢出所有資料,然後可以修改、新增與刪除裡面的資料。
其實大多數的資訊系統都是類似的工作,你會需要看到資料顯示在程式裡面,你可能想修改資料內容,如果有新的資料要加進去,你會新增這些資料到系統中。同樣的,假如有些資料不需要了,你會進行刪除工作。因此資料庫系統的設計,也就主要包括了:
**1. 查詢(或取得)
2. 新增
3. 修改(或更新)
4. 刪除**
這邊我們透過一個簡單的案例,告訴同學如何以程式進行這四個主要的工作,下次你使用任何資訊系統,你也會發現其實多半也就是這四種工作。

## 開啟新專案
開啟新專案之前有教學過,如果你忘記了或不太熟悉,請點選[這裡](https://hackmd.io/sFFV6T2oT0-ysHotbIybhw?view#WPF-%E7%9A%84%E5%B0%88%E6%A1%88%E8%A8%AD%E5%AE%9A)。
:::success
我們的專案名稱可以叫做「Datatable」,簡單就好。
:::
## 開始吧!
還記得第二部分的基本視窗程式設計嗎?同樣的,我們依照之前說過的兩個主要程式設計流程來設計:
1. 介面基本設計
2. 程式撰寫
因此,我們也是先初步設計介面,再來進行程式撰寫。
## 第一步:進行介面基本設計
和過去的範例程式都類似,請你先從工具箱拉控制項元件進來,需要 5 個輸入文字框、4 個按鍵。其中,這次我們使用一個新的控制項稱為「**==資料網格(DataGird)==**」,像這樣的控制項很適合顯示表格資料,例如下圖。由於資料庫的資料多半是表格型態的,所以我們使用資料網格來顯示資料庫的查詢結果。

將每一個控制項設定一個名稱,例如以下的範例名稱:
:::info
1. 資料網格:dgData
2. ID輸入文字框:txtId
3. 姓名輸入文字框:txtName
4. 帳號輸入文字框:txtAccount
5. 密碼輸入文字框:txtPassword
6. 管理者輸入文字框:txtAdmin
7. 顯示所有資料按鍵:btnLogin
8. 更改按鍵:btnUpdate
9. 新增按鍵:btnInsert
10. 刪除按鍵:btnDelete
:::

## 第二部分:程式碼撰寫
接下來,由於我們要連線的資料庫叫做 MySQL 資料庫管理系統,如果要用 Visual Studio 2019 來連線到 MySQL,必須要使用 NuGet 安裝第三方的 MySQL.Data 套件程式庫。
請參考這個[連結](https://hackmd.io/FWm-tgyaSTivwWyJ6ol_iA?view#%E7%AC%AC%E4%BA%8C%E9%83%A8%E5%88%86%EF%BC%9A%E7%A8%8B%E5%BC%8F%E7%A2%BC%E6%92%B0%E5%AF%AB),來安裝套件。
### 事件綁定
然後我們就要把程式寫進來,讓程式可以運作,你可以先將所有按鍵都先做一次 click 事件綁定。以下的程式碼你可先參考,將所需要的第 1 行、第15-18行先行置入。
```csharp=
using System.Data; // 導入 DataTable 函式庫
using MySql.Data.MySqlClient; // 導入第三方 MySQL.Data 套件的連線程式庫
namespace Datatable
{
/// <summary>
/// MainWindow.xaml 的互動邏輯
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
// 資料庫連線字串,Server:資料庫管理系統的位址(IP或網址)、Uid:可以使用資料庫的帳號、Pwd:可以使用資料庫的密碼、database:要連線的資料庫名稱
private string connstr = "Server=120.101.58.4;Uid=user;Pwd=user;database=login";
// 建立一個資料表物件 dt,我們把資料庫中的資料抓出來之後,放到這個物件中,然後顯示在表格上
private DataTable dt = new DataTable();
private void btnQuery_Click(object sender, RoutedEventArgs e)
{
}
private void btnUpdate_Click(object sender, RoutedEventArgs e)
{
}
private void btnInsert_Click(object sender, RoutedEventArgs e)
{
}
private void btnDelete_Click(object sender, RoutedEventArgs e)
{
}
}
}
```
一樣為了要對資料庫連線,必須要先設定連線字串,connstr 變數就儲存了對 MySQL 資料庫的連線字串。
### 資料表 DataTable 物件基礎認識
其中我們這次使用一個新的資料型態,叫做「資料表(DataTable)」物件,這種變數顧名思義,就是用來放表格的。你可以這樣想,既然資料庫查詢出來的結果,本來就是表格的樣子,那麼像這樣的資料表物件最適合來存放資料庫查詢結果,因此我們使用這種物件來儲存查詢結果。
宣告一個資料表的語法如下:
```csharp
DataTable dt = new DataTable();
```
我們宣告一個資料表物件之後,將查詢結果放到這個物件裡面,然後用程式顯示在畫面上。如同下圖的概念。

### 將資料庫資料查詢結果放到資料表物件
接下來我們要實作將資料庫查詢的結果,放到資料表物件中,請將以下程式碼放到你的專案裡面。
你可以看的到,以下程式是一個函式,它進行的工作就是和資料庫連線。連線成功後,使用一種叫做「資料庫連接器(Adapter)」與資料庫做橋梁,對資料庫下 SQL 操作指令進行查詢,再將查詢結果放到資料表物件中。最後將資料表物件的內容,顯示在資料網格(dgData)中,你就可以在程式畫面上看到資料內容。
另外,我們也需要在資料網格設定為「綁定資料表」,請到資料網格的屬性中,找尋一個「ItemsSource」項目,點選右側的小正方形,再選擇「自訂運算式」,輸入「{ Binding }」,這樣才能讓資料表物件與資料網格進行連動。

然後你可以把這個函式的執行,放到「顯示所有資料」按鍵的 Click 事件綁定。這時你可以測試程式,按下「顯示所有資料」按鍵,就可以看到左側的資料網格中出現了查詢結果。
```csharp=
// 查詢並顯示資料的函式
private void GetAllData()
{
try
{
// 使用 using 語法來進行資料庫連線
using (var conn = new MySqlConnection(connstr))
{
conn.Open(); // 開啟資料庫連線
// 使用 using 語法來設定資料庫的連接器
using (var da = new MySqlDataAdapter())
{
string sqlSelectAll = "SELECT * FROM users"; // SQL指令
da.SelectCommand = new MySqlCommand(sqlSelectAll, conn); // 執行查詢
dt.Clear(); // 清除資料表物件
da.Fill(dt); // 將查詢到的資料放到資料表物件
dgData.DataContext = dt; // 讓資料表物件顯示在資料網格裡面
}
}
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString()); // 如果中間的資料庫連線流程有問題,就會跳出錯誤訊息
}
}
private void btnQuery_Click(object sender, RoutedEventArgs e)
{
GetAllData(); // 按下「顯示所有資料」按鍵,執行查詢並顯示資料的函式
}
```

### 點選資料,顯示在輸入對話框之中
然後我們要設計一個簡單機制,當你選擇資料網格的其中一列資料,也會同時在右側的輸入文字框中出現,為什麼要這樣設計呢?因為我們希望點選一列資料,可以讓使用者進行更改或刪除的動作。因此,必須要讓特定資料被顯示出來,才能做這樣的更改與刪除。
當然你要做到這樣的效果,必須也要找到這個資料網格的特定事件,請回到視窗設計屬性視窗的事件清單中,找尋一個「**SelectionChanged 事件**」,並且綁定它,如下圖

然後將以下的程式碼放到這個事件綁定中。
```csharp=
private void dgData_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
int selected_index = dgData.SelectedIndex; // 取得你按的是第幾列的資料?
// 因為要取得資料顯示在輸入文字框中,必須將在DataGrid中所選擇的列,與資料表物件的資料列位置彼此對應
// 但可能DataGrid沒有資料顯示(selected_index = -1),或者選到最下面的一列(selected_index = 資料表物件總列數),造成與資料表物件無法相對應,會產生錯誤,因此必須要做判斷
if (selected_index >= 0 && selected_index != dt.Rows.Count)
{
txtId.Text = dt.Rows[selected_index].ItemArray[0].ToString();
txtAccount.Text = dt.Rows[selected_index].ItemArray[1].ToString();
txtPassword.Text = dt.Rows[selected_index].ItemArray[2].ToString();
txtName.Text = dt.Rows[selected_index].ItemArray[3].ToString();
txtAdmin.Text = dt.Rows[selected_index].ItemArray[4].ToString();
}
}
```
這一段的程式很單純,其實就先找出你你按的是第幾列的資料?取得第幾列的數值後,做一個簡單的判斷,避免數值小於 0 或者剛好等同於資料表物件的資料數。
因為要取得資料顯示在輸入文字框中,必須將在DataGrid中所選擇的列,與資料表物件的資料列位置彼此對應。不過,有可能資料網格中沒有資料,也就是點選資料網格後,取得的第幾列數值為 -1,或者選到最下面的一列,造成與資料表物件無法相對應,會產生錯誤,因此必須要做判斷。
如果我們選到的資料都是有資料的,就會在資料物件中相對應的資料列取出,逐一顯示在輸入文字框之中。
現在你測試程式,只要點選表格中的任何一筆資料,就會顯示在輸入文字框之中。

### 資料修改
接下來我們設計資料修改的機制,請在「更改」按鍵的 click 事件綁定中放入以下程式內容:
```csharp=
private void btnUpdate_Click(object sender, RoutedEventArgs e)
{
try
{
using (var conn = new MySqlConnection(connstr))
{
conn.Open();
// 使用 using 語法來設定資料庫的操作命令
using (MySqlCommand cmd = new MySqlCommand("UPDATE users SET account=@account, password=@password, name=@name WHERE id=@id", conn))
{
cmd.Parameters.AddWithValue("@account", txtAccount.Text); // 將輸入文字框的資料與資料庫操作指令做對應
cmd.Parameters.AddWithValue("@password", txtPassword.Text);
cmd.Parameters.AddWithValue("@name", txtName.Text);
cmd.Parameters.AddWithValue("@id", txtId.Text);
cmd.ExecuteNonQuery(); // 執行資料庫操作
}
}
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString()); // 如果中間的資料庫連線流程有問題,就會跳出錯誤訊息
}
finally
{
GetAllData(); // 執行完操作後,重新整理資料
}
}
```
以上的程式和之前資料庫查詢的方式都有點類似,也是要先設定SQ操作指令,這邊我們使用的指令稱為「**更新指令(Update)**」,並且我們連結輸入文字框的資料,最後使用「ExecuteNonQuery()」執行資料庫操作,真實地將資料庫中的資料進行修改。
並且在 finally 的階段,還會再重新查詢一次資料,這是因為修改後的資料還是要再查詢一次,才能同步更新在資料網格中。
這時你進行程式測試,點選表格中的任何一筆資料,然後在輸入文字框中修改資料,按下「更改」按鍵,你就會看到資料也同步修改在左側的資料網格中。

### 資料新增與刪除
同樣的,資料的新增與刪除其實都與更新類似,請在「新增」、「刪除」按鍵的 click 事件綁定中放入以下程式內容:
```csharp=
private void btnInsert_Click(object sender, RoutedEventArgs e)
{
try
{
// 使用 using 語法來進行資料庫連線
using (var conn = new MySqlConnection(connstr))
{
conn.Open();
using (MySqlCommand cmd = new MySqlCommand("INSERT INTO users (account, password, name) VALUES (@account, @password, @name)", conn))
{
cmd.Parameters.AddWithValue("@account", txtAccount.Text);
cmd.Parameters.AddWithValue("@password", txtPassword.Text);
cmd.Parameters.AddWithValue("@name", txtName.Text);
cmd.ExecuteNonQuery();
}
}
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString()); // 如果中間的資料庫連線流程有問題,就會跳出錯誤訊息
}
finally
{
GetAllData();
}
}
private void btnDelete_Click(object sender, RoutedEventArgs e)
{
try
{
using (var conn = new MySqlConnection(connstr))
{
conn.Open();
using (MySqlCommand cmd = new MySqlCommand("DELETE FROM users WHERE id=@id", conn))
{
cmd.Parameters.AddWithValue("@id", int.Parse(txtId.Text));
cmd.ExecuteNonQuery();
}
}
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString()); // 如果中間的資料庫連線流程有問題,就會跳出錯誤訊息
}
finally
{
GetAllData();
}
}
```
你也可以看到,其實這兩個按鍵的程式內容,基本上都類似,主要差異也是在 SQL 操作指令上面,如果是新增資料,使用的是「Insert」指令,如果是刪除資料,則是使用「Delete」指令
## 結語
以上的程式我們讓同學初步理解,利用資料庫其實能夠儲存各種樣式的資料,只要你能夠進行資料庫連線,就能進行查詢、新增、修改與刪除等工作。大多數的資訊系統其實都是類似的形式,現代的各種資訊系統簡單來看,也都是類似的形式。