Compare commits
17 Commits
dd10745c76
...
1.0
| Author | SHA1 | Date | |
|---|---|---|---|
| e431a05124 | |||
| b20e0a94dd | |||
| afa98b2681 | |||
| 6d50c28c02 | |||
| 8be81812c1 | |||
| de603b5cef | |||
| 640d9acbf6 | |||
| e511610052 | |||
|
|
d17e1183f9 | ||
|
|
bb71806a22 | ||
|
|
e89fc79ebb | ||
|
|
5b56d76cd7 | ||
|
|
0c8b10cfe6 | ||
|
|
d4f3ac8776 | ||
|
|
746f8c4039 | ||
|
|
cca7c37ffe | ||
|
|
306994542e |
4
.gitignore
vendored
4
.gitignore
vendored
@@ -361,4 +361,6 @@ MigrationBackup/
|
||||
|
||||
# Fody - auto-generated XML schema
|
||||
FodyWeavers.xsd
|
||||
IAF42_Kaulmann_CarmanagerV2.zip
|
||||
|
||||
# Submissions folder (contains submission ZIPs)
|
||||
submissions/
|
||||
@@ -1,60 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace CarManagerV2
|
||||
{
|
||||
public class 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)
|
||||
{
|
||||
this.id = id;
|
||||
this.make = make;
|
||||
this.model = model;
|
||||
this.year = year;
|
||||
this.color = color;
|
||||
this.mileage = mileage;
|
||||
this.price = price;
|
||||
}
|
||||
|
||||
public int Id { get { return id; } set { id = value; } }
|
||||
public string Make { get { return make; } set { make = value; } }
|
||||
public string Model { get { return model; } set { model = value; } }
|
||||
public int Year { get { return year; } set { year = value; } }
|
||||
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 override string ToString()
|
||||
{
|
||||
return $"{make} {model} ({year})";
|
||||
}
|
||||
|
||||
public string ToCsvString()
|
||||
{
|
||||
return $"{id};{make};{model};{year};{color};{mileage};{price}";
|
||||
}
|
||||
|
||||
public static Car FromCsvString(string csv)
|
||||
{
|
||||
string[] parts = csv.Split(';');
|
||||
return new Car(int.Parse(parts[0]), parts[1], parts[2], int.Parse(parts[3]), parts[4], int.Parse(parts[5]), decimal.Parse(parts[6]));
|
||||
}
|
||||
|
||||
public bool IsChanged(Car other)
|
||||
{
|
||||
return make != other.make || model != other.model || year != other.year || color != other.color || mileage != other.mileage || price != other.price;
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,150 +0,0 @@
|
||||
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;
|
||||
|
||||
namespace CarManagerV2
|
||||
{
|
||||
public partial class MainForm : Form
|
||||
{
|
||||
List<Car> cars = new List<Car>();
|
||||
|
||||
public MainForm()
|
||||
{
|
||||
InitializeComponent();
|
||||
SafeManager.InitializeFile("cars.csv");
|
||||
List<Car> _cars = SafeManager.ReadCars("cars.csv");
|
||||
refreshCars(_cars);
|
||||
}
|
||||
|
||||
private void refreshCars(List<Car> _cars)
|
||||
{
|
||||
//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;
|
||||
//}
|
||||
|
||||
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)
|
||||
{
|
||||
CarCard card = new CarCard();
|
||||
card.CarName = $"{car.Make} {car.Model}";
|
||||
card.CarDetails = $"{car.Year}, {car.Mileage} km, ${car.Price}";
|
||||
card.CarImage = ImageManager.GetImage(car);
|
||||
card.CardClicked += (s, e) =>
|
||||
{
|
||||
Console.WriteLine($"Card clicked: {car.Id}");
|
||||
CarDetailsForm detailsForm = new CarDetailsForm(car);
|
||||
detailsForm.FormClosed += (s2, e2) =>
|
||||
{
|
||||
// refresh cars
|
||||
Application.DoEvents();
|
||||
Console.WriteLine("Refreshing cars...");
|
||||
List<Car> __cars = SafeManager.ReadCars("cars.csv");
|
||||
refreshCars(__cars);
|
||||
};
|
||||
detailsForm.ShowDialog();
|
||||
};
|
||||
flpCars.Controls.Add(card);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void btnNewCar_Click(object sender, EventArgs e)
|
||||
{
|
||||
Car foocar = StateManager.CreateCar("New", "Car", 2020, "White", 0, 20000);
|
||||
CarDetailsForm detailsForm = new CarDetailsForm(foocar);
|
||||
detailsForm.FormClosed += (s2, e2) =>
|
||||
{
|
||||
// refresh cars
|
||||
Console.WriteLine("Refreshing cars...");
|
||||
cars = SafeManager.ReadCars("cars.csv");
|
||||
refreshCars(cars);
|
||||
};
|
||||
detailsForm.ShowDialog();
|
||||
}
|
||||
|
||||
void searchList(string query)
|
||||
{
|
||||
List<Car> results = new List<Car>();
|
||||
foreach (Car car in cars)
|
||||
{
|
||||
if (car.Make.ToLower().Contains(query.ToLower()) || car.Model.ToLower().Contains(query.ToLower()) || car.Year.ToString().Contains(query) || car.Mileage.ToString().Contains(query) || car.Price.ToString().Contains(query))
|
||||
{
|
||||
results.Add(car);
|
||||
}
|
||||
}
|
||||
refreshCars(results);
|
||||
}
|
||||
|
||||
private void tbxSearch_TextChanged(object sender, EventArgs e)
|
||||
{
|
||||
string query = tbxSearch.Text;
|
||||
if(string.IsNullOrWhiteSpace(query))
|
||||
{
|
||||
refreshCars(cars);
|
||||
}
|
||||
else
|
||||
{
|
||||
searchList(query);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
|
||||
namespace CarManagerV2
|
||||
{
|
||||
internal class SafeManager
|
||||
{
|
||||
public static void InitializeFile(string path)
|
||||
{
|
||||
if (!File.Exists(@path))
|
||||
{
|
||||
using (StreamWriter writer = new StreamWriter(@path))
|
||||
{
|
||||
// Create the file, empty
|
||||
writer.WriteLine();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static List<Car> ReadCars(string path)
|
||||
{
|
||||
List<Car> cars = new List<Car>();
|
||||
using (StreamReader reader = new StreamReader(@path))
|
||||
{
|
||||
string line;
|
||||
while ((line = reader.ReadLine()) != null)
|
||||
{
|
||||
// Process the line
|
||||
if (line == "") continue;
|
||||
cars.Add(Car.FromCsvString(line));
|
||||
}
|
||||
}
|
||||
return cars;
|
||||
}
|
||||
|
||||
public static void SaveCars(string path, List<Car> cars)
|
||||
{
|
||||
using (StreamWriter writer = new StreamWriter(@path))
|
||||
{
|
||||
foreach (Car car in cars)
|
||||
{
|
||||
writer.WriteLine(car.ToCsvString());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,59 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace CarManagerV2
|
||||
{
|
||||
internal class StateManager
|
||||
{
|
||||
|
||||
static List<Car> cars = new List<Car>();
|
||||
|
||||
public static Car GetCarById(int id)
|
||||
{
|
||||
cars = SafeManager.ReadCars("cars.csv");
|
||||
return cars.FirstOrDefault(c => c.Id == id);
|
||||
}
|
||||
|
||||
public static List<Car> Cars { get { return cars; } set { cars = value; } }
|
||||
|
||||
public static void AddCar(Car car)
|
||||
{
|
||||
cars = SafeManager.ReadCars("cars.csv");
|
||||
cars.Add(car);
|
||||
SafeManager.SaveCars("cars.csv", cars);
|
||||
}
|
||||
|
||||
public static void RemoveCar(Car car)
|
||||
{
|
||||
cars = SafeManager.ReadCars("cars.csv");
|
||||
Car existingCar = GetCarById(car.Id);
|
||||
if (existingCar == null) return;
|
||||
cars.Remove(existingCar);
|
||||
SafeManager.SaveCars("cars.csv", cars);
|
||||
}
|
||||
|
||||
public static void UpdateCar(Car car)
|
||||
{
|
||||
Car existingCar = GetCarById(car.Id);
|
||||
if (existingCar != null)
|
||||
{
|
||||
int index = cars.IndexOf(existingCar);
|
||||
cars[index] = car;
|
||||
Console.WriteLine("Updated car: " + existingCar.Id);
|
||||
SafeManager.SaveCars("cars.csv", cars);
|
||||
}
|
||||
}
|
||||
|
||||
public static Car CreateCar(string make, string model, int year, string color, int mileage, decimal price)
|
||||
{
|
||||
cars = SafeManager.ReadCars("cars.csv");
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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", "CarManagerV3\CarManagerV3.csproj", "{93CA258B-A645-41A8-A24F-59036ABC173F}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
114
CarManagerV3/Car.cs
Normal file
114
CarManagerV3/Car.cs
Normal file
@@ -0,0 +1,114 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace CarManagerV3
|
||||
{
|
||||
/// <summary>
|
||||
/// Class <c>Car</c> represents a car with various attributes such as make, model, year, color, mileage, and price.
|
||||
/// </summary>
|
||||
public class Car
|
||||
{
|
||||
public int Id;
|
||||
public string Make;
|
||||
public string Model;
|
||||
public int Year;
|
||||
public string Color;
|
||||
public int Mileage;
|
||||
public decimal Price;
|
||||
public int Order;
|
||||
|
||||
//TODO: Make Id read-only CUID. Allows for better integrity, especially when merging.
|
||||
//TODO: Add buying price and automatic calculation of profit/loss with selling price suggestion.
|
||||
//TODO: Add Buying Date.
|
||||
//TODO: Add sold boolean and Sold Date.
|
||||
//TODO: Add "hidden" attribute for cars that are not for sale anymore but should be kept in the database for records.
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Car"/> class.
|
||||
/// </summary>
|
||||
/// <param name="id">The unique identifier as an Integer.</param>
|
||||
/// <param name="make">The make / manufacturer of the car.</param>
|
||||
/// <param name="model">The model.</param>
|
||||
/// <param name="year">The year the car was built.</param>
|
||||
/// <param name="color">The color of the car.</param>
|
||||
/// <param name="mileage">The current mileage on the car.</param>
|
||||
/// <param name="price">The selling-price of the car.</param>
|
||||
/// <param name="order">The order.</param>
|
||||
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;
|
||||
this.Model = model;
|
||||
this.Year = year;
|
||||
this.Color = color;
|
||||
this.Mileage = mileage;
|
||||
this.Price = price;
|
||||
this.Order = order;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Converts to string in a custom readable format.
|
||||
/// <example>
|
||||
/// For Example:
|
||||
/// Skoda Fabia (2015)
|
||||
/// </example>
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// A <see cref="System.String" /> that represents this instance.
|
||||
/// </returns>
|
||||
public override string ToString()
|
||||
{
|
||||
return $"{this.Make} {this.Model} ({this.Year})";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts to default CSV savable string.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public string ToCsvString()
|
||||
{
|
||||
return $"{this.Id};{this.Make};{this.Model};{this.Year};{this.Color};{this.Mileage};{this.Price}";
|
||||
}
|
||||
|
||||
//TODO: Add error handling for malformed CSV strings and detection for missing fields.
|
||||
//TODO: Add support for different CSV formats (e.g., different delimiters, quoted fields, etc.).
|
||||
//TODO: Add support for nil or optional fields.
|
||||
//TODO: Add detectin for invalid data / nonsensical values (e.g., negative mileage or year in the future). / Validate it actually is a car.
|
||||
/// <summary>
|
||||
/// Creates a <see cref="Car"/> instance from a CSV string in the default format.
|
||||
/// </summary>
|
||||
/// <param name="csv">The CSV Line.</param>
|
||||
/// <returns>
|
||||
/// A new <see cref="Car"/> instance.
|
||||
/// </returns>
|
||||
public static Car FromCsvString(string csv)
|
||||
{
|
||||
string[] parts = csv.Split(';');
|
||||
return new Car(int.Parse(parts[0]), parts[1], parts[2], int.Parse(parts[3]), parts[4], int.Parse(parts[5]), decimal.Parse(parts[6]));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether this <see cref="Car"/> is any different from the provided <see cref="Car" />.
|
||||
/// </summary>
|
||||
/// <param name="other">The <see cref="Car"/> to check against</param>
|
||||
/// <returns>
|
||||
/// <c>true</c> if the specified other is changed; otherwise, <c>false</c>.
|
||||
/// </returns>
|
||||
public bool IsChanged(Car other)
|
||||
{
|
||||
return this.Make != other.Make || this.Model != other.Model || this.Year != other.Year || this.Color != other.Color || this.Mileage != other.Mileage || this.Price != other.Price || this.Color != other.Color;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clones this instance.
|
||||
/// </summary>
|
||||
/// <returns>An identical but seperate <see cref="Car"/></returns>
|
||||
public Car Clone()
|
||||
{
|
||||
return new Car(this.Id, this.Make, this.Model, this.Year, this.Color, this.Mileage, this.Price);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
10889
CarManagerV3/CarCard.resx
Normal file
10889
CarManagerV3/CarCard.resx
Normal file
File diff suppressed because it is too large
Load Diff
@@ -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);
|
||||
//
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,12 +6,27 @@
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProjectGuid>{93CA258B-A645-41A8-A24F-59036ABC173F}</ProjectGuid>
|
||||
<OutputType>WinExe</OutputType>
|
||||
<RootNamespace>CarManagerV2</RootNamespace>
|
||||
<AssemblyName>CarManagerV2</AssemblyName>
|
||||
<RootNamespace>CarManagerV3</RootNamespace>
|
||||
<AssemblyName>CarManagerV3</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
|
||||
<Deterministic>true</Deterministic>
|
||||
<PublishUrl>publish\</PublishUrl>
|
||||
<Install>true</Install>
|
||||
<InstallFrom>Disk</InstallFrom>
|
||||
<UpdateEnabled>false</UpdateEnabled>
|
||||
<UpdateMode>Foreground</UpdateMode>
|
||||
<UpdateInterval>7</UpdateInterval>
|
||||
<UpdateIntervalUnits>Days</UpdateIntervalUnits>
|
||||
<UpdatePeriodically>false</UpdatePeriodically>
|
||||
<UpdateRequired>false</UpdateRequired>
|
||||
<MapFileExtensions>true</MapFileExtensions>
|
||||
<ApplicationRevision>0</ApplicationRevision>
|
||||
<ApplicationVersion>1.0.0.%2a</ApplicationVersion>
|
||||
<IsWebBootstrapper>false</IsWebBootstrapper>
|
||||
<UseApplicationTrust>false</UseApplicationTrust>
|
||||
<BootstrapperEnabled>true</BootstrapperEnabled>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
@@ -96,6 +111,7 @@
|
||||
<Compile Include="Properties\Resources.Designer.cs">
|
||||
<AutoGen>True</AutoGen>
|
||||
<DependentUpon>Resources.resx</DependentUpon>
|
||||
<DesignTime>True</DesignTime>
|
||||
</Compile>
|
||||
<None Include="Properties\Settings.settings">
|
||||
<Generator>SettingsSingleFileGenerator</Generator>
|
||||
@@ -110,5 +126,17 @@
|
||||
<ItemGroup>
|
||||
<None Include="App.config" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<BootstrapperPackage Include=".NETFramework,Version=v4.7.2">
|
||||
<Visible>False</Visible>
|
||||
<ProductName>Microsoft .NET Framework 4.7.2 %28x86 and x64%29</ProductName>
|
||||
<Install>true</Install>
|
||||
</BootstrapperPackage>
|
||||
<BootstrapperPackage Include="Microsoft.Net.Framework.3.5.SP1">
|
||||
<Visible>False</Visible>
|
||||
<ProductName>.NET Framework 3.5 SP1</ProductName>
|
||||
<Install>false</Install>
|
||||
</BootstrapperPackage>
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
</Project>
|
||||
@@ -5,11 +5,16 @@ using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace CarManagerV2
|
||||
namespace CarManagerV3
|
||||
{
|
||||
/// <summary>
|
||||
/// The <c>ImageManager</c> class is responsible for managing car images, including fetching and storing them locally.
|
||||
/// </summary>
|
||||
internal class ImageManager
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the image folder by creating it if it does not exist.
|
||||
/// </summary>
|
||||
public static void InitializeImageFolder()
|
||||
{
|
||||
string path = "images";
|
||||
@@ -19,6 +24,11 @@ namespace CarManagerV2
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generates the image path for a given car based on its attributes.
|
||||
/// </summary>
|
||||
/// <param name="car">The car.</param>
|
||||
/// <returns>The image path for this Car.</returns>
|
||||
public static string GetImagePath(Car car)
|
||||
{
|
||||
string basePath = "images/";
|
||||
@@ -26,6 +36,11 @@ namespace CarManagerV2
|
||||
return basePath + fileName;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the image for a given car, fetching it if necessary.
|
||||
/// </summary>
|
||||
/// <param name="car">The car.</param>
|
||||
/// <returns>The <see cref="Image"/> object for the car.</returns>
|
||||
public static Image GetImage(Car car)
|
||||
{
|
||||
InitializeImageFolder();
|
||||
@@ -43,11 +58,16 @@ namespace CarManagerV2
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Fetches an image for a Car from https://cdn.imagin.studio/getimage if it does not already exist locally and saves it to images/<c>Make_Model_Year_Color.webp</>.
|
||||
/// If the image cannot be fetched, a placeholder image is used instead.
|
||||
/// Uses example customer id "hrjavascript-mastery", because they wouldn't give me one. Stole this from a tutorial page. :)
|
||||
/// </summary>
|
||||
/// <param name="car">The car.</param>
|
||||
public static void FetchImage(Car car)
|
||||
{
|
||||
// Fetch the image from https://cdn.imagin.studio/getimage and save it to images/Make_Model_Year.webp
|
||||
// use params like this: ?customer=hrjavascript-mastery&zoomType=fullscreen&make={make}&modelFamily={model}&modelYear={year}&angle=front&paintDescription={color}&fileType=png
|
||||
|
||||
// check if the image already exists
|
||||
string path = GetImagePath(car);
|
||||
if (System.IO.File.Exists(path))
|
||||
@@ -55,7 +75,7 @@ namespace CarManagerV2
|
||||
return;
|
||||
}
|
||||
string url = $"https://cdn.imagin.studio/getimage?customer=hrjavascript-mastery&zoomType=fullscreen&make={car.Make}&modelFamily={car.Model}&modelYear={car.Year}&angle=front&paintDescription={car.Color}&fileType=png";
|
||||
//add Referer header
|
||||
//add Referer header to avoid 403 error
|
||||
using (var client = new System.Net.WebClient())
|
||||
{
|
||||
client.Headers.Add("Referer", "http://localhost");
|
||||
@@ -65,7 +85,7 @@ namespace CarManagerV2
|
||||
}
|
||||
catch
|
||||
{
|
||||
// if error, use no_image_available.png
|
||||
// if error, use fallback image no_image_available.png
|
||||
System.IO.File.Copy("images/no_image_available.png", path);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
404
CarManagerV3/MainForm.cs
Normal file
404
CarManagerV3/MainForm.cs
Normal file
@@ -0,0 +1,404 @@
|
||||
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;
|
||||
|
||||
namespace CarManagerV3
|
||||
{
|
||||
public partial class MainForm : Form
|
||||
{
|
||||
List<Car> cars = new List<Car>();
|
||||
string filepath = "cars.csv";
|
||||
|
||||
public MainForm()
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
// Open the most recent file if it exists. Otherwise, use default filepath.
|
||||
List<string> recentFiles = SafeManager.GetRecentPaths();
|
||||
if(recentFiles.Count > 0)
|
||||
{
|
||||
filepath = recentFiles[0];
|
||||
}
|
||||
|
||||
SafeManager.InitializeFile(filepath);
|
||||
List<Car> _cars = SafeManager.ReadCars(filepath);
|
||||
|
||||
refreshCars(_cars);
|
||||
refreshRecents();
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Refreshes the cars displayed in the flow layout panel.
|
||||
/// </summary>
|
||||
/// <param name="_cars">The cars.</param>
|
||||
/// <param name="updateGlobal">if set to <c>true</c> [update global].</param>
|
||||
private async void refreshCars(List<Car> _cars, bool updateGlobal = true)
|
||||
{
|
||||
|
||||
this.Text = "Car Manager - " + System.IO.Path.GetFileName(filepath);
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
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.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 += async (s2, e2) =>
|
||||
{
|
||||
Console.WriteLine("Car details form closed.");
|
||||
// refresh cars
|
||||
Console.WriteLine("Refreshing cars...");
|
||||
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();
|
||||
};
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
|
||||
private void btnNewCar_Click(object sender, EventArgs e)
|
||||
{
|
||||
Car foocar = StateManager.CreateCar("New", "Car", 2020, "White", 0, 20000);
|
||||
CarDetailsForm detailsForm = new CarDetailsForm(foocar);
|
||||
detailsForm.FormClosed += (s2, e2) =>
|
||||
{
|
||||
// refresh cars
|
||||
Console.WriteLine("Refreshing cars...");
|
||||
List<Car> cars_ = SafeManager.ReadCars(filepath);
|
||||
refreshCars(cars_, false);
|
||||
};
|
||||
detailsForm.ShowDialog();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Filters the cars by a search query.
|
||||
/// </summary>
|
||||
/// <param name="query">The search query.</param>
|
||||
/// <returns>
|
||||
/// A list of <see cref="Car"/> objects that match the query.
|
||||
/// </returns>
|
||||
List<Car> filterCarsByQuery(string query)
|
||||
{
|
||||
List<Car> results = new List<Car>();
|
||||
foreach (Car car in cars)
|
||||
{
|
||||
if (car.Make.ToLower().Contains(query.ToLower()) || car.Model.ToLower().Contains(query.ToLower()) || car.Year.ToString().Contains(query) || car.Mileage.ToString().Contains(query) || car.Price.ToString().Contains(query))
|
||||
{
|
||||
results.Add(car);
|
||||
}
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Searches the list of cars by a query and refreshes the display.
|
||||
/// </summary>
|
||||
/// <param name="query">The query.</param>
|
||||
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;
|
||||
await Task.Delay(100); // debounce
|
||||
if (query != tbxSearch.Text) return; // text changed during delay
|
||||
//flpCars.Controls.Clear();
|
||||
if (string.IsNullOrWhiteSpace(query))
|
||||
{
|
||||
refreshCars(cars);
|
||||
}
|
||||
else
|
||||
{
|
||||
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.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Unbind and remove this.
|
||||
private void recentFilesToolStripMenuItem_Click(object sender, EventArgs e)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Refreshes the recently opened files menu.
|
||||
/// </summary>
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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>
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace CarManagerV2
|
||||
namespace CarManagerV3
|
||||
{
|
||||
partial class PleaseWait
|
||||
{
|
||||
@@ -8,7 +8,7 @@ using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace CarManagerV2
|
||||
namespace CarManagerV3
|
||||
{
|
||||
public partial class PleaseWait : Form
|
||||
{
|
||||
@@ -4,7 +4,7 @@ using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace CarManagerV2
|
||||
namespace CarManagerV3
|
||||
{
|
||||
internal static class Program
|
||||
{
|
||||
@@ -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("")]
|
||||
@@ -8,8 +8,8 @@
|
||||
// </auto-generated>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace CarManagerV2.Properties
|
||||
{
|
||||
namespace CarManagerV3.Properties {
|
||||
using System;
|
||||
|
||||
|
||||
/// <summary>
|
||||
@@ -19,32 +19,27 @@ namespace CarManagerV2.Properties
|
||||
// class via a tool like ResGen or Visual Studio.
|
||||
// To add or remove a member, edit your .ResX file then rerun ResGen
|
||||
// with the /str option, or rebuild your VS project.
|
||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
|
||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||
internal class Resources
|
||||
{
|
||||
internal class Resources {
|
||||
|
||||
private static global::System.Resources.ResourceManager resourceMan;
|
||||
|
||||
private static global::System.Globalization.CultureInfo resourceCulture;
|
||||
|
||||
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
|
||||
internal Resources()
|
||||
{
|
||||
internal Resources() {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the cached ResourceManager instance used by this class.
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
internal static global::System.Resources.ResourceManager ResourceManager
|
||||
{
|
||||
get
|
||||
{
|
||||
if ((resourceMan == null))
|
||||
{
|
||||
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("CarManagerV2.Properties.Resources", typeof(Resources).Assembly);
|
||||
internal static global::System.Resources.ResourceManager ResourceManager {
|
||||
get {
|
||||
if (object.ReferenceEquals(resourceMan, null)) {
|
||||
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("CarManagerV3.Properties.Resources", typeof(Resources).Assembly);
|
||||
resourceMan = temp;
|
||||
}
|
||||
return resourceMan;
|
||||
@@ -56,14 +51,11 @@ namespace CarManagerV2.Properties
|
||||
/// resource lookups using this strongly typed resource class.
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
internal static global::System.Globalization.CultureInfo Culture
|
||||
{
|
||||
get
|
||||
{
|
||||
internal static global::System.Globalization.CultureInfo Culture {
|
||||
get {
|
||||
return resourceCulture;
|
||||
}
|
||||
set
|
||||
{
|
||||
set {
|
||||
resourceCulture = value;
|
||||
}
|
||||
}
|
||||
@@ -8,21 +8,17 @@
|
||||
// </auto-generated>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace CarManagerV2.Properties
|
||||
{
|
||||
namespace CarManagerV3.Properties {
|
||||
|
||||
|
||||
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")]
|
||||
internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase
|
||||
{
|
||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "17.14.0.0")]
|
||||
internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
|
||||
|
||||
private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
|
||||
|
||||
public static Settings Default
|
||||
{
|
||||
get
|
||||
{
|
||||
public static Settings Default {
|
||||
get {
|
||||
return defaultInstance;
|
||||
}
|
||||
}
|
||||
135
CarManagerV3/SafeManager.cs
Normal file
135
CarManagerV3/SafeManager.cs
Normal file
@@ -0,0 +1,135 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
|
||||
namespace CarManagerV3
|
||||
{
|
||||
/// <summary>
|
||||
/// Handles safe reading and writing of car data to files.
|
||||
/// </summary>
|
||||
internal class SafeManager
|
||||
{
|
||||
/// <summary>
|
||||
/// The path of the txt file that contains recently opened file paths.
|
||||
/// </summary>
|
||||
private static readonly string recentPathsFile = "recent_paths.txt";
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a file at a specified path if it does not already exist.
|
||||
/// </summary>
|
||||
/// <param name="path">The path.</param>
|
||||
public static void InitializeFile(string path)
|
||||
{
|
||||
if (!File.Exists(@path))
|
||||
{
|
||||
using (StreamWriter writer = new StreamWriter(@path))
|
||||
{
|
||||
// Create the file, empty
|
||||
writer.WriteLine();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads cars from a specified file path.
|
||||
/// </summary>
|
||||
/// <param name="path">The path.</param>
|
||||
/// <returns>
|
||||
/// A <see cref="List{Car}"/> containing the cars read from the file.
|
||||
/// </returns>
|
||||
public static List<Car> ReadCars(string path)
|
||||
{
|
||||
List<Car> cars = new List<Car>();
|
||||
using (StreamReader reader = new StreamReader(@path))
|
||||
{
|
||||
string line;
|
||||
while ((line = reader.ReadLine()) != null)
|
||||
{
|
||||
// Process the line
|
||||
if (line == "") continue;
|
||||
cars.Add(Car.FromCsvString(line));
|
||||
}
|
||||
}
|
||||
return cars;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Saves the cars to a specified file path.
|
||||
/// </summary>
|
||||
/// <param name="path">The path.</param>
|
||||
/// <param name="cars">A <see cref="List{Car}"/> containing all cars to save.</param>
|
||||
public static void SaveCars(string path, List<Car> cars)
|
||||
{
|
||||
using (StreamWriter writer = new StreamWriter(@path))
|
||||
{
|
||||
foreach (Car car in cars)
|
||||
{
|
||||
writer.WriteLine(car.ToCsvString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a file path to the recent paths list.
|
||||
/// If a path is already in the list, it is moved to the top.
|
||||
/// Otherwise, it is added to the top.
|
||||
/// Keeps only the 5 most recent paths.
|
||||
/// </summary>
|
||||
/// <param name="path">The path.</param>
|
||||
public static void AddRecentPath(string path)
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the recently opened file paths.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// A <see cref="List{String}"/> containing the recent file paths.
|
||||
/// </returns>
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
117
CarManagerV3/StateManager.cs
Normal file
117
CarManagerV3/StateManager.cs
Normal file
@@ -0,0 +1,117 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace CarManagerV3
|
||||
{
|
||||
// Could be made non-static / non-singleton if multiple collections are needed in the future. Not likely though.
|
||||
/// <summary>
|
||||
/// The <c>StateManager</c> class is responsible for managing the state of the car collection, including adding, removing, updating, and retrieving cars.
|
||||
/// Fully static / singleton at the moment, as only one collection is needed.
|
||||
/// </summary>
|
||||
internal class StateManager
|
||||
{
|
||||
// Initialize global static list of cars
|
||||
static List<Car> cars = new List<Car>();
|
||||
// Initialize default file path for car data.
|
||||
// TODO: If no recent file paths are found, prompt user to select a file path instead of using a hardcoded default in the program folder.
|
||||
static string filePath = "cars.csv";
|
||||
|
||||
/// <summary>
|
||||
/// Gets a car by its identifier.
|
||||
/// </summary>
|
||||
/// <param name="id">The identifier.</param>
|
||||
/// <returns>
|
||||
/// A <see cref="Car"/> object if found; otherwise, null.
|
||||
/// </returns>
|
||||
public static Car GetCarById(int id)
|
||||
{
|
||||
cars = SafeManager.ReadCars(filePath);
|
||||
return cars.FirstOrDefault(c => c.Id == id);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Public getter and setter for the cars list. Used to have better control over the list.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The cars.
|
||||
/// </value>
|
||||
public static List<Car> Cars { get { return cars; } set { cars = value; } }
|
||||
|
||||
/// <summary>
|
||||
/// Adds a car to the collection.
|
||||
/// </summary>
|
||||
/// <param name="car">The car to add.</param>
|
||||
public static void AddCar(Car car)
|
||||
{
|
||||
cars = SafeManager.ReadCars(filePath);
|
||||
cars.Add(car);
|
||||
SafeManager.SaveCars(filePath, cars);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes a car from the collection.
|
||||
/// </summary>
|
||||
/// <param name="car">The car to remove.</param>
|
||||
public static void RemoveCar(Car car)
|
||||
{
|
||||
cars = SafeManager.ReadCars(filePath);
|
||||
Car existingCar = GetCarById(car.Id);
|
||||
if (existingCar == null) return;
|
||||
cars.Remove(existingCar);
|
||||
SafeManager.SaveCars(filePath, cars);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates a car in the collection. Identifies itself by its Id.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// If the car's Id has changed during editing, this will not work correctly. Keep Id immutable!
|
||||
/// </remarks>
|
||||
/// <param name="car">The car to update.</param>
|
||||
public static void UpdateCar(Car car)
|
||||
{
|
||||
Car existingCar = GetCarById(car.Id);
|
||||
if (existingCar != null)
|
||||
{
|
||||
int index = cars.IndexOf(existingCar);
|
||||
cars[index] = car;
|
||||
Console.WriteLine("Updated car: " + existingCar.Id);
|
||||
SafeManager.SaveCars(filePath, cars);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new car and adds it to the collection.
|
||||
/// </summary>
|
||||
/// <param name="make">The make.</param>
|
||||
/// <param name="model">The model.</param>
|
||||
/// <param name="year">The year.</param>
|
||||
/// <param name="color">The color.</param>
|
||||
/// <param name="mileage">The mileage.</param>
|
||||
/// <param name="price">The price.</param>
|
||||
/// <returns>
|
||||
/// The newly created <see cref="Car"/> object.
|
||||
/// </returns>
|
||||
public static Car CreateCar(string make, string model, int year, string color, int mileage, decimal price)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the file path used for saving and loading car data.
|
||||
/// Called when user selects a new file path.
|
||||
/// </summary>
|
||||
/// <param name="path">The path.</param>
|
||||
public static void setFilePath(string path)
|
||||
{
|
||||
filePath = path;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user