news 2026/6/9 2:07:08

告别增删改查Demo!用WinForm + SQLite做个带UI的本地数据管理器(C#实战)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
告别增删改查Demo!用WinForm + SQLite做个带UI的本地数据管理器(C#实战)

告别增删改查Demo!用WinForm + SQLite做个带UI的本地数据管理器(C#实战)

在初学数据库编程时,我们往往满足于控制台应用中执行几条SQL语句的Demo。但当需要开发真实可用的桌面工具时,如何将数据库操作与用户界面优雅结合就成了新的挑战。本文将带你用WinForm和SQLite打造一个功能完整的本地数据管理器,实现从命令行到图形界面的关键跨越。

这个项目特别适合需要快速开发小型数据管理工具的.NET开发者。我们将重点解决三个核心问题:如何通过DataGridView实现数据的可视化展示与编辑、如何利用BindingSource简化数据绑定逻辑,以及如何将SQLite的性能优化技巧应用到实际窗体程序中。最终完成的程序将具备数据浏览、条件筛选、批量操作等实用功能。

1. 环境准备与项目搭建

1.1 创建WinForm项目

在Visual Studio中新建Windows窗体应用项目,建议选择.NET Framework 4.7.2或更高版本。这个版本对SQLite有更好的支持,也包含了我们需要的WinForm控件。

通过NuGet添加以下关键包:

  • System.Data.SQLite(完整版,非Core版本)
  • System.Data.SQLite.Linq(可选,用于LINQ支持)

注意:安装时务必确认包作者是"SQLite Development Team",避免使用第三方维护的可能存在兼容性问题的版本。

1.2 设计基础数据库结构

我们将创建一个简单的联系人管理系统作为示例。使用DB Browser for SQLite创建数据库文件并执行以下SQL:

CREATE TABLE Contacts ( Id INTEGER PRIMARY KEY AUTOINCREMENT, Name TEXT NOT NULL, Phone TEXT, Email TEXT, Category TEXT CHECK(Category IN ('同事', '朋友', '家人', '其他')), CreateTime DATETIME DEFAULT CURRENT_TIMESTAMP ); -- 插入示例数据 INSERT INTO Contacts (Name, Phone, Email, Category) VALUES ('张三', '13800138000', 'zhangsan@example.com', '同事'), ('李四', '13900139000', 'lisi@example.com', '朋友');

将生成的.db文件放入项目目录下的App_Data文件夹(需手动创建),并设置文件的"复制到输出目录"属性为"始终复制"。

2. 构建主数据管理界面

2.1 窗体布局设计

创建主窗体MainForm,包含以下核心控件:

  • DataGridView(命名为dgvContacts):用于展示和编辑数据
  • BindingSource(命名为bsContacts):作为数据绑定的桥梁
  • 工具栏或菜单栏:包含刷新、新增、删除、保存等操作按钮
  • 筛选面板:包含TextBox和ComboBox用于条件筛选

关键布局代码如下:

// 初始化DataGridView dgvContacts.AutoGenerateColumns = false; dgvContacts.AllowUserToAddRows = false; dgvContacts.SelectionMode = DataGridViewSelectionMode.FullRowSelect; // 手动定义列 var columns = new[] { new DataGridViewTextBoxColumn { DataPropertyName = "Id", HeaderText = "ID", ReadOnly = true }, new DataGridViewTextBoxColumn { DataPropertyName = "Name", HeaderText = "姓名" }, new DataGridViewTextBoxColumn { DataPropertyName = "Phone", HeaderText = "电话" }, new DataGridViewComboBoxColumn { DataPropertyName = "Category", HeaderText = "分类", DataSource = new List<string> { "同事", "朋友", "家人", "其他" } } }; dgvContacts.Columns.AddRange(columns);

2.2 实现数据绑定与加载

创建数据访问层类ContactRepository,封装数据库操作:

public class ContactRepository { private readonly string _connectionString; public ContactRepository(string dbPath) { _connectionString = $"Data Source={dbPath};Version=3;"; ConfigurePerformance(); } private void ConfigurePerformance() { using var conn = new SQLiteConnection(_connectionString); conn.Open(); var cmd = conn.CreateCommand(); cmd.CommandText = @" PRAGMA synchronous = NORMAL; PRAGMA journal_mode = WAL; PRAGMA cache_size = -5000;"; cmd.ExecuteNonQuery(); } public DataTable GetAllContacts() { using var conn = new SQLiteConnection(_connectionString); var adapter = new SQLiteDataAdapter("SELECT * FROM Contacts", conn); var dt = new DataTable(); adapter.Fill(dt); return dt; } public int SaveChanges(DataTable changes) { using var conn = new SQLiteConnection(_connectionString); var adapter = new SQLiteDataAdapter("SELECT * FROM Contacts", conn); var builder = new SQLiteCommandBuilder(adapter); adapter.InsertCommand = builder.GetInsertCommand(); adapter.UpdateCommand = builder.GetUpdateCommand(); adapter.DeleteCommand = builder.GetDeleteCommand(); return adapter.Update(changes); } }

在窗体中加载数据:

private void LoadData() { var dbPath = Path.Combine(Application.StartupPath, @"App_Data\contacts.db"); var repository = new ContactRepository(dbPath); bsContacts.DataSource = repository.GetAllContacts(); dgvContacts.DataSource = bsContacts; }

3. 实现高级数据操作功能

3.1 条件筛选与搜索

为TextBox和ComboBox添加事件处理,实现实时筛选:

private void ApplyFilter() { if (!(bsContacts.DataSource is DataTable dt)) return; var filters = new List<string>(); if (!string.IsNullOrWhiteSpace(txtSearch.Text)) filters.Add($"Name LIKE '%{txtSearch.Text}%'"); if (cmbCategory.SelectedItem != null) filters.Add($"Category = '{cmbCategory.SelectedItem}'"); bsContacts.Filter = filters.Any() ? string.Join(" AND ", filters) : null; }

3.2 批量操作与数据验证

实现批量删除和导入导出功能:

// 批量删除选中行 private void btnBatchDelete_Click(object sender, EventArgs e) { if (dgvContacts.SelectedRows.Count == 0) return; var result = MessageBox.Show($"确定要删除选中的{dgvContacts.SelectedRows.Count}条记录吗?", "确认删除", MessageBoxButtons.YesNo); if (result == DialogResult.Yes) { foreach (DataGridViewRow row in dgvContacts.SelectedRows) { bsContacts.RemoveAt(row.Index); } SaveChanges(); } } // 导出为CSV private void ExportToCsv(DataTable data, string filePath) { var sb = new StringBuilder(); // 列头 var columnNames = data.Columns.Cast<DataColumn>() .Select(c => c.ColumnName); sb.AppendLine(string.Join(",", columnNames)); // 数据行 foreach (DataRow row in data.Rows) { var fields = row.ItemArray.Select(f => $"\"{f.ToString().Replace("\"", "\"\"")}\""); sb.AppendLine(string.Join(",", fields)); } File.WriteAllText(filePath, sb.ToString(), Encoding.UTF8); }

4. 性能优化与异常处理

4.1 SQLite性能调优

在ContactRepository的构造函数中,我们已经设置了WAL模式和缓存大小。此外还可以添加:

// 在ConfigurePerformance方法中添加 cmd.CommandText = @" PRAGMA temp_store = MEMORY; PRAGMA busy_timeout = 3000;"; cmd.ExecuteNonQuery();

对于批量插入操作,建议使用事务:

public void BatchInsertContacts(List<Contact> contacts) { using var conn = new SQLiteConnection(_connectionString); conn.Open(); using var transaction = conn.BeginTransaction(); try { foreach (var contact in contacts) { var cmd = conn.CreateCommand(); cmd.CommandText = @"INSERT INTO Contacts (Name, Phone, Email, Category) VALUES (@name, @phone, @email, @category)"; cmd.Parameters.AddWithValue("@name", contact.Name); // 添加其他参数... cmd.ExecuteNonQuery(); } transaction.Commit(); } catch { transaction.Rollback(); throw; } }

4.2 健壮的错误处理机制

实现全局异常处理和应用日志:

// 在Program.cs中 static class Program { [STAThread] static void Main() { Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException); Application.ThreadException += (s, e) => HandleException(e.Exception); AppDomain.CurrentDomain.UnhandledException += (s, e) => HandleException(e.ExceptionObject as Exception); Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); Application.Run(new MainForm()); } static void HandleException(Exception ex) { if (ex == null) return; // 记录日志 File.AppendAllText("error.log", $"[{DateTime.Now}] {ex}\n\n", Encoding.UTF8); // 用户友好提示 MessageBox.Show($"发生错误: {ex.Message}\n\n详细信息已记录", "系统错误", MessageBoxButtons.OK, MessageBoxIcon.Error); } }

5. 界面美化与用户体验优化

5.1 数据可视化增强

为DataGridView添加条件格式:

private void DgvContacts_CellFormatting(object sender, DataGridViewCellFormattingEventArgs e) { if (dgvContacts.Columns[e.ColumnIndex].Name == "Category" && e.Value != null) { e.CellStyle.BackColor = e.Value.ToString() switch { "同事" => Color.LightBlue, "朋友" => Color.LightGreen, "家人" => Color.LightPink, _ => Color.White }; } if (dgvContacts.Columns[e.ColumnIndex].Name == "Phone" && e.Value != null) { if (!Regex.IsMatch(e.Value.ToString(), @"^\d{11}$")) e.CellStyle.ForeColor = Color.Red; } }

5.2 添加状态栏与进度反馈

实现操作状态提示:

private void ShowStatus(string message, int timeout = 3000) { statusLabel.Text = message; if (timeout > 0) { var timer = new Timer { Interval = timeout }; timer.Tick += (s, e) => { statusLabel.Text = "就绪"; timer.Stop(); timer.Dispose(); }; timer.Start(); } } // 在数据加载等耗时操作中使用 private async void btnRefresh_Click(object sender, EventArgs e) { ShowStatus("正在加载数据...", 0); await Task.Run(() => LoadData()); ShowStatus($"数据加载完成,共{bsContacts.Count}条记录"); }

6. 项目扩展与部署

6.1 添加插件系统支持

设计简单的插件接口:

public interface IDataManagerPlugin { string Name { get; } void Initialize(MainForm form); void Execute(); } // 示例插件:数据统计 public class StatsPlugin : IDataManagerPlugin { public string Name => "数据统计"; private MainForm _form; public void Initialize(MainForm form) { _form = form; var menuItem = new ToolStripMenuItem(Name); menuItem.Click += (s, e) => Execute(); _form.PluginsMenu.DropDownItems.Add(menuItem); } public void Execute() { if (!(_form.BindingSource.DataSource is DataTable dt)) return; var stats = dt.AsEnumerable() .GroupBy(r => r.Field<string>("Category")) .Select(g => new { Category = g.Key, Count = g.Count(), LastDate = g.Max(r => r.Field<DateTime>("CreateTime")) }); var sb = new StringBuilder(); foreach (var stat in stats) { sb.AppendLine($"{stat.Category}: {stat.Count}条,最后添加于{stat.LastDate:yyyy-MM-dd}"); } MessageBox.Show(sb.ToString(), "分类统计"); } }

6.2 打包与部署注意事项

使用ClickOnce或Inno Setup创建安装包时,需特别注意:

  • 包含SQLite.Interop.dll(根据平台选择x86或x64版本)
  • 设置数据库文件的正确访问权限
  • 添加.NET Framework运行时的安装检查

创建简单的配置类处理用户设置:

public class AppSettings { private static readonly string ConfigPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "MyDataManager", "settings.json"); public string LastUsedDatabase { get; set; } public WindowState WindowState { get; set; } public Size WindowSize { get; set; } public Point WindowLocation { get; set; } public static AppSettings Load() { try { if (File.Exists(ConfigPath)) { var json = File.ReadAllText(ConfigPath); return JsonConvert.DeserializeObject<AppSettings>(json); } } catch { } return new AppSettings(); } public void Save() { Directory.CreateDirectory(Path.GetDirectoryName(ConfigPath)); var json = JsonConvert.SerializeObject(this); File.WriteAllText(ConfigPath, json); } }
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/9 2:05:01

如何用WebPShop插件为Photoshop解锁WebP完整能力

如何用WebPShop插件为Photoshop解锁WebP完整能力 【免费下载链接】WebPShop Photoshop plug-in for opening and saving WebP images 项目地址: https://gitcode.com/gh_mirrors/we/WebPShop 还在为Photoshop原生WebP支持功能不全而烦恼吗&#xff1f;虽然Photoshop 23.…

作者头像 李华