Add the code below to your main form file. This version uses parameterized queries for safety, loads all products into the DataGridView, validates input, and supports the core inventory workflow.
using Microsoft.Data.SqlClient;
using System;
using System.Data;
using System.Globalization;
using System.IO;
using System.Text;
using System.Windows.Forms;
namespace InventoryManagementSystem
{
public partial class Form1 : Form
{
private readonly string connectionString =
@"Server=.\SQLEXPRESS;Database=InventoryDB;Trusted_Connection=True;TrustServerCertificate=True;";
public Form1()
{
InitializeComponent();
Load += Form1_Load;
}
private void Form1_Load(object sender, EventArgs e)
{
cmbCategory.Items.AddRange(new string[]
{
"Accessories",
"Displays",
"Storage",
"Office",
"Networking",
"Components"
});
dgvProducts.SelectionMode = DataGridViewSelectionMode.FullRowSelect;
dgvProducts.MultiSelect = false;
dgvProducts.ReadOnly = true;
dgvProducts.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.Fill;
LoadProducts();
ClearFields();
}
private void LoadProducts(string keyword = "")
{
using SqlConnection conn = new SqlConnection(connectionString);
using SqlCommand cmd = new SqlCommand();
cmd.Connection = conn;
if (string.IsNullOrWhiteSpace(keyword))
{
cmd.CommandText = @"SELECT ProductID, ProductName, Category, UnitPrice, Quantity, ReorderLevel,
(UnitPrice * Quantity) AS StockValue
FROM Products
ORDER BY ProductName";
}
else
{
cmd.CommandText = @"SELECT ProductID, ProductName, Category, UnitPrice, Quantity, ReorderLevel,
(UnitPrice * Quantity) AS StockValue
FROM Products
WHERE ProductName LIKE @keyword OR Category LIKE @keyword
ORDER BY ProductName";
cmd.Parameters.AddWithValue("@keyword", "%" + keyword + "%");
}
DataTable table = new DataTable();
using SqlDataAdapter adapter = new SqlDataAdapter(cmd);
adapter.Fill(table);
dgvProducts.DataSource = table;
HighlightLowStockRows();
lblStatus.Text = table.Rows.Count + " product(s) loaded.";
}
private void HighlightLowStockRows()
{
foreach (DataGridViewRow row in dgvProducts.Rows)
{
if (row.Cells["Quantity"].Value != null && row.Cells["ReorderLevel"].Value != null)
{
int quantity = Convert.ToInt32(row.Cells["Quantity"].Value);
int reorderLevel = Convert.ToInt32(row.Cells["ReorderLevel"].Value);
if (quantity <= reorderLevel)
{
row.DefaultCellStyle.BackColor = System.Drawing.Color.MistyRose;
}
else
{
row.DefaultCellStyle.BackColor = System.Drawing.Color.White;
}
}
}
}
private bool ValidateInputs(out decimal unitPrice, out int quantity, out int reorderLevel)
{
unitPrice = 0;
quantity = 0;
reorderLevel = 0;
if (string.IsNullOrWhiteSpace(txtProductName.Text))
{
MessageBox.Show("Please enter a product name.");
txtProductName.Focus();
return false;
}
if (cmbCategory.SelectedIndex == -1)
{
MessageBox.Show("Please select a category.");
cmbCategory.Focus();
return false;
}
if (!decimal.TryParse(txtUnitPrice.Text, NumberStyles.Number, CultureInfo.InvariantCulture, out unitPrice))
{
MessageBox.Show("Please enter a valid unit price.");
txtUnitPrice.Focus();
return false;
}
if (!int.TryParse(txtQuantity.Text, out quantity))
{
MessageBox.Show("Please enter a valid quantity.");
txtQuantity.Focus();
return false;
}
if (!int.TryParse(txtReorderLevel.Text, out reorderLevel))
{
MessageBox.Show("Please enter a valid reorder level.");
txtReorderLevel.Focus();
return false;
}
if (unitPrice < 0 || quantity < 0 || reorderLevel < 0)
{
MessageBox.Show("Numeric values cannot be negative.");
return false;
}
return true;
}
private void ClearFields()
{
txtProductID.Clear();
txtProductName.Clear();
cmbCategory.SelectedIndex = -1;
txtUnitPrice.Clear();
txtQuantity.Clear();
txtReorderLevel.Text = "5";
txtSearch.Clear();
txtProductName.Focus();
}
private void btnAdd_Click(object sender, EventArgs e)
{
if (!ValidateInputs(out decimal unitPrice, out int quantity, out int reorderLevel))
return;
using SqlConnection conn = new SqlConnection(connectionString);
using SqlCommand cmd = new SqlCommand(
@"INSERT INTO Products (ProductName, Category, UnitPrice, Quantity, ReorderLevel)
VALUES (@ProductName, @Category, @UnitPrice, @Quantity, @ReorderLevel)", conn);
cmd.Parameters.AddWithValue("@ProductName", txtProductName.Text.Trim());
cmd.Parameters.AddWithValue("@Category", cmbCategory.Text);
cmd.Parameters.AddWithValue("@UnitPrice", unitPrice);
cmd.Parameters.AddWithValue("@Quantity", quantity);
cmd.Parameters.AddWithValue("@ReorderLevel", reorderLevel);
conn.Open();
cmd.ExecuteNonQuery();
LoadProducts();
ClearFields();
lblStatus.Text = "Product added successfully.";
}
private void btnUpdate_Click(object sender, EventArgs e)
{
if (string.IsNullOrWhiteSpace(txtProductID.Text))
{
MessageBox.Show("Please select a product to update.");
return;
}
if (!ValidateInputs(out decimal unitPrice, out int quantity, out int reorderLevel))
return;
using SqlConnection conn = new SqlConnection(connectionString);
using SqlCommand cmd = new SqlCommand(
@"UPDATE Products
SET ProductName = @ProductName,
Category = @Category,
UnitPrice = @UnitPrice,
Quantity = @Quantity,
ReorderLevel = @ReorderLevel
WHERE ProductID = @ProductID", conn);
cmd.Parameters.AddWithValue("@ProductID", Convert.ToInt32(txtProductID.Text));
cmd.Parameters.AddWithValue("@ProductName", txtProductName.Text.Trim());
cmd.Parameters.AddWithValue("@Category", cmbCategory.Text);
cmd.Parameters.AddWithValue("@UnitPrice", unitPrice);
cmd.Parameters.AddWithValue("@Quantity", quantity);
cmd.Parameters.AddWithValue("@ReorderLevel", reorderLevel);
conn.Open();
cmd.ExecuteNonQuery();
LoadProducts();
ClearFields();
lblStatus.Text = "Product updated successfully.";
}
private void btnDelete_Click(object sender, EventArgs e)
{
if (string.IsNullOrWhiteSpace(txtProductID.Text))
{
MessageBox.Show("Please select a product to delete.");
return;
}
DialogResult confirm = MessageBox.Show(
"Delete this product?",
"Confirm Delete",
MessageBoxButtons.YesNo,
MessageBoxIcon.Warning);
if (confirm != DialogResult.Yes)
return;
using SqlConnection conn = new SqlConnection(connectionString);
using SqlCommand cmd = new SqlCommand(
"DELETE FROM Products WHERE ProductID = @ProductID", conn);
cmd.Parameters.AddWithValue("@ProductID", Convert.ToInt32(txtProductID.Text));
conn.Open();
cmd.ExecuteNonQuery();
LoadProducts();
ClearFields();
lblStatus.Text = "Product deleted successfully.";
}
private void btnSearch_Click(object sender, EventArgs e)
{
LoadProducts(txtSearch.Text.Trim());
}
private void btnClear_Click(object sender, EventArgs e)
{
ClearFields();
LoadProducts();
lblStatus.Text = "Form cleared.";
}
private void dgvProducts_CellClick(object sender, DataGridViewCellEventArgs e)
{
if (e.RowIndex < 0) return;
DataGridViewRow row = dgvProducts.Rows[e.RowIndex];
txtProductID.Text = row.Cells["ProductID"].Value?.ToString();
txtProductName.Text = row.Cells["ProductName"].Value?.ToString();
cmbCategory.Text = row.Cells["Category"].Value?.ToString();
txtUnitPrice.Text = row.Cells["UnitPrice"].Value?.ToString();
txtQuantity.Text = row.Cells["Quantity"].Value?.ToString();
txtReorderLevel.Text = row.Cells["ReorderLevel"].Value?.ToString();
}
private void btnExportCsv_Click(object sender, EventArgs e)
{
if (dgvProducts.Rows.Count == 0)
{
MessageBox.Show("There is no data to export.");
return;
}
using SaveFileDialog dialog = new SaveFileDialog();
dialog.Filter = "CSV files (*.csv)|*.csv";
dialog.FileName = "inventory-report.csv";
if (dialog.ShowDialog() != DialogResult.OK)
return;
StringBuilder sb = new StringBuilder();
for (int i = 0; i < dgvProducts.Columns.Count; i++)
{
sb.Append(dgvProducts.Columns[i].HeaderText);
if (i < dgvProducts.Columns.Count - 1)
sb.Append(",");
}
sb.AppendLine();
foreach (DataGridViewRow row in dgvProducts.Rows)
{
if (row.IsNewRow) continue;
for (int i = 0; i < dgvProducts.Columns.Count; i++)
{
sb.Append(row.Cells[i].Value?.ToString());
if (i < dgvProducts.Columns.Count - 1)
sb.Append(",");
}
sb.AppendLine();
}
File.WriteAllText(dialog.FileName, sb.ToString());
lblStatus.Text = "CSV exported successfully.";
MessageBox.Show("Export completed.");
}
}
}
This example gives you a complete working flow. After placing the controls on the form, make sure each button click event and the DataGridView cell click event are connected to the matching methods.