C#基于Sunnyui框架和MVC模式实现用户登录管理

C#基于Sunnyui框架和MVC模式实现用户登录管理

  • 1 Controller
    • 1.1 UserManagementController.cs(控制器入口)
  • 2 Model
    • 2.1 UserRepository.cs(用户管理模型)
    • 2.2 User.cs(用户结构体)
    • 2.3 SQLiteHelper.cs(数据库工具类)
  • 3 View
    • 3.1 LoginView.cs(用户登录窗体)
    • 3.2 UserManagementForm(用户信息管理窗体)
      • 3.2.1 UserManagementForm.cs
      • 3.2.2 UserManagementForm.Designer.cs
    • 3.3 UserUpdataForm.cs(添加、修改用户对话框)
  • 4 扩展类

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

说明

  • 类库结构如上图;
  • 外部调用通过控制器入口调用
  • 用户信息存储于数据库表中,路径:输出目录中userdata.db;
  • 初始化自动生成开发者账号信息;

1 Controller

1.1 UserManagementController.cs(控制器入口)

using System;
using System.Windows.Forms;
using UserManage.Extensions;
using UserManage.Model;
using UserManage.View;namespace UserManage.Controller
{public class UserManagementController{private const string PermissionPrompt = "权限不足,请登录管理员账号";private readonly LoginView _loginForm;private readonly UserRepository _userRepository;private readonly UserManagementForm _userManagementForm;private User _currentUser;public UserType UserPermission => _currentUser?.UserType ?? UserType.Null;public UserManagementController(){_loginForm = new LoginView();_userRepository = new UserRepository();_userManagementForm = new UserManagementForm();_loginForm.LoginClicked += LoginForm_LoginClicked;_userManagementForm.AddUserClicked += UserManagementForm_AddUserClicked;_userManagementForm.ModifyUserClicked += UserManagementForm_ChangeUserPasswordClicked;_userManagementForm.DeleteUserClicked += UserManagementForm_DeleteUserClicked;}public UserManagementController(UserManagementForm userManagementForm){this._userManagementForm = userManagementForm;}public void RunLoginForm(){_loginForm.ShowDialog();}public void RunUserManagementForm(){if (_currentUser == null){UserManageExtension.ShowMsg(PermissionPrompt);return;}if (_currentUser.UserType != UserType.Developer && _currentUser.UserType != UserType.Admin){UserManageExtension.ShowMsg(PermissionPrompt);return;}LoadUser();_userManagementForm.ShowDialog();}private void LoadUser(){var table = _userRepository.GetUserTable();table.Columns[0].ColumnName = "用户名";table.Columns[1].ColumnName = "密码";table.Columns[2].ColumnName = "用户类型";_currentUser = _userRepository.GetUser(_currentUser.Username, _currentUser.Password);if (_currentUser.UserType != UserType.Developer){for (int i = 0; i < table.Rows.Count; i++){if (table.Rows[i][2].ToString().GetUserTypeByText() == UserType.Developer){table.Rows[i][1] = "******";}}}_userManagementForm.LoadUsers(table);}private void LoginForm_LoginClicked(object sender, EventArgs e){string username = _loginForm.Username;string password = _loginForm.Password;_currentUser = _userRepository.AuthenticateUser(username, password);if (_currentUser != null){_loginForm.Hide();}else{UserManageExtension.ShowMsg("用户名或密码错误!");}}private void UserManagementForm_AddUserClicked(object sender, EventArgs e){UserUpdataForm addDialog = new UserUpdataForm();switch (UserPermission){case UserType.Developer:addDialog.RefreshAddUserInfo();addDialog.AddUserType(UserType.Developer, UserType.Admin, UserType.NormalUser);break;case UserType.Admin:addDialog.RefreshAddUserInfo();addDialog.AddUserType(UserType.NormalUser);break;default:UserManageExtension.ShowMsg(PermissionPrompt);return;}addDialog.EnterClicked += (o, args) =>{try{string username = addDialog.UserName;string password = addDialog.Password;UserType userType = addDialog.UserType;if (addDialog.UserType == UserType.Null){UserManageExtension.ShowMsg("请选择用户类型!");return;}if (UserManageExtension.Confirm($"请确认添加用户信息" +$"{Environment.NewLine}用户名:{username}" +$"{Environment.NewLine}密码:{password}" +$"{Environment.NewLine}用户类型:{userType.GetUserTypeText()}" +$"{Environment.NewLine}确认添加?")){_userRepository.AddUser(username, password, userType);addDialog.DialogResult = DialogResult.OK;}}catch (Exception ex){UserManageExtension.ShowMsg(ex.Message);}};addDialog.ShowDialog();LoadUser();}private void UserManagementForm_DeleteUserClicked(object sender, EventArgs e){var selectUser = _userManagementForm.SelectUser;if (selectUser == null){UserManageExtension.ShowMsg(PermissionPrompt);return;}if (selectUser.UserType == UserType.Developer){UserManageExtension.ShowMsg("无法删除开发者账户", "警告");return;}if (!UserManageExtension.Confirm($"您正在删除用户[{selectUser.Username}],删除后将无法恢复,请确认是否继续?"))return;switch (UserPermission){case UserType.Developer:case UserType.Admin:_userRepository.DeleteUser(selectUser.Username);break;default:UserManageExtension.ShowMsg(PermissionPrompt);return;}LoadUser();}private void UserManagementForm_ChangeUserPasswordClicked(object sender, EventArgs e){var selectUser = _userManagementForm.SelectUser;if (selectUser == null){UserManageExtension.ShowMsg("请选择需要修改的用户");return;}if (selectUser.UserType == UserType.Developer && _currentUser.UserType != UserType.Developer){UserManageExtension.ShowMsg(PermissionPrompt);return;}UserUpdataForm updataDialog = new UserUpdataForm();switch (UserPermission){case UserType.Developer:updataDialog.RefreshUdpataUserInfo(selectUser);updataDialog.AddUserType(UserType.Developer, UserType.Admin, UserType.NormalUser);break;case UserType.Admin:updataDialog.RefreshUdpataUserInfo(selectUser);updataDialog.AddUserType(UserType.NormalUser);break;default:UserManageExtension.ShowMsg(PermissionPrompt);return;}updataDialog.EnterClicked += (o, args) =>{try{if (updataDialog.UserType == UserType.Null){UserManageExtension.ShowMsg("请选择用户类型!");return;}string username = updataDialog.UserName;string password = updataDialog.Password;UserType userType = updataDialog.UserType;if (UserManageExtension.Confirm($"用户[{username}],密码修改为{Environment.NewLine}{password}{Environment.NewLine}是否确认修改?")){_userRepository.ChangePassword(username, password, userType.GetUserTypeText());updataDialog.DialogResult = DialogResult.OK;}}catch (Exception ex){UserManageExtension.ShowMsg(ex.Message);}};updataDialog.ShowDialog();LoadUser();}}
}

2 Model

2.1 UserRepository.cs(用户管理模型)

using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SQLite;
using System.Diagnostics.Eventing.Reader;
using System.IO;
using System.Text.RegularExpressions;
using System.Windows.Forms;
using UserManage.Extensions;namespace UserManage.Model
{public class UserRepository{private const string TableName = "User";private string connectionString = "Data Source=users.db;Version=3;";private SQLiteHelper _sqLiteHelper;public const string DeveloperName = "Veizu";public const string DeveloperPassword = "Aa123456";public UserRepository(){CreateUserTable();EnsureDeveloperExists();}private void CreateUserTable(){string datasource = Path.Combine(Application.StartupPath, "userdata.db");string tableInfo = TableName + "( " + "Username CHAR(50) PRIMARY KEY," + "Password CHAR(50)," + "UserType CHAR(50)" + ")";if (File.Exists(datasource)){_sqLiteHelper = SQLiteHelper.OpenConnection(datasource);if (_sqLiteHelper.IsExistsTable(TableName)){_sqLiteHelper.CreateTableSql(tableInfo);}}else{_sqLiteHelper = SQLiteHelper.OpenConnection(datasource, true, tableInfo);}}private void EnsureDeveloperExists(){try{AddUser(DeveloperName, DeveloperPassword, UserType.Developer);}catch (Exception ex){}}public User AuthenticateUser(string username, string password){return GetUser(username, password);}public void IsValidUsernamePassword(string username, string password){string pattern = @"^[a-zA-Z\u4e00-\u9fa5][a-zA-Z0-9\u4e00-\u9fa5]*$";var ret = Regex.IsMatch(username, pattern);if (!ret){throw new ArgumentException("用户名第一个字符必须是英文字母或中文开头,仅允许包含中文、英文和数字。");}pattern = @"^[a-zA-Z0-9]+$";ret = Regex.IsMatch(password, pattern);if (!ret){throw new ArgumentException("用户密码仅允许字母和数字。");}const int passwordLength = 6;if (password.Length < passwordLength){throw new ArgumentException($"用户密码不得少于{passwordLength}个字符。");}}public bool IsUserExists(string username, string password = null){return GetUser(username, password) != null;}public DataTable GetUserTable(){var table = _sqLiteHelper.Select(TableName);return table.Copy();}public User GetUser(string username, string password = null){User user = null;var table = GetUserTable();foreach (DataRow row in table.Rows){if (row[nameof(User.Username)].ToString().Equals(username)){if (!string.IsNullOrWhiteSpace(password)){if (row[nameof(User.Password)].ToString().Equals(password)){UserType userType = row[nameof(User.UserType)].ToString().GetUserTypeByText();user = new User(username, password, userType);}}else{user = new User(username);}}}return user;}public void AddUser(string username, string password, UserType userType){IsValidUsernamePassword(username, password);if (IsUserExists(username)){throw new ArgumentException("该用户名已存在,请选择其他用户名。");}var parameter1 = new SQLiteParameter { ParameterName = nameof(User.Username), Value = username };var parameter2 = new SQLiteParameter { ParameterName = nameof(User.Password), Value = password };var parameter3 = new SQLiteParameter { ParameterName = nameof(User.UserType), Value = userType.GetUserTypeText() };_sqLiteHelper.Insert(TableName, parameter1, parameter2, parameter3);}public void DeleteUser(string username){_sqLiteHelper.Delete(TableName, nameof(User.Username), username);}public void ChangePassword(string username, string newPassword, string newUserType){IsValidUsernamePassword(username, newPassword);string updateQuery = $"UPDATE {TableName} SET {nameof(User.Password)} = '{newPassword}',{nameof(User.UserType)} = '{newUserType}' WHERE {nameof(User.Username)} = '{username}';";_sqLiteHelper.ExecuteSql(updateQuery);}}
}

2.2 User.cs(用户结构体)

namespace UserManage.Model
{public enum UserType{Null = 1,Developer = 3,Admin = 5,NormalUser = 7,}public class User{public string Username { get; set; }public string Password { get; set; }public UserType UserType { get; set; }public User(string username, string password, UserType userType){Username = username;Password = password;UserType = userType;}public User(string username){Username = username;}}
}

2.3 SQLiteHelper.cs(数据库工具类)

using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SQLite;
using System.IO;
using System.Linq;
using System.Reflection;namespace UserManage.Model
{public class SQLiteHelper{public static SQLiteConnection _conn;private static SQLiteConnectionStringBuilder _connstr;public static SQLiteHelper OpenConnection<T>(string datasource) where T : class{if (!File.Exists(datasource)) SQLiteConnection.CreateFile(datasource);var sqLite = new SQLiteHelper();_conn = new SQLiteConnection();_connstr = new SQLiteConnectionStringBuilder { DataSource = datasource };_conn.ConnectionString = _connstr.ToString();_conn.Open();var tableName = Path.GetFileNameWithoutExtension(datasource);string query = $"SELECT name FROM sqlite_master WHERE type='table' AND name='{tableName}';";using (SQLiteCommand command = new SQLiteCommand(query, _conn)){// 执行查询并获取结果object result = command.ExecuteScalar();// 如果结果不为空,则表存在if (result == null){sqLite.CreateTable<T>();}}return sqLite;}/// <summary>/// 打开数据库/// </summary>/// <param name="datasource">数据库路径</param>/// <param name="isCreate">是否创建数据库文件</param>/// <param name="tableInfo">创建数据库文件时同时创建的表结构信息;例如:TableName(ListIndex INTEGER, ListTime CHAR(23), ListSpeed INTEGER, ListRemark CHAR(50))</param>/// <returns>返回SQLiteHelper实例</returns>public static SQLiteHelper OpenConnection(string datasource, bool isCreate, string tableInfo = null){if (isCreate) SQLiteConnection.CreateFile(datasource);var sqLite = new SQLiteHelper();_conn = new SQLiteConnection();_connstr = new SQLiteConnectionStringBuilder { DataSource = datasource };_conn.ConnectionString = _connstr.ToString();_conn.Open();if (isCreate) sqLite.CreateTableSql(tableInfo);return sqLite;}/// <summary>/// 打开数据库/// </summary>/// <param name="datasource">数据库路径</param>/// <returns>返回SQLiteHelper实例</returns>public static SQLiteHelper OpenConnection(string datasource) => OpenConnection(datasource, false);/// <summary>/// 关闭/// </summary>public void CloseConnection(){if (_conn.State == ConnectionState.Closed)return;_conn.Close();_conn.Dispose();_connstr.Clear();}/// <summary>/// 执行自定义SQL语句,/// </summary>/// <param name="sql">自定义SQL语句</param>/// <returns>返回影响行数</returns>public int ExecuteSql(string sql){using (var cmd = _conn.CreateCommand()){cmd.CommandText = sql;return cmd.ExecuteNonQuery();}}public int CreateTable<T>() where T : class{string createTableQuery = GenerateCreateTableQuery(typeof(T));// 创建SQLite命令对象using (SQLiteCommand command = new SQLiteCommand(createTableQuery, _conn)){// 执行SQL命令command.ExecuteNonQuery();}return 0;}static string GenerateCreateTableQuery(Type type){// 获取实体类名称string tableName = type.Name;// 初始化SQL语句字符串构建器System.Text.StringBuilder query = new System.Text.StringBuilder();query.Append($"CREATE TABLE IF NOT EXISTS {tableName} (");// 获取实体类的所有属性PropertyInfo[] properties = type.GetProperties();for (int i = 0; i < properties.Length; i++){PropertyInfo property = properties[i];// 获取属性名称string columnName = property.Name;// 获取属性类型string columnType = GetSqliteType(property.PropertyType);query.Append($"{columnName} {columnType}");if (i < properties.Length - 1){query.Append(", ");}}query.Append(");");return query.ToString();}static string GetSqliteType(Type type){// 根据C#类型映射到SQLite类型if (type == typeof(int) || type == typeof(long)){return "INTEGER";}else if (type == typeof(string)){return "TEXT";}else if (type == typeof(double) || type == typeof(float)){return "REAL";}else if (type == typeof(List<DateTime>) || type == typeof(List<double>)){return "BLOB";}else{return "TEXT";}}/// <summary>/// 执行自定义SQL语句,/// </summary>/// <param name="tableInfo">表结构信息;例如:TableName(ListIndex INTEGER, ListTime CHAR(23), ListSpeed INTEGER, ListRemark CHAR(50))</param>/// <returns>返回影响行数</returns>public int CreateTableSql(string tableInfo) => ExecuteSql($"CREATE TABLE {tableInfo}");/// <summary>/// 执行插入操作/// </summary>/// <param name="tableName">表名</param>/// <param name="colName">字段名</param>/// <param name="value">参数数据</param>/// <returns>int</returns>public int Insert(string tableName, string colName, string value){using (var cmd = _conn.CreateCommand()){cmd.CommandText = $"INSERT INTO {tableName} VALUES(@{colName})";cmd.Parameters.Add(colName, DbType.String).Value = value;return cmd.ExecuteNonQuery();}}/// <summary>/// 执行插入操作/// </summary>/// <param name="tableName">表名</param>/// <param name="parameters">键值对</param>/// <returns>int</returns>public int Insert(string tableName, SQLiteParameter parameters){using (var cmd = _conn.CreateCommand()){// 添加参数到命令对象  cmd.Parameters.Add(parameters);// 构建完整的INSERT语句  string query = $"INSERT INTO {tableName} ({parameters.ParameterName}) VALUES (@{parameters.ParameterName});";cmd.CommandText = query;// 执行命令并返回受影响的行数  return cmd.ExecuteNonQuery();}}/// <summary>/// 从库中删除表/// </summary>/// <param name="tableName"></param>public int DeleteTable(string tableName){using (var cmd = _conn.CreateCommand()){cmd.CommandText = $"DROP TABLE IF EXISTS {tableName}";return cmd.ExecuteNonQuery();}}/// <summary>/// 执行删除操作/// </summary>/// <param name="tableName">表名</param>/// <param name="colName">字段名</param>/// <param name="value">参数数据</param>/// <returns>int</returns>public int Delete(string tableName, string colName, object value){using (var cmd = _conn.CreateCommand()){cmd.CommandText = $"DELETE FROM {tableName} WHERE {colName} = @value;";cmd.Parameters.Add("value", DbType.Object).Value = value;return cmd.ExecuteNonQuery();}}/// <summary>/// 执行查询操作/// </summary>/// <param name="tableName">表名</param>/// <returns>DataTable</returns>public DataTable Select(string tableName){var dt = new DataTable();var cmd = _conn.CreateCommand();cmd.CommandText = $"select * from {tableName}";var dao = new SQLiteDataAdapter(cmd);dao.Fill(dt);cmd.ExecuteNonQuery();dao.Dispose();return dt.Copy();}public bool IsExistsTable(string tableName){string cmdText = $"select count(*) from sqlite_master where name='{tableName}' and type='table';";int count = ExecuteSql(cmdText);return count > 0;}public bool IsExistsData(string tableName, Dictionary<string, object> parameter){string query = $"SELECT COUNT(*) FROM {tableName} WHERE ";foreach (var key in parameter.Keys){query += $"{key} = @{key}";}query += $";";SQLiteCommand command = new SQLiteCommand(query, _conn);foreach (var key in parameter.Keys){command.Parameters.AddWithValue($"@{key}", parameter[key]);}int count = Convert.ToInt32(command.ExecuteScalar());return count > 0;}/// <summary>/// 执行插入操作/// </summary>/// <param name="tableName">表名</param>/// <param name="parameters">键值对字典</param>/// <returns>int</returns>public int Insert(string tableName, params SQLiteParameter[] parameters){int count = -1;try{using (var cmd = _conn.CreateCommand()){string query = $"INSERT INTO" +$" {tableName}" +$" ({string.Join(",", parameters.Select(p => p.ParameterName))})" +$" VALUES ({string.Join(",", parameters.Select(p => "@" + p.ParameterName))});";cmd.Parameters.AddRange(parameters);cmd.CommandText = query;count = cmd.ExecuteNonQuery();}return count;}catch (Exception ex){throw ex;}}/// <summary>/// 批量插入/// </summary>/// <param name="tableName"></param>/// <param name="col"></param>/// <param name="list"></param>public void Insert(string tableName, string[] col, List<object[]> list){var cmd = _conn.CreateCommand();SQLiteTransaction transaction = _conn.BeginTransaction();cmd.Transaction = transaction;try{object[] obj = list.First();var parameters = new List<SQLiteParameter>();for (int i = 0; i < col.Length; i++){parameters.Add(new SQLiteParameter(col[i], obj[i]));}string sql = $"INSERT INTO" +$" {tableName}" +$" ({string.Join(",", parameters.Select(p => p.ParameterName))})" +$" VALUES ({string.Join(",", parameters.Select(p => "@" + p.ParameterName))});";foreach (var value in list){parameters = new List<SQLiteParameter>();for (int i = 0; i < col.Length; i++){parameters.Add(new SQLiteParameter(col[i], value[i]));}cmd.Parameters.AddRange(parameters.ToArray());cmd.CommandText = sql;cmd.ExecuteNonQuery();}transaction.Commit();}catch (Exception e){}}/// <summary>/// 执行更新操作/// </summary>/// <param name="tableName">表名</param>/// <param name="setValues">新数据</param>/// <param name="whereClause">条件</param>/// <param name="parameters">条件数据</param>/// <returns>int</returns>public int Update(string tableName, Dictionary<string, object> setValues, string whereClause, List<SQLiteParameter> parameters){using (var cmd = _conn.CreateCommand()){List<string> setColumns = new List<string>();int index = 0;foreach (var kvp in setValues){setColumns.Add($"{kvp.Key} = @{kvp.Key}");cmd.Parameters.Add(new SQLiteParameter($"@{kvp.Key}", kvp.Value));index++;}string query = $"UPDATE {tableName} SET {string.Join(",", setColumns)} WHERE {whereClause}";cmd.CommandText = query;cmd.Parameters.AddRange(parameters.ToArray());return cmd.ExecuteNonQuery();}}}
}

3 View

3.1 LoginView.cs(用户登录窗体)

using System;
using System.Windows.Forms;
using Sunny.UI;
using UserManage.Model;namespace UserManage.View
{public partial class LoginView : UIForm{private UILabel lblUsername;private UILabel lblPassword;private UITextBox txtUsername;private UITextBox txtPassword;private UIButton btnLogin;public event EventHandler LoginClicked;public string Username => txtUsername.Text;public string Password => txtPassword.Text;public LoginView(){InitializeComponent();InitializeUI();}private void LoginView_VisibleChanged(object sender, EventArgs e){txtUsername.Text = string.Empty;txtPassword.Text = string.Empty;txtUsername.Text = "Veizu";txtPassword.Text = "Aa123456";}private void InitializeUI(){// 设置窗体样式this.Text = "用户登录";this.Size = new System.Drawing.Size(300, 250);this.StartPosition = FormStartPosition.CenterScreen;int locationY = 80;int yInterval = 20;// 用户名标签lblUsername = new UILabel();lblUsername.Location = new System.Drawing.Point(30, locationY);lblUsername.Size = new System.Drawing.Size(80, 25);lblUsername.Text = "用户名:";this.Controls.Add(lblUsername);// 用户名文本框txtUsername = new UITextBox();txtUsername.Location = new System.Drawing.Point(120, locationY);txtUsername.Size = new System.Drawing.Size(150, 25);this.Controls.Add(txtUsername);// 密码标签locationY += yInterval + txtUsername.Height;lblPassword = new UILabel();lblPassword.Location = new System.Drawing.Point(30, locationY);lblPassword.Size = new System.Drawing.Size(80, 25);lblPassword.Text = "密码:";this.Controls.Add(lblPassword);// 密码文本框txtPassword = new UITextBox();txtPassword.Location = new System.Drawing.Point(120, locationY);txtPassword.Size = new System.Drawing.Size(150, 25);txtPassword.PasswordChar = '*';this.Controls.Add(txtPassword);// 登录按钮locationY += yInterval + txtPassword.Height;btnLogin = new UIButton();btnLogin.Size = new System.Drawing.Size(100, 30);int btnLoginX = txtPassword.Location.X + txtPassword.Width - btnLogin.Width;btnLogin.Location = new System.Drawing.Point(btnLoginX, locationY);btnLogin.Text = "登录";btnLogin.Click += btnLogin_Click;this.Controls.Add(btnLogin);}private void btnLogin_Click(object sender, EventArgs e){LoginClicked?.Invoke(this, EventArgs.Empty);}}
}

3.2 UserManagementForm(用户信息管理窗体)

3.2.1 UserManagementForm.cs

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using Sunny.UI;
using UserManage.Extensions;
using UserManage.Model;namespace UserManage.View
{public partial class UserManagementForm : UIForm{public User SelectUser{get{if (dgv_User.CurrentCell != null){var row = dgv_User.Rows[dgv_User.CurrentCell.RowIndex];string username = row.Cells[0].Value.ToString();string password = row.Cells[1].Value.ToString();UserType userType = row.Cells[2].Value.ToString().GetUserTypeByText();return new User(username, password, userType);}else{return null;}}}public event EventHandler AddUserClicked;public event EventHandler ModifyUserClicked;public event EventHandler DeleteUserClicked;public UserManagementForm(){InitializeComponent();InitializeUI();}private void InitializeUI(){// 设置窗体样式this.Size = new Size(800, 600);this.Text = "用户管理";this.StartPosition = FormStartPosition.CenterScreen;}/// <summary>/// 设置数据表样式/// </summary>private void SetDgvStyle(UIDataGridView table){try{table.ClearSelection();//int index = dgvStandard.CurrentCell.RowIndex;//获取选中行table.MultiSelect = false; //不可多选table.AllowUserToAddRows = false; //设置不显示添加行table.AllowUserToDeleteRows = false; //设置不允许删除行table.AllowUserToResizeColumns = false; //设置列不可调整table.ColumnHeadersDefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleCenter; //列名居中table.AllowUserToResizeRows = false; //设置行不可调整table.RowHeadersVisible = false; //设置表头不显示行标题table.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.Fill; //列填充控件显示区域table.CausesValidation = false; //焦点table.ColumnHeadersBorderStyle = DataGridViewHeaderBorderStyle.Single; //边框样式table.ColumnHeadersHeightSizeMode = DataGridViewColumnHeadersHeightSizeMode.AutoSize; //列高调整table.RowsDefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleCenter; //单元格内容居中table.ReadOnly = true; //只读设置table.RowHeadersWidth = 4; //列宽设置(按照权重设置时需要先添加列)table.RowHeadersWidthSizeMode = DataGridViewRowHeadersWidthSizeMode.AutoSizeToAllHeaders; //设置列表头不可调整table.RowTemplate.Height = 28; //设置行高table.ScrollBars = ScrollBars.Vertical; //显示垂直滚动条table.TabStop = false; //设置无Tab焦点table.VirtualMode = true; //设置可进行数据管理//dgv.Rows[lastTableHeadRow].Frozen = true;//冻结指定行for (var i = 0; i < table.Columns.Count; i++){table.Columns[i].SortMode = DataGridViewColumnSortMode.NotSortable; //禁止点击列名排序dgv_User.Columns[i].FillWeight = 1; //按权重分配列宽}}catch (Exception){// ignored}}private void btnAddUser_Click(object sender, EventArgs e){AddUserClicked?.Invoke(this, EventArgs.Empty);}private void btnModifyUser_Click(object sender, EventArgs e){ModifyUserClicked?.Invoke(this, EventArgs.Empty);}private void btnDeleteUser_Click(object sender, EventArgs e){DeleteUserClicked?.Invoke(this, EventArgs.Empty);}public void LoadUsers(DataTable table){dgv_User.DataSource = null;dgv_User.DataSource = table;SetDgvStyle(dgv_User);}}}

3.2.2 UserManagementForm.Designer.cs

namespace UserManage.View
{partial class UserManagementForm{/// <summary>/// Required designer variable./// </summary>private System.ComponentModel.IContainer components = null;/// <summary>/// Clean up any resources being used./// </summary>/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>protected override void Dispose(bool disposing){if (disposing && (components != null)){components.Dispose();}base.Dispose(disposing);}#region Windows Form Designer generated code/// <summary>/// Required method for Designer support - do not modify/// the contents of this method with the code editor./// </summary>private void InitializeComponent(){System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle1 = new System.Windows.Forms.DataGridViewCellStyle();System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle2 = new System.Windows.Forms.DataGridViewCellStyle();System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle3 = new System.Windows.Forms.DataGridViewCellStyle();System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle4 = new System.Windows.Forms.DataGridViewCellStyle();System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle5 = new System.Windows.Forms.DataGridViewCellStyle();this.btn_Add = new Sunny.UI.UIButton();this.btn_Updata = new Sunny.UI.UIButton();this.btn_Del = new Sunny.UI.UIButton();this.dgv_User = new Sunny.UI.UIDataGridView();((System.ComponentModel.ISupportInitialize)(this.dgv_User)).BeginInit();this.SuspendLayout();// // btn_Add// this.btn_Add.Cursor = System.Windows.Forms.Cursors.Hand;this.btn_Add.Font = new System.Drawing.Font("宋体", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134)));this.btn_Add.Location = new System.Drawing.Point(59, 60);this.btn_Add.MinimumSize = new System.Drawing.Size(1, 1);this.btn_Add.Name = "btn_Add";this.btn_Add.Size = new System.Drawing.Size(149, 35);this.btn_Add.TabIndex = 0;this.btn_Add.Text = "添加用户";this.btn_Add.TipsFont = new System.Drawing.Font("宋体", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134)));this.btn_Add.Click += new System.EventHandler(this.btnAddUser_Click);// // btn_Updata// this.btn_Updata.Cursor = System.Windows.Forms.Cursors.Hand;this.btn_Updata.Font = new System.Drawing.Font("宋体", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134)));this.btn_Updata.Location = new System.Drawing.Point(233, 60);this.btn_Updata.MinimumSize = new System.Drawing.Size(1, 1);this.btn_Updata.Name = "btn_Updata";this.btn_Updata.Size = new System.Drawing.Size(149, 35);this.btn_Updata.TabIndex = 0;this.btn_Updata.Text = "修改用户";this.btn_Updata.TipsFont = new System.Drawing.Font("宋体", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134)));this.btn_Updata.Click += new System.EventHandler(this.btnModifyUser_Click);// // btn_Del// this.btn_Del.Cursor = System.Windows.Forms.Cursors.Hand;this.btn_Del.Font = new System.Drawing.Font("宋体", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134)));this.btn_Del.Location = new System.Drawing.Point(407, 60);this.btn_Del.MinimumSize = new System.Drawing.Size(1, 1);this.btn_Del.Name = "btn_Del";this.btn_Del.Size = new System.Drawing.Size(149, 35);this.btn_Del.TabIndex = 0;this.btn_Del.Text = "删除用户";this.btn_Del.TipsFont = new System.Drawing.Font("宋体", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134)));this.btn_Del.Click += new System.EventHandler(this.btnDeleteUser_Click);// // uiDataGridView1// dataGridViewCellStyle1.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(235)))), ((int)(((byte)(243)))), ((int)(((byte)(255)))));this.dgv_User.AlternatingRowsDefaultCellStyle = dataGridViewCellStyle1;this.dgv_User.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right)));this.dgv_User.BackgroundColor = System.Drawing.Color.White;this.dgv_User.ColumnHeadersBorderStyle = System.Windows.Forms.DataGridViewHeaderBorderStyle.Single;dataGridViewCellStyle2.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleCenter;dataGridViewCellStyle2.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(80)))), ((int)(((byte)(160)))), ((int)(((byte)(255)))));dataGridViewCellStyle2.Font = new System.Drawing.Font("宋体", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134)));dataGridViewCellStyle2.ForeColor = System.Drawing.Color.White;dataGridViewCellStyle2.SelectionBackColor = System.Drawing.SystemColors.Highlight;dataGridViewCellStyle2.SelectionForeColor = System.Drawing.SystemColors.HighlightText;dataGridViewCellStyle2.WrapMode = System.Windows.Forms.DataGridViewTriState.True;this.dgv_User.ColumnHeadersDefaultCellStyle = dataGridViewCellStyle2;this.dgv_User.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize;dataGridViewCellStyle3.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleLeft;dataGridViewCellStyle3.BackColor = System.Drawing.SystemColors.Window;dataGridViewCellStyle3.Font = new System.Drawing.Font("宋体", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134)));dataGridViewCellStyle3.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(48)))), ((int)(((byte)(48)))), ((int)(((byte)(48)))));dataGridViewCellStyle3.SelectionBackColor = System.Drawing.SystemColors.Highlight;dataGridViewCellStyle3.SelectionForeColor = System.Drawing.SystemColors.HighlightText;dataGridViewCellStyle3.WrapMode = System.Windows.Forms.DataGridViewTriState.False;this.dgv_User.DefaultCellStyle = dataGridViewCellStyle3;this.dgv_User.EnableHeadersVisualStyles = false;this.dgv_User.Font = new System.Drawing.Font("宋体", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134)));this.dgv_User.GridColor = System.Drawing.Color.FromArgb(((int)(((byte)(80)))), ((int)(((byte)(160)))), ((int)(((byte)(255)))));this.dgv_User.Location = new System.Drawing.Point(39, 122);this.dgv_User.Name = "dgv_User";dataGridViewCellStyle4.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleLeft;dataGridViewCellStyle4.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(235)))), ((int)(((byte)(243)))), ((int)(((byte)(255)))));dataGridViewCellStyle4.Font = new System.Drawing.Font("宋体", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134)));dataGridViewCellStyle4.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(48)))), ((int)(((byte)(48)))), ((int)(((byte)(48)))));dataGridViewCellStyle4.SelectionBackColor = System.Drawing.Color.FromArgb(((int)(((byte)(80)))), ((int)(((byte)(160)))), ((int)(((byte)(255)))));dataGridViewCellStyle4.SelectionForeColor = System.Drawing.Color.White;dataGridViewCellStyle4.WrapMode = System.Windows.Forms.DataGridViewTriState.True;this.dgv_User.RowHeadersDefaultCellStyle = dataGridViewCellStyle4;dataGridViewCellStyle5.BackColor = System.Drawing.Color.White;dataGridViewCellStyle5.Font = new System.Drawing.Font("宋体", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134)));this.dgv_User.RowsDefaultCellStyle = dataGridViewCellStyle5;this.dgv_User.RowTemplate.Height = 23;this.dgv_User.SelectedIndex = -1;this.dgv_User.Size = new System.Drawing.Size(901, 503);this.dgv_User.StripeOddColor = System.Drawing.Color.FromArgb(((int)(((byte)(235)))), ((int)(((byte)(243)))), ((int)(((byte)(255)))));this.dgv_User.TabIndex = 1;// // UserManagementForm// this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.None;this.ClientSize = new System.Drawing.Size(982, 658);this.Controls.Add(this.dgv_User);this.Controls.Add(this.btn_Del);this.Controls.Add(this.btn_Updata);this.Controls.Add(this.btn_Add);this.Name = "UserManagementForm";this.Text = "UserManagementForm__";this.ZoomScaleRect = new System.Drawing.Rectangle(15, 15, 800, 450);((System.ComponentModel.ISupportInitialize)(this.dgv_User)).EndInit();this.ResumeLayout(false);}#endregionprivate Sunny.UI.UIButton btn_Add;private Sunny.UI.UIButton btn_Updata;private Sunny.UI.UIButton btn_Del;private Sunny.UI.UIDataGridView dgv_User;}
}

3.3 UserUpdataForm.cs(添加、修改用户对话框)

using System;
using System.Windows.Forms;
using Sunny.UI;
using UserManage.Extensions;
using UserManage.Model;namespace UserManage.View
{public partial class UserUpdataForm : UIForm{private UILabel lblUsername;private UILabel lblUserType;private UILabel lblPassword;private UITextBox txtUsername;private UIComboBox comUserType;private UITextBox txtPassword;private UIButton btnEnter;public UserType UserType => comUserType.Text.GetUserTypeByText();public string UserName => txtUsername.Text;public string Password => txtPassword.Text;public event EventHandler EnterClicked;public UserUpdataForm(){InitializeComponent();InitializeUI();}private void InitializeUI(){// 设置窗体样式this.Size = new System.Drawing.Size(300, 280);this.StartPosition = FormStartPosition.CenterScreen;int locationY = 80;int yInterval = 20;// 用户名标签lblUsername = new UILabel();lblUsername.Location = new System.Drawing.Point(30, locationY);lblUsername.Size = new System.Drawing.Size(80, 25);lblUsername.Text = "用户名:";this.Controls.Add(lblUsername);// 用户名文本框txtUsername = new UITextBox();txtUsername.Location = new System.Drawing.Point(120, locationY);txtUsername.Size = new System.Drawing.Size(150, 25);this.Controls.Add(txtUsername);// 用户类型locationY += yInterval + txtUsername.Height;lblUserType = new UILabel();lblUserType.Location = new System.Drawing.Point(30, locationY);lblUserType.Size = new System.Drawing.Size(80, 25);lblUserType.Text = "用户类型:";this.Controls.Add(lblUserType);// 用户类型下拉框comUserType = new UIComboBox();comUserType.Location = new System.Drawing.Point(120, locationY);comUserType.Size = new System.Drawing.Size(150, 25);this.Controls.Add(comUserType);// 密码标签locationY += yInterval + comUserType.Height;lblPassword = new UILabel();lblPassword.Location = new System.Drawing.Point(30, locationY);lblPassword.Size = new System.Drawing.Size(80, 25);lblPassword.Text = "密码:";this.Controls.Add(lblPassword);// 密码文本框txtPassword = new UITextBox();txtPassword.Location = new System.Drawing.Point(120, locationY);txtPassword.Size = new System.Drawing.Size(150, 25);this.Controls.Add(txtPassword);// 登录按钮locationY += yInterval + txtPassword.Height;btnEnter = new UIButton();btnEnter.Size = new System.Drawing.Size(100, 30);int btnLoginX = txtPassword.Location.X + txtPassword.Width - btnEnter.Width;btnEnter.Location = new System.Drawing.Point(btnLoginX, locationY);btnEnter.Click += BtnEnterClick;this.Controls.Add(btnEnter);}private void BtnEnterClick(object sender, EventArgs e){EnterClicked?.Invoke(this, EventArgs.Empty);}public void AddUserType(params UserType[] userTypes){if (comUserType != null){foreach (var userType in userTypes){comUserType.Items.Add(userType.GetUserTypeText());}}}public void RefreshAddUserInfo(){txtUsername.Text = string.Empty;txtPassword.Text = string.Empty;comUserType.Text = string.Empty;this.Text = "添加用户";btnEnter.Text = "添加";txtUsername.ReadOnly = false;}public void RefreshUdpataUserInfo(User user){comUserType.Text = user.UserType.GetUserTypeText();txtUsername.Text = user.Username;txtPassword.Text = user.Password;txtUsername.ReadOnly = true;this.Text = "修改用户";btnEnter.Text = "确认";}}
}

4 扩展类

using Sunny.UI;
using UserManage.Model;namespace UserManage.Extensions
{public static class UserManageExtension{public static string GetUserTypeText(this UserType type){switch (type){case UserType.Developer:return "开发者";case UserType.Admin:return "管理员";case UserType.NormalUser:return "普通用户";default:return "未登录";}}public static UserType GetUserTypeByText(this string text){if (text.Equals(UserType.Developer.GetUserTypeText()))return UserType.Developer;else if (text.Equals(UserType.Admin.GetUserTypeText()))return UserType.Admin;else if (text.Equals(UserType.NormalUser.GetUserTypeText()))return UserType.NormalUser;elsereturn UserType.Null;}public static void ShowMsg(string text, string title = "提示"){UIMessageBox.ShowError(text);}public static bool Confirm(string text){return UIMessageBox.ShowAsk(text);}}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/80273.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

自然语言处理(NLP)技术的实例

自然语言处理&#xff08;NLP&#xff09;技术在各个领域都有广泛的应用&#xff0c;以下是几个例子&#xff1a; 语音识别&#xff1a;通过NLP技术&#xff0c;计算机可以识别和理解语音指令&#xff0c;例如智能助手如Siri和Alexa就是通过语音识别技术实现与用户的交互。 机…

Spring Boot实战(三十六)编写单元测试

目录 一、什么是单元测试&#xff1f;二、Spring Boot 中的单元测试依赖三、举例 Spring Boot 中不同层次的单元测试3.1 Service层3.2 Controller 层3.3 Repository层 四、Spring Boot 中 Mock、Spy 对象的使用4.1 使用Mock对象的背景4.2 什么是Mock对象&#xff0c;有哪些好处…

aws服务(四)文件存储服务S3 介绍使用代码集成

一、介绍 1、简介 Amazon S3 是 Amazon Web Services 提供的一种对象存储服务(Object Storage),用于在云中存储和检索任意数量的数据。它以高可用性、高扩展性和高持久性著称,非常适合用来存储网站资源、数据备份、日志文件、大数据、机器学习输入输出等。 2、主要特性 …

应用信息1.13.0发布

增加工具箱 增加启动器功能 增加布局查看器 增加手动安装和卸载应用 增加APK文件解析 增加应用多选功能 增加查看应用预装版本 增加应用信息和ADB命令导出 修复其它问题... 百度下载&#xff1a;百度网盘 请输入提取码 提取码&#xff1a;1234

【Vue3 实战】插槽封装与懒加载

一、为什么需要插槽&#xff1f;从一个面板组件说起 在电商首页开发中&#xff0c;经常遇到这样的场景&#xff1a; 「新鲜好物」「人气推荐」同样类型模块都需要相同的标题栏&#xff0c;但内容区布局不同 这时候&#xff0c;插槽&#xff08;Slot&#xff09;就像一个「内容…

虚无隧穿产生宇宙(true nothing tunneling) 是谁提出的

是 亚历克斯.维连金 英文名&#xff08;alex vilenkin 或者 Alexander Vilenkin)提出来的。 “虚无隧穿产生宇宙”&#xff08;true nothing tunneling&#xff09;这一概念并非一个标准的物理学术语&#xff0c;它更像是对某些现代宇宙学理论的描述&#xff0c;尤其是涉及宇宙…

postgis:添加索引时提示“对访问方法 gist 数据类型 geometry 没有默认的操作符表“

问题 在对gis表的geom字段创建空间索引时&#xff0c;出现“对访问方法 "gist" 数据类型 geometry 没有默认的操作符表”的提示报错。 解决方案 按系列步骤进行排查并解决。 1.先确认已安装postgis -- 查看postgis版本 SELECT postgis_full_version() 若安装了则…

图论---Prim堆优化(稀疏图)

题目通常会提示数据范围&#xff1a; 若 V ≤ 500&#xff0c;两种方法均可&#xff08;朴素Prim更稳&#xff09;。 若 V ≤ 1e5&#xff0c;必须用优先队列Prim vector 存图。 #include <iostream> #include <vector> #include <queue> #include <…

代码随想录算法训练营第一天:数组part1

今日学习的文章链接和视频链接 ● 自己看到题目的第一想法 ● 看完代码随想录之后的想法 ● 自己实现过程中遇到哪些困难 ● 今日收获&#xff0c;记录一下自己的学习时长 状态 思路理解完成 30% 代码debug完成 60% 代码模板总结并抽象出来 100% 题目 704 二分查找 题目链接…

企业为何要求禁用缺省口令?安全风险及应对措施分析

在当今数字化时代&#xff0c;企业网络安全面临着前所未有的挑战。缺省口令的使用是网络安全中的一个重要隐患&#xff0c;许多企业在制定网络安全红线时&#xff0c;明确要求禁用缺省口令。本文将探讨这一要求的原因及其对企业安全的重要性。 引言&#xff1a;一个真实的入侵场…

PostgreSQL 中的权限视图

PostgreSQL 中的权限视图 PostgreSQL 提供了多个系统视图来查询权限信息&#xff0c;虽然不像 Oracle 的 DBA_SYS_PRIVS 那样集中在一个视图中&#xff0c;但可以通过组合以下视图获取完整的系统权限信息。 一 主要权限相关视图 Oracle 视图PostgreSQL 对应视图描述DBA_SYS_…

【防火墙 pfsense】1简介

&#xff08;1&#xff09; pfSense 有以下可能的用途&#xff1a; 边界防火墙 路由器 交换机 无线路由器 / 无线接入点 &#xff08;2&#xff09;边界防火墙 ->要充当边界防火墙&#xff0c;pfSense 系统至少需要两个接口&#xff1a;一个广域网&#xff08;WAN&#xff0…

数据库+Docker+SSH三合一!深度评测HexHub的全栈开发体验

作为一名技术博主&#xff0c;我最近一直被各种开发工具切换搞得焦头烂额。数据库要用Navicat&#xff0c;服务器管理得开Termius&#xff0c;Docker操作还得切到命令行&#xff0c;每天光在不同工具间切换就浪费了大量时间。直到团队里的一位架构师向我推荐了HexHub这个一体化…

第十天 Shader编程:编写简单表面着色器 Addressable资源管理系统 DOTS(面向数据技术栈)入门

前言 作为Unity初学者&#xff0c;在实现复杂场景时经常会遇到性能瓶颈。本文将带你通过四个关键技术的实战学习&#xff0c;掌握现代Unity开发的核心优化方案&#xff1a; Shader编程 - 编写表面着色器控制物体渲染Addressable系统 - 实现高效资源管理DOTS技术栈 - 解锁百万…

项目自动化测试

一.设计测试用例(细致全面) 二.先引入所需要的pom.xml依赖 1.selenium依赖 2.webdrivermanager依赖 3.commons-io依赖 编写测试用例–按照页面对用例进行划分,每个页面是Java文件,页面下的所有用例统一管理 三.common包(放入公用包) 类1utils 可以调用driver对象,访问url …

ap无法上线问题定位(交换机发包没有剥掉pvid tag)

一中学&#xff0c;新开的40台appoe交换机核心交换机旁挂ac出口路由的组网&#xff0c;反馈ap无法上线&#xff0c;让协助解决。 组网如下&#xff1a; 排查过程&#xff1a; 检查ac的配置&#xff0c;没有发现问题 发现配置没有问题&#xff0c;vlan1000配置子接口&#xff…

第十七届山东省职业院校技能大赛 中职组网络建设与运维赛项

第十七届山东省职业院校技能大赛 中职组网络建设与运维赛项 赛题 B 卷 第十七届山东省职业院校技能大赛中职组网络建设与运维赛项 1 赛题说明 一、竞赛项目简介 “网络建设与运维”竞赛共分为以下三个模块&#xff1a;  网络理论测试&#xff1b;  网络建设与调试&#xf…

关于QT信号、槽、槽函数的讲解

也是好久没有发帖子了&#xff0c;最近博主主要还是在边学QT边完成任务&#xff0c;所以进度很慢&#xff0c;但确实在这几天对于QT自身槽和信号这类特殊的机制有了一定简单的理解&#xff0c;所以还是想记录下来&#xff0c;如果有初学者看到帖子对他有一定的帮助&#xff0c;…

YOLOv8 涨点新方案:SlideLoss FocalLoss 优化,小目标检测效果炸裂!

YOLOv8优化秘籍&#xff1a;用SlideLoss和FocalLoss提升小目标检测精度&#xff08;附代码实战&#xff09;​​ ​&#x1f4cc; 核心问题&#xff1a;YOLOv8在检测小物体时效果不够好&#xff1f;​​ YOLOv8虽然是强大的目标检测模型&#xff0c;但在处理小物体或类别不平…

基于cubeMX的hal库STM32实现MQ2烟雾浓度检测

一、任务目标 使用STM32F103C8T6单片机&#xff0c;使用单片机AD模块采集MQ2烟雾传感器的数据&#xff0c;在OLED屏显示检测到的AD值、电压值和浓度值&#xff08;ppm单位&#xff09;。 二、实现过程 1、MQ2烟雾传感器的浓度转化方法 &#xff08;1&#xff09;实验所用的M…