10 Commits

Author SHA1 Message Date
e511610052 Initial commit 2026-01-23 12:09:56 +01:00
Frozd
d17e1183f9 feat: keybinds 2025-11-28 13:08:18 +01:00
Frozd
bb71806a22 fix: better Image loading 2025-11-28 13:00:32 +01:00
Frozd
e89fc79ebb fix: more performant loading 2025-11-28 12:53:29 +01:00
Frozd
5b56d76cd7 fix: ordering 2025-11-28 12:41:45 +01:00
Frozd
0c8b10cfe6 fix: ordering with search v0 2025-11-28 12:37:22 +01:00
Frozd
d4f3ac8776 fix: search 2025-11-28 12:22:56 +01:00
Frozd
746f8c4039 fix: Update instead of re-fill 2025-11-28 12:09:07 +01:00
Frozd
cca7c37ffe chore: async loading 2025-11-28 10:46:06 +01:00
Frozd
306994542e fix: async car update 2025-11-28 10:35:59 +01:00
19 changed files with 11328 additions and 8542 deletions

View File

@@ -4,7 +4,7 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace CarManagerV2
namespace CarManagerV3
{
public class Car
{
@@ -15,8 +15,9 @@ namespace CarManagerV2
string color;
int mileage;
decimal price;
int order;
public Car(int id, string make, string model, int year, string color, int mileage, decimal price)
public Car(int id, string make, string model, int year, string color, int mileage, decimal price, int order = 0)
{
this.id = id;
this.make = make;
@@ -25,6 +26,7 @@ namespace CarManagerV2
this.color = color;
this.mileage = mileage;
this.price = price;
this.order = order;
}
public int Id { get { return id; } set { id = value; } }
@@ -34,6 +36,7 @@ namespace CarManagerV2
public string Color { get { return color; } set { color = value; } }
public int Mileage { get { return mileage; } set { mileage = value; } }
public decimal Price { get { return price; } set { price = value; } }
public int Order { get { return order; } set { order = value; } }
public override string ToString()
@@ -54,7 +57,12 @@ namespace CarManagerV2
public bool IsChanged(Car other)
{
return make != other.make || model != other.model || year != other.year || color != other.color || mileage != other.mileage || price != other.price;
return make != other.make || model != other.model || year != other.year || color != other.color || mileage != other.mileage || price != other.price || other.color != color;
}
public Car Clone()
{
return new Car(id, make, model, year, color, mileage, price);
}
}
}

View File

@@ -1,4 +1,4 @@
namespace CarManagerV2
namespace CarManagerV3
{
partial class CarCard
{
@@ -82,6 +82,7 @@
this.pbxCar.Dock = System.Windows.Forms.DockStyle.Fill;
this.pbxCar.Image = ((System.Drawing.Image)(resources.GetObject("pbxCar.Image")));
this.pbxCar.ImageLocation = "";
this.pbxCar.InitialImage = ((System.Drawing.Image)(resources.GetObject("pbxCar.InitialImage")));
this.pbxCar.Location = new System.Drawing.Point(3, 3);
this.pbxCar.Name = "pbxCar";
this.pbxCar.Size = new System.Drawing.Size(204, 124);

View File

@@ -8,10 +8,12 @@ using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace CarManagerV2
namespace CarManagerV3
{
public partial class CarCard : UserControl
{
public Car Car;
public string CarName
{
get { return lblCarName.Text; }
@@ -44,6 +46,19 @@ namespace CarManagerV2
this.Click += (s, e) => this.OnCardClicked();
}
public async void LoadImage()
{
this.CarImage = pbxCar.InitialImage; // Set to loading image
await Task.Run(() =>
{
Image img = ImageManager.GetImage(this.Car);
if (img != null)
{
this.CarImage = img;
}
});
}
private void ForwardClick(object sender, EventArgs e)
{
// Raise your CardClicked event no matter what got clicked
@@ -58,5 +73,16 @@ namespace CarManagerV2
this.CardClicked(this, EventArgs.Empty);
}
}
public void ClearCardClickedHandlers()
{
if (this.CardClicked != null)
{
foreach (Delegate d in this.CardClicked.GetInvocationList())
{
this.CardClicked -= (EventHandler)d;
}
}
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,4 +1,4 @@
namespace CarManagerV2
namespace CarManagerV3
{
partial class CarDetailsForm
{
@@ -118,7 +118,7 @@
this.tbxMake.Location = new System.Drawing.Point(68, 203);
this.tbxMake.Name = "tbxMake";
this.tbxMake.Size = new System.Drawing.Size(473, 22);
this.tbxMake.TabIndex = 2;
this.tbxMake.TabIndex = 1;
this.tbxMake.TextChanged += new System.EventHandler(this.tbxMake_TextChanged);
//
// label2
@@ -136,7 +136,7 @@
this.tbxModel.Location = new System.Drawing.Point(68, 233);
this.tbxModel.Name = "tbxModel";
this.tbxModel.Size = new System.Drawing.Size(473, 22);
this.tbxModel.TabIndex = 4;
this.tbxModel.TabIndex = 2;
this.tbxModel.TextChanged += new System.EventHandler(this.tbxModel_TextChanged);
//
// label3
@@ -194,7 +194,7 @@
this.btnSave.Location = new System.Drawing.Point(450, 8);
this.btnSave.Name = "btnSave";
this.btnSave.Size = new System.Drawing.Size(75, 23);
this.btnSave.TabIndex = 0;
this.btnSave.TabIndex = 7;
this.btnSave.Text = "Save";
this.btnSave.UseVisualStyleBackColor = true;
this.btnSave.Click += new System.EventHandler(this.btnSave_Click);
@@ -204,7 +204,7 @@
this.btnDelete.Location = new System.Drawing.Point(369, 8);
this.btnDelete.Name = "btnDelete";
this.btnDelete.Size = new System.Drawing.Size(75, 23);
this.btnDelete.TabIndex = 1;
this.btnDelete.TabIndex = 8;
this.btnDelete.Text = "Delete";
this.btnDelete.UseVisualStyleBackColor = true;
this.btnDelete.Click += new System.EventHandler(this.btnDelete_Click);
@@ -228,7 +228,7 @@
0});
this.nudYear.Name = "nudYear";
this.nudYear.Size = new System.Drawing.Size(120, 22);
this.nudYear.TabIndex = 10;
this.nudYear.TabIndex = 3;
this.nudYear.ValueChanged += new System.EventHandler(this.nudYear_ValueChanged);
//
// tbxColor
@@ -237,7 +237,7 @@
this.tbxColor.Location = new System.Drawing.Point(68, 293);
this.tbxColor.Name = "tbxColor";
this.tbxColor.Size = new System.Drawing.Size(473, 22);
this.tbxColor.TabIndex = 11;
this.tbxColor.TabIndex = 4;
this.tbxColor.TextChanged += new System.EventHandler(this.tbxColor_TextChanged);
//
// nudMileage
@@ -250,7 +250,7 @@
0});
this.nudMileage.Name = "nudMileage";
this.nudMileage.Size = new System.Drawing.Size(120, 22);
this.nudMileage.TabIndex = 12;
this.nudMileage.TabIndex = 5;
this.nudMileage.ThousandsSeparator = true;
this.nudMileage.ValueChanged += new System.EventHandler(this.nudMileage_ValueChanged);
//
@@ -265,7 +265,7 @@
0});
this.nudPrice.Name = "nudPrice";
this.nudPrice.Size = new System.Drawing.Size(120, 22);
this.nudPrice.TabIndex = 13;
this.nudPrice.TabIndex = 6;
this.nudPrice.ThousandsSeparator = true;
this.nudPrice.ValueChanged += new System.EventHandler(this.nudPrice_ValueChanged);
//

View File

@@ -8,7 +8,7 @@ using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace CarManagerV2
namespace CarManagerV3
{
public partial class CarDetailsForm : Form
{
@@ -59,17 +59,26 @@ namespace CarManagerV2
car.Price = nudPrice.Value;
}
private void btnSave_Click(object sender, EventArgs e)
private async void btnSave_Click(object sender, EventArgs e)
{
// save car to cars.csv and close form
//saving car... popuo
btnSave.Enabled = false;
btnDelete.Enabled = false;
var msgbox = new PleaseWait();
msgbox.Show();
Application.DoEvents();
StateManager.UpdateCar(car);
Image fooimg = ImageManager.GetImage(car);
msgbox.Close();
await Task.Run(() =>
{
StateManager.UpdateCar(car);
Console.WriteLine("Saved car: " + car.Id);
});
Console.WriteLine("Car saved. " + car.Id);
btnSave.Enabled = true;
btnDelete.Enabled = true;
this.Close();
msgbox.Close();
}
@@ -85,5 +94,24 @@ namespace CarManagerV2
}
}
protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
{
if (keyData == Keys.Enter)
{
btnSave.PerformClick();
return true; // Indicate that the key has been handled
}
if (keyData == Keys.Escape)
{
this.Close();
return true; // Indicate that the key has been handled
}
if (keyData == Keys.Delete)
{
btnDelete.PerformClick();
return true; // Indicate that the key has been handled
}
return base.ProcessCmdKey(ref msg, keyData);
}
}
}

View File

@@ -5,7 +5,7 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace CarManagerV2
namespace CarManagerV3
{
internal class ImageManager
{

View File

@@ -1,4 +1,4 @@
namespace CarManagerV2
namespace CarManagerV3
{
partial class MainForm
{
@@ -34,33 +34,45 @@
this.tableLayoutPanel2 = new System.Windows.Forms.TableLayoutPanel();
this.tbxSearch = new System.Windows.Forms.TextBox();
this.btnNewCar = new System.Windows.Forms.Button();
this.menuStrip1 = new System.Windows.Forms.MenuStrip();
this.fileToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.openToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.saveToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.saveAsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.importToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.recentFilesToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.revealInFileExplorerToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.tableLayoutPanel1.SuspendLayout();
this.tableLayoutPanel2.SuspendLayout();
this.menuStrip1.SuspendLayout();
this.SuspendLayout();
//
// tableLayoutPanel1
//
this.tableLayoutPanel1.ColumnCount = 1;
this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F));
this.tableLayoutPanel1.Controls.Add(this.flpCars, 0, 1);
this.tableLayoutPanel1.Controls.Add(this.tableLayoutPanel2, 0, 0);
this.tableLayoutPanel1.Controls.Add(this.flpCars, 0, 2);
this.tableLayoutPanel1.Controls.Add(this.tableLayoutPanel2, 0, 1);
this.tableLayoutPanel1.Controls.Add(this.menuStrip1, 0, 0);
this.tableLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill;
this.tableLayoutPanel1.Location = new System.Drawing.Point(0, 0);
this.tableLayoutPanel1.Name = "tableLayoutPanel1";
this.tableLayoutPanel1.RowCount = 2;
this.tableLayoutPanel1.RowCount = 3;
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle());
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 40F));
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle());
this.tableLayoutPanel1.Size = new System.Drawing.Size(800, 450);
this.tableLayoutPanel1.Size = new System.Drawing.Size(802, 458);
this.tableLayoutPanel1.TabIndex = 0;
this.tableLayoutPanel1.Paint += new System.Windows.Forms.PaintEventHandler(this.tableLayoutPanel1_Paint);
//
// flpCars
//
this.flpCars.AutoScroll = true;
this.flpCars.AutoScrollMargin = new System.Drawing.Size(0, 200);
this.flpCars.Dock = System.Windows.Forms.DockStyle.Fill;
this.flpCars.Location = new System.Drawing.Point(3, 43);
this.flpCars.Location = new System.Drawing.Point(3, 67);
this.flpCars.Name = "flpCars";
this.flpCars.Size = new System.Drawing.Size(794, 404);
this.flpCars.Size = new System.Drawing.Size(796, 412);
this.flpCars.TabIndex = 1;
//
// tableLayoutPanel2
@@ -71,12 +83,12 @@
this.tableLayoutPanel2.Controls.Add(this.tbxSearch, 0, 0);
this.tableLayoutPanel2.Controls.Add(this.btnNewCar, 1, 0);
this.tableLayoutPanel2.Dock = System.Windows.Forms.DockStyle.Fill;
this.tableLayoutPanel2.Location = new System.Drawing.Point(3, 3);
this.tableLayoutPanel2.Location = new System.Drawing.Point(3, 27);
this.tableLayoutPanel2.Name = "tableLayoutPanel2";
this.tableLayoutPanel2.RowCount = 1;
this.tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F));
this.tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 34F));
this.tableLayoutPanel2.Size = new System.Drawing.Size(794, 34);
this.tableLayoutPanel2.Size = new System.Drawing.Size(796, 34);
this.tableLayoutPanel2.TabIndex = 2;
//
// tbxSearch
@@ -84,13 +96,13 @@
this.tbxSearch.Dock = System.Windows.Forms.DockStyle.Fill;
this.tbxSearch.Location = new System.Drawing.Point(3, 3);
this.tbxSearch.Name = "tbxSearch";
this.tbxSearch.Size = new System.Drawing.Size(391, 22);
this.tbxSearch.Size = new System.Drawing.Size(392, 22);
this.tbxSearch.TabIndex = 3;
this.tbxSearch.TextChanged += new System.EventHandler(this.tbxSearch_TextChanged);
//
// btnNewCar
//
this.btnNewCar.Location = new System.Drawing.Point(400, 3);
this.btnNewCar.Location = new System.Drawing.Point(401, 3);
this.btnNewCar.Name = "btnNewCar";
this.btnNewCar.Size = new System.Drawing.Size(75, 23);
this.btnNewCar.TabIndex = 4;
@@ -98,19 +110,88 @@
this.btnNewCar.UseVisualStyleBackColor = true;
this.btnNewCar.Click += new System.EventHandler(this.btnNewCar_Click);
//
// menuStrip1
//
this.menuStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.fileToolStripMenuItem});
this.menuStrip1.Location = new System.Drawing.Point(0, 0);
this.menuStrip1.Name = "menuStrip1";
this.menuStrip1.Size = new System.Drawing.Size(802, 24);
this.menuStrip1.TabIndex = 3;
this.menuStrip1.Text = "menuStrip1";
//
// fileToolStripMenuItem
//
this.fileToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.openToolStripMenuItem,
this.saveToolStripMenuItem,
this.saveAsToolStripMenuItem,
this.importToolStripMenuItem,
this.recentFilesToolStripMenuItem,
this.revealInFileExplorerToolStripMenuItem});
this.fileToolStripMenuItem.Name = "fileToolStripMenuItem";
this.fileToolStripMenuItem.Size = new System.Drawing.Size(37, 20);
this.fileToolStripMenuItem.Text = "File";
//
// openToolStripMenuItem
//
this.openToolStripMenuItem.Name = "openToolStripMenuItem";
this.openToolStripMenuItem.Size = new System.Drawing.Size(187, 22);
this.openToolStripMenuItem.Text = "Open";
this.openToolStripMenuItem.Click += new System.EventHandler(this.openToolStripMenuItem_Click);
//
// saveToolStripMenuItem
//
this.saveToolStripMenuItem.Name = "saveToolStripMenuItem";
this.saveToolStripMenuItem.Size = new System.Drawing.Size(187, 22);
this.saveToolStripMenuItem.Text = "Save";
this.saveToolStripMenuItem.Click += new System.EventHandler(this.saveToolStripMenuItem_Click);
//
// saveAsToolStripMenuItem
//
this.saveAsToolStripMenuItem.Name = "saveAsToolStripMenuItem";
this.saveAsToolStripMenuItem.Size = new System.Drawing.Size(187, 22);
this.saveAsToolStripMenuItem.Text = "Save as";
this.saveAsToolStripMenuItem.Click += new System.EventHandler(this.saveAsToolStripMenuItem_Click);
//
// importToolStripMenuItem
//
this.importToolStripMenuItem.Name = "importToolStripMenuItem";
this.importToolStripMenuItem.Size = new System.Drawing.Size(187, 22);
this.importToolStripMenuItem.Text = "Import";
this.importToolStripMenuItem.Click += new System.EventHandler(this.importToolStripMenuItem_Click);
//
// recentFilesToolStripMenuItem
//
this.recentFilesToolStripMenuItem.Name = "recentFilesToolStripMenuItem";
this.recentFilesToolStripMenuItem.Size = new System.Drawing.Size(187, 22);
this.recentFilesToolStripMenuItem.Text = "Recent Files";
this.recentFilesToolStripMenuItem.Click += new System.EventHandler(this.recentFilesToolStripMenuItem_Click);
//
// revealInFileExplorerToolStripMenuItem
//
this.revealInFileExplorerToolStripMenuItem.Name = "revealInFileExplorerToolStripMenuItem";
this.revealInFileExplorerToolStripMenuItem.Size = new System.Drawing.Size(187, 22);
this.revealInFileExplorerToolStripMenuItem.Text = "Reveal in File Explorer";
this.revealInFileExplorerToolStripMenuItem.Click += new System.EventHandler(this.revealInFileExplorerToolStripMenuItem_Click);
//
// MainForm
//
this.AutoScaleDimensions = new System.Drawing.SizeF(8F, 16F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(800, 450);
this.ClientSize = new System.Drawing.Size(802, 458);
this.Controls.Add(this.tableLayoutPanel1);
this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon")));
this.MainMenuStrip = this.menuStrip1;
this.MinimumSize = new System.Drawing.Size(818, 497);
this.Name = "MainForm";
this.Text = "Carmanager 2";
this.Text = "Carmanager 3";
this.tableLayoutPanel1.ResumeLayout(false);
this.tableLayoutPanel1.PerformLayout();
this.tableLayoutPanel2.ResumeLayout(false);
this.tableLayoutPanel2.PerformLayout();
this.menuStrip1.ResumeLayout(false);
this.menuStrip1.PerformLayout();
this.ResumeLayout(false);
}
@@ -122,5 +203,13 @@
private System.Windows.Forms.TableLayoutPanel tableLayoutPanel2;
private System.Windows.Forms.TextBox tbxSearch;
private System.Windows.Forms.Button btnNewCar;
private System.Windows.Forms.MenuStrip menuStrip1;
private System.Windows.Forms.ToolStripMenuItem fileToolStripMenuItem;
private System.Windows.Forms.ToolStripMenuItem openToolStripMenuItem;
private System.Windows.Forms.ToolStripMenuItem saveToolStripMenuItem;
private System.Windows.Forms.ToolStripMenuItem saveAsToolStripMenuItem;
private System.Windows.Forms.ToolStripMenuItem importToolStripMenuItem;
private System.Windows.Forms.ToolStripMenuItem recentFilesToolStripMenuItem;
private System.Windows.Forms.ToolStripMenuItem revealInFileExplorerToolStripMenuItem;
}
}

View File

@@ -8,102 +8,124 @@ using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace CarManagerV2
namespace CarManagerV3
{
public partial class MainForm : Form
{
List<Car> cars = new List<Car>();
string filepath = "cars.csv";
public MainForm()
{
InitializeComponent();
SafeManager.InitializeFile("cars.csv");
List<Car> _cars = SafeManager.ReadCars("cars.csv");
List<string> recentFiles = SafeManager.GetRecentPaths();
if(recentFiles.Count > 0)
{
filepath = recentFiles[0];
}
SafeManager.InitializeFile(filepath);
List<Car> _cars = SafeManager.ReadCars(filepath);
refreshCars(_cars);
refreshRecents();
}
private void refreshCars(List<Car> _cars)
private async void refreshCars(List<Car> _cars, bool updateGlobal = true)
{
//if cars havent changed, dont refresh
//bool changed = false;
//foreach (Car car in _cars)
//{
// Car existing = cars.FirstOrDefault(c => c.Id == car.Id);
// if (existing == null)
// {
// Console.WriteLine($"Added: {car.Id}");
// changed = true;
// break;
// }
// else if (existing.isChanged(car))
// {
// Console.WriteLine($"Modified: {car.Id} / {existing.Id}");
// changed = true;
// break;
// }
//}
//if(!changed && cars.Count == _cars.Count)
//{
// Console.WriteLine("Cars are the same, not refreshing.");
// cars = _cars;
// return;
//}
this.Text = "Car Manager - " + System.IO.Path.GetFileName(filepath);
cars = _cars;
// Sort by Car.Order. If equal, sort by ID
_cars = _cars.Count > 0 ? _cars.OrderBy(c => c.Order).ThenBy(c => c.Id).ToList() : _cars;
if (updateGlobal)
{
cars = _cars;
}
//if(cars.SequenceEqual(_cars))
//{
// Console.WriteLine("Cars are the same, not refreshing.");
// return;
//}
//else
//{
// Console.WriteLine("Cars have changed, refreshing.");
// Console.WriteLine($"Old cars count: {cars.Count}, New cars count: {_cars.Count}");
// //find differences
// Console.WriteLine("Saved:");
// foreach (var car in cars)
// {
// Console.Write($"{car.Id}, {car.Make} {car.Model}, {car.Year} | ");
// }
// Console.WriteLine("-------------------");
// Console.WriteLine("New:");
// foreach (var car in _cars)
// {
// Console.Write($"{car.Id}, {car.Make} {car.Model}, {car.Year} | ");
// }
// Console.WriteLine();
// var addedCars = _cars.Except(cars).ToList();
// var removedCars = cars.Except(_cars).ToList();
// var modifiedCars = _cars.Where(c => cars.Any(c2 => c2.Id == c.Id && !c2.Equals(c))).ToList();
// Console.WriteLine($"Added cars: {addedCars.Count}, Removed cars: {removedCars.Count}, Modified cars: {modifiedCars.Count}");
//}
flpCars.Controls.Clear();
foreach (Car car in _cars)
{
// not in list? add it
bool isNew = flpCars.Controls.OfType<CarCard>().All(c => c.Car.Id != car.Id);
// existing but changed? update it
CarCard card = new CarCard();
if (!isNew)
{
CarCard existing = flpCars.Controls.OfType<CarCard>().First(c => c.Car.Id == car.Id);
Car existingCar = existing.Car;
if (existing == null)
{
Console.Error.WriteLine($"[L] Error: Existing car card not found for car ID: {car.Id}");
continue;
}
// compare details
Console.WriteLine($"[L] Checking car: {car.Id} | Car Color: {car.Color} | Ex Color: {existingCar.Color}");
if (existingCar.IsChanged(car))
{
Console.WriteLine($"[L] Updating car: {car.Id}");
// changes
card = existing;
}
else
{
// no changes
Console.WriteLine($"[L] No changes for car: {car.Id}");
flpCars.Controls.SetChildIndex(existing, _cars.IndexOf(car));
continue;
}
}
card.CarName = $"{car.Make} {car.Model}";
card.CarDetails = $"{car.Year}, {car.Mileage} km, ${car.Price}";
card.CarImage = ImageManager.GetImage(car);
card.Car = car.Clone();
card.LoadImage();
// clear existing event handlers to prevent multiple subscriptions
card.ClearCardClickedHandlers();
card.CardClicked += (s, e) =>
{
Console.WriteLine($"Card clicked: {car.Id}");
CarDetailsForm detailsForm = new CarDetailsForm(car);
detailsForm.FormClosed += (s2, e2) =>
detailsForm.FormClosed += async (s2, e2) =>
{
Console.WriteLine("Car details form closed.");
// refresh cars
Application.DoEvents();
Console.WriteLine("Refreshing cars...");
List<Car> __cars = SafeManager.ReadCars("cars.csv");
List<Car> __cars = await Task.Run(() => SafeManager.ReadCars(filepath));
if (tbxSearch.Text.Length > 0)
{
Console.WriteLine("Search box has text, applying search filter.");
cars = __cars;
searchList(tbxSearch.Text);
return;
}
refreshCars(__cars);
};
detailsForm.ShowDialog();
};
flpCars.Controls.Add(card);
if (isNew)
{
flpCars.Controls.Add(card);
}
flpCars.Controls.SetChildIndex(card, _cars.IndexOf(car));
}
// Remove cards that are no longer in _cars
var carIds = _cars.Select(c => c.Id).ToList();
var cardsToRemove = flpCars.Controls.OfType<CarCard>().Where(c => !carIds.Contains(c.Car.Id)).ToList();
foreach (var card in cardsToRemove)
{
flpCars.Controls.Remove(card);
}
flpCars.Refresh();
flpCars.Invalidate();
flpCars.Update();
}
@@ -115,13 +137,13 @@ namespace CarManagerV2
{
// refresh cars
Console.WriteLine("Refreshing cars...");
cars = SafeManager.ReadCars("cars.csv");
refreshCars(cars);
List<Car> cars_ = SafeManager.ReadCars(filepath);
refreshCars(cars_, false);
};
detailsForm.ShowDialog();
}
void searchList(string query)
List<Car> filterCarsByQuery(string query)
{
List<Car> results = new List<Car>();
foreach (Car car in cars)
@@ -131,13 +153,22 @@ namespace CarManagerV2
results.Add(car);
}
}
refreshCars(results);
return results;
}
private void tbxSearch_TextChanged(object sender, EventArgs e)
void searchList(string query)
{
List<Car> results = filterCarsByQuery(query);
refreshCars(results, false);
}
private async void tbxSearch_TextChanged(object sender, EventArgs e)
{
string query = tbxSearch.Text;
if(string.IsNullOrWhiteSpace(query))
await Task.Delay(100); // debounce
if (query != tbxSearch.Text) return; // text changed during delay
//flpCars.Controls.Clear();
if (string.IsNullOrWhiteSpace(query))
{
refreshCars(cars);
}
@@ -146,5 +177,207 @@ namespace CarManagerV2
searchList(query);
}
}
private void tableLayoutPanel1_Paint(object sender, PaintEventArgs e)
{
}
private void openToolStripMenuItem_Click(object sender, EventArgs e)
{
OpenFileDialog dlgOpen = new OpenFileDialog();
dlgOpen.Filter = "CSV Files (*.csv)|*.csv|All Files (*.*)|*.*";
dlgOpen.Title = "Open Car Data File";
// Default to users documents
dlgOpen.InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
DialogResult result = dlgOpen.ShowDialog();
if (result == DialogResult.OK)
{
try
{
List<Car> importedCars = SafeManager.ReadCars(dlgOpen.FileName);
if(importedCars.Count == 0)
{
throw new Exception("File doesn't contain valid Cars.");
}
filepath = dlgOpen.FileName;
cars = importedCars;
StateManager.setFilePath(filepath);
// Refresh display
refreshCars(cars);
MessageBox.Show("File loaded successfully.", "Load File", MessageBoxButtons.OK, MessageBoxIcon.Information);
SafeManager.AddRecentPath(filepath);
refreshRecents();
}
catch (Exception ex)
{
MessageBox.Show("Error loading file: " + ex.Message);
}
}
}
private void saveToolStripMenuItem_Click(object sender, EventArgs e)
{
refreshCars(cars);
SafeManager.SaveCars(filepath, cars);
MessageBox.Show("File saved successfully.", "Save File", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
private void saveAsToolStripMenuItem_Click(object sender, EventArgs e)
{
SaveFileDialog dlgSave = new SaveFileDialog();
dlgSave.Filter = "CSV Files (*.csv)|*.csv|All Files (*.*)|*.*";
dlgSave.Title = "Save Car Data File As";
// Default to users documents
dlgSave.InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
DialogResult result = dlgSave.ShowDialog();
if (result == DialogResult.OK)
{
// does file already exist?
/*if (System.IO.File.Exists(dlgSave.FileName))
{
var overwriteResult = MessageBox.Show("File already exists. Overwrite?", "Overwrite File", MessageBoxButtons.YesNo, MessageBoxIcon.Warning);
if (overwriteResult != DialogResult.Yes)
{
return;
}
}*/
// Windows already handles this lmao
filepath = dlgSave.FileName;
this.Text = "Car Manager - " + System.IO.Path.GetFileName(filepath);
StateManager.setFilePath(filepath);
SafeManager.SaveCars(filepath, cars);
SafeManager.AddRecentPath(filepath);
}
refreshRecents();
}
private void importToolStripMenuItem_Click(object sender, EventArgs e)
{
DialogResult result = MessageBox.Show("Importing will add cars from another file to this file. This action cannot be undone. Continue?", "Import Cars", MessageBoxButtons.YesNo, MessageBoxIcon.Warning);
if (result == DialogResult.Yes)
{
OpenFileDialog dlgOpen = new OpenFileDialog();
dlgOpen.Filter = "CSV Files (*.csv)|*.csv|All Files (*.*)|*.*";
dlgOpen.Title = "Import Car Data File";
// Default to users documents
dlgOpen.InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
DialogResult dlgResult = dlgOpen.ShowDialog();
if (dlgResult == DialogResult.OK)
{
try
{
Console.WriteLine("Starting merge...");
List<Car> importedCars = SafeManager.ReadCars(dlgOpen.FileName);
if(importedCars.Count == 0)
{
throw new Exception("File doesn't contain valid Cars.");
}
// merge cars
foreach (Car car in importedCars)
{
// check if car with same ID exists
if (cars.Any(c => c.Id == car.Id))
{
// assign new ID
int newId = cars.Count > 0 ? cars.Max(c => c.Id) + 1 : 1;
car.Id = newId;
}
cars.Add(car);
}
DialogResult mergeAsNewFileResult = MessageBox.Show("Do you want to save the merged cars as a new file?", "Save As New File", MessageBoxButtons.YesNo, MessageBoxIcon.Question);
if (mergeAsNewFileResult == DialogResult.Yes)
{
SaveFileDialog dlgSave = new SaveFileDialog();
dlgSave.Filter = "CSV Files (*.csv)|*.csv|All Files (*.*)|*.*";
dlgSave.Title = "Save Merged Car Data File As";
// Default to users documents
dlgSave.InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
DialogResult saveResult = dlgSave.ShowDialog();
if (saveResult == DialogResult.OK)
{
filepath = dlgSave.FileName;
StateManager.setFilePath(filepath);
SafeManager.SaveCars(filepath, cars);
SafeManager.AddRecentPath(filepath);
refreshRecents();
}
}
else
{
// save to current file
SafeManager.SaveCars(filepath, cars);
}
// Refresh display
refreshCars(cars);
MessageBox.Show("File imported successfully.", "Import File", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
catch (Exception ex)
{
MessageBox.Show("Error importing file: " + ex.Message);
}
}
else
{
Console.WriteLine("Import cancelled.");
}
}
}
private void recentFilesToolStripMenuItem_Click(object sender, EventArgs e)
{
}
private void refreshRecents()
{
recentFilesToolStripMenuItem.DropDownItems.Clear();
List<string> recentFiles = SafeManager.GetRecentPaths();
recentFilesToolStripMenuItem.Enabled = recentFiles.Count > 0;
recentFilesToolStripMenuItem.ToolTipText = recentFiles.Count > 0 ? "" : "No recent files.";
foreach (string path in recentFiles)
{
ToolStripMenuItem item = new ToolStripMenuItem(path);
item.Click += (s, e2) =>
{
try
{
List<Car> importedCars = SafeManager.ReadCars(path);
if(importedCars.Count == 0)
{
throw new Exception("File doesn't contain valid Cars.");
}
filepath = path;
cars = importedCars;
StateManager.setFilePath(filepath);
// Refresh display
refreshCars(cars);
MessageBox.Show("File loaded successfully.", "Load File", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
catch (Exception ex)
{
MessageBox.Show("Error loading file: " + ex.Message);
}
};
recentFilesToolStripMenuItem.DropDownItems.Add(item);
}
}
private void revealInFileExplorerToolStripMenuItem_Click(object sender, EventArgs e)
{
// Open File Explorer at the location of the current filepath
if (System.IO.File.Exists(filepath))
{
System.Diagnostics.Process.Start("explorer.exe", "/select,\"" + filepath + "\"");
}
else
{
MessageBox.Show("File does not exist.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
}
}

View File

@@ -117,6 +117,9 @@
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<metadata name="menuStrip1.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>17, 17</value>
</metadata>
<assembly alias="System.Drawing" name="System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
<data name="$this.Icon" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>

View File

@@ -1,4 +1,4 @@
namespace CarManagerV2
namespace CarManagerV3
{
partial class PleaseWait
{

View File

@@ -8,7 +8,7 @@ using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace CarManagerV2
namespace CarManagerV3
{
public partial class PleaseWait : Form
{

View File

@@ -4,7 +4,7 @@ using System.Linq;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace CarManagerV2
namespace CarManagerV3
{
internal static class Program
{

View File

@@ -5,11 +5,11 @@ using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("CarManagerV2")]
[assembly: AssemblyTitle("CarManagerV3")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("CarManagerV2")]
[assembly: AssemblyProduct("CarManagerV3")]
[assembly: AssemblyCopyright("Copyright © 2025")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]

View File

@@ -6,10 +6,12 @@ using System.Text;
using System.Threading.Tasks;
namespace CarManagerV2
namespace CarManagerV3
{
internal class SafeManager
{
private static readonly string recentPathsFile = "recent_paths.txt";
public static void InitializeFile(string path)
{
if (!File.Exists(@path))
@@ -48,5 +50,54 @@ namespace CarManagerV2
}
}
}
public static void AddRecentPath(string path)
{
// Read the file, if the path is not already in the file, add it to the top.
// If it is already in the file, move it to the top.
// Keep only the 5 most recent paths.
List<string> paths = new List<string>();
if (File.Exists(recentPathsFile))
{
using (StreamReader reader = new StreamReader(recentPathsFile))
{
string line;
while ((line = reader.ReadLine()) != null)
{
paths.Add(line);
}
}
}
paths.Remove(path);
paths.Insert(0, path);
if (paths.Count > 5)
{
paths = paths.Take(5).ToList();
}
using (StreamWriter writer = new StreamWriter(recentPathsFile))
{
foreach (string p in paths)
{
writer.WriteLine(p);
}
}
}
public static List<string> GetRecentPaths()
{
List<string> paths = new List<string>();
if (File.Exists(recentPathsFile))
{
using (StreamReader reader = new StreamReader(recentPathsFile))
{
string line;
while ((line = reader.ReadLine()) != null)
{
paths.Add(line);
}
}
}
return paths;
}
}
}

View File

@@ -4,16 +4,17 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace CarManagerV2
namespace CarManagerV3
{
internal class StateManager
{
static List<Car> cars = new List<Car>();
static string filePath = "cars.csv";
public static Car GetCarById(int id)
{
cars = SafeManager.ReadCars("cars.csv");
cars = SafeManager.ReadCars(filePath);
return cars.FirstOrDefault(c => c.Id == id);
}
@@ -21,18 +22,18 @@ namespace CarManagerV2
public static void AddCar(Car car)
{
cars = SafeManager.ReadCars("cars.csv");
cars = SafeManager.ReadCars(filePath);
cars.Add(car);
SafeManager.SaveCars("cars.csv", cars);
SafeManager.SaveCars(filePath, cars);
}
public static void RemoveCar(Car car)
{
cars = SafeManager.ReadCars("cars.csv");
cars = SafeManager.ReadCars(filePath);
Car existingCar = GetCarById(car.Id);
if (existingCar == null) return;
cars.Remove(existingCar);
SafeManager.SaveCars("cars.csv", cars);
SafeManager.SaveCars(filePath, cars);
}
public static void UpdateCar(Car car)
@@ -43,17 +44,22 @@ namespace CarManagerV2
int index = cars.IndexOf(existingCar);
cars[index] = car;
Console.WriteLine("Updated car: " + existingCar.Id);
SafeManager.SaveCars("cars.csv", cars);
SafeManager.SaveCars(filePath, cars);
}
}
public static Car CreateCar(string make, string model, int year, string color, int mileage, decimal price)
{
cars = SafeManager.ReadCars("cars.csv");
cars = SafeManager.ReadCars(filePath);
int newId = cars.Count > 0 ? cars.Max(c => c.Id) + 1 : 1;
Car newCar = new Car(newId, make, model, year, color, mileage, price);
AddCar(newCar);
return newCar;
}
public static void setFilePath(string path)
{
filePath = path;
}
}
}

View File

@@ -1,9 +1,9 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.14.36414.22 d17.14
VisualStudioVersion = 17.14.36414.22
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CarManagerV2", "CarManagerV2\CarManagerV2.csproj", "{93CA258B-A645-41A8-A24F-59036ABC173F}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CarManagerV3", "CarManagerV2\CarManagerV3.csproj", "{93CA258B-A645-41A8-A24F-59036ABC173F}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution

Binary file not shown.