WinForms / WPF 常見需求:從資料庫日期欄位提取不重複年份繫結至 ComboBox,供使用者篩選年度報表。本文提供完整資料存取封裝、SQL 查詢、繫結屬性設定、預設選項邏輯。
public class MyDBase : IDisposable
{
private readonly SqlConnection _conn;
public MyDBase(string server, string db, string user, string pwd)
{
var cs = $"Server={server};Database={db};User Id={user};Password={pwd};";
_conn = new SqlConnection(cs);
_conn.Open();
}
public DataSet GetRecordset(string sql)
{
var ds = new DataSet();
using (var da = new SqlDataAdapter(sql, _conn))
da.Fill(ds);
return ds;
}
public bool ErrorCode() => false; // 簡化:實際應檢查 _conn.State / 執行例外
public string ErrMessage() => "";
public void DBClose() => _conn?.Close();
public void Dispose() => DBClose();
}
private void Education_Training_Load(object sender, EventArgs e)
{
using (var db = new MyDBase(DBUser.sserver, DBUser.DBName, DBUser.suser, DBUser.spasswd))
{
// 1. 查詢不重複年份,降序排列(最新在前)
const string sql = "SELECT DISTINCT YEAR([date]) AS [Year] FROM education_train ORDER BY [Year] DESC";
DataSet ds = db.GetRecordset(sql);
if (ds.Tables[0].Rows.Count == 0) return; // 無資料直接返回
// 2. 繫結 ComboBox
comboBox_Year.DataSource = ds.Tables[0];
comboBox_Year.DisplayMember = "Year"; // 顯示給使用者看的欄位
comboBox_Year.ValueMember = "Year"; // 實際取得的值
// 3. 預設選中最新年份(索引 0,因 SQL 已 DESC)
comboBox_Year.SelectedIndex = 0;
// 4. 同步顯示於 DataGridView(示範)
dataGridView1.DataSource = ds.Tables[0];
} // using 確保連線關閉
}
| 屬性/方法 | 作用 | 注意事項 |
|---|---|---|
| SELECT DISTINCT YEAR(date) | 提取日期欄位年份並去重 | SQL Server YEAR() 函數回傳 INT |
| ORDER BY [Year] DESC | 最新年份在前 | 配合 SelectedIndex=0 直接預設最新 |
| DisplayMember / ValueMember | 同欄位雙重指定 | 若需顯示「2024 年」格式,可在 SQL 用 CONCAT(YEAR(date),' 年') |
| SelectedIndex = 0 | 預設選第一項 | 避免 -1 導致無選項 |
| using 區塊 | 自動釋放連線 | 防止連線洩漏,生產碼必加 |
string sql = "SELECT DISTINCT YEAR([date]) AS [Year] FROM education_train WHERE [date] >= @start ORDER BY [Year] DESC";
using (var cmd = new SqlCommand(sql, _conn))
{
cmd.Parameters.AddWithValue("@start", startDate);
using (var da = new SqlDataAdapter(cmd))
da.Fill(ds);
}
<ComboBox x:Name="comboBox_Year"
DisplayMemberPath="Year"
SelectedValuePath="Year"
ItemsSource="{Binding Years}" />
// ViewModel
public ObservableCollection Years { get; } = new();
public async Task LoadYearsAsync()
{
using (var conn = new SqlConnection(cs))
using (var cmd = new SqlCommand("SELECT DISTINCT YEAR([date]) FROM education_train ORDER BY 1 DESC", conn))
{
await conn.OpenAsync();
using (var rdr = await cmd.ExecuteReaderAsync())
while (await rdr.ReadAsync()) Years.Add(rdr.GetInt32(0));
}
// 預設選最新
SelectedYear = Years.FirstOrDefault();
}
YEAR(NULL) 回傳 NULL,DISTINCT 會保留一個 NULL 年份 → 繫結時出現空白項。解決:WHERE [date] IS NOT NULL。ValueMember 為 INT,取值時用 (int)comboBox_Year.SelectedValue,勿用 ToString() 再轉型。掌握 DISTINCT YEAR() + DataSource 繫結模式,即可快速實作年度篩選下拉選單,代碼簡潔且易維護。