515 lines
20 KiB
C#
515 lines
20 KiB
C#
using DBWizard.Models;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.ComponentModel;
|
|
using System.Data;
|
|
using System.Drawing;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Runtime.Serialization.Formatters.Binary;
|
|
using System.Text;
|
|
using System.Text.RegularExpressions;
|
|
using System.Threading.Tasks;
|
|
using System.Windows.Forms;
|
|
|
|
/* References
|
|
* https://www.youtube.com/watch?v=ayp3tHEkRc0
|
|
* https://www.coderslexicon.com/passing-data-between-forms-using-delegates-and-events/
|
|
* https://dapper-tutorial.net/parameter-anonymous
|
|
* https://www.sqlitetutorial.net/sqlite-foreign-key/
|
|
* https://stackoverflow.com/questions/289680/difference-between-2-dates-in-sqlite
|
|
* https://dzone.com/articles/convert-object-byte-array-and
|
|
*/
|
|
// Using a delegate to trigger method on the customer view form
|
|
// https://www.coderslexicon.com/passing-data-between-forms-using-delegates-and-events/
|
|
|
|
|
|
|
|
namespace DBWizard
|
|
{
|
|
public partial class Form1 : Form
|
|
{
|
|
//Global instance of the SqliteDataAccess object.
|
|
SqliteDataAccess SqliteDataAccess = new SqliteDataAccess();
|
|
|
|
public Form1()
|
|
{
|
|
InitializeComponent();
|
|
}
|
|
|
|
//Needed to populate the pre-filled comboboxes. Do not remove.
|
|
private void Form1_Load(object sender, EventArgs e)
|
|
{
|
|
FillComboBoxes();
|
|
}
|
|
|
|
/* Fills program and school combo boxes on the form
|
|
* with the static data from the sqlite db.
|
|
* input: no input
|
|
* output: void
|
|
*/
|
|
private void FillComboBoxes()
|
|
{
|
|
List<Program> programs = new List<Program>();
|
|
programs = SqliteDataAccess.GetPrograms();
|
|
|
|
foreach (Program p in programs)
|
|
{
|
|
programComboBox.Items.Add(p.name);
|
|
}
|
|
|
|
List<School> schools = new List<School>();
|
|
schools = SqliteDataAccess.GetSchools();
|
|
foreach (School s in schools)
|
|
{
|
|
schoolComboBox.Items.Add(s.name);
|
|
}
|
|
}
|
|
|
|
//TODO: implement
|
|
/* On click of the picture box, open a file picker dialog box
|
|
* for the user to browse/choose a picture.
|
|
* Once the user clicks OK, the _FileOK event below fires,
|
|
* validates the input. If file is OK, then read the file from disk,
|
|
* convert it into an Image, and save it to the student picture box.
|
|
*/
|
|
private void student_pictureBox_DoubleClick(object sender, EventArgs e)
|
|
{
|
|
MessageBox.Show("A file picker dialog will show and the user can choose a picture to upload\nIt's not done yet.");
|
|
return;
|
|
|
|
DialogResult dr = pic_openFileDialog.ShowDialog();
|
|
if(dr == DialogResult.OK)
|
|
{
|
|
//Place in dicpic box
|
|
|
|
//change tag of pic box to something else
|
|
student_pictureBox.Tag = "dickpic";
|
|
}
|
|
}
|
|
|
|
//TODO: Test
|
|
/* Event that occurs when the user clicks Open in the file dialog box.
|
|
* Check to see if the photo size is too large for the database.
|
|
* If so, reject the file and inform the user.
|
|
*/
|
|
private void pic_openFileDialog_FileOk(object sender, CancelEventArgs e)
|
|
{
|
|
byte[] dickpic;
|
|
|
|
//Chunk file into bytes.
|
|
//If file > 2147483647 bytes long, reject file.
|
|
dickpic = ObjectToByteArray(student_pictureBox.Image);
|
|
if (dickpic.Length >= 2147483647) //THAT'S WHAT SHE SAID
|
|
{
|
|
MessageBox.Show("The selected student photo size is too large. Choose a smaller photo size or shrink the photo.");
|
|
return;
|
|
}
|
|
}
|
|
|
|
/* Event that fires when user clicks the save button or
|
|
* chooses File > Save Student.
|
|
*
|
|
* Saves student to database.
|
|
* If existing student, update student.
|
|
* If new student, insert student.
|
|
*/
|
|
private void save_button_Click(object sender, EventArgs e)
|
|
{
|
|
byte[] dickpic;
|
|
|
|
//Perform sanity check, ensure all data is filled except picture
|
|
|
|
// Check to see if any control is in error.
|
|
foreach (Control c in errorProvider1.ContainerControl.Controls)
|
|
{
|
|
if (errorProvider1.GetError(c) != "")
|
|
{
|
|
MessageBox.Show("Please fix the errors on the form!");
|
|
return;
|
|
}
|
|
}
|
|
|
|
//TODO: Add Check - if pic is not default pic,
|
|
//Validate the picture is under 2147483647 characters in length.
|
|
dickpic = ObjectToByteArray(student_pictureBox.Image);
|
|
if(dickpic.Length >= 2147483647) //THAT'S WHAT SHE SAID
|
|
{
|
|
MessageBox.Show("The selected student photo size is too large. Choose a smaller photo size or shrink the photo.");
|
|
return;
|
|
}
|
|
|
|
//Collect form data in student object
|
|
Student student = new Student();
|
|
student.student_id = student_idTextBox.Text;
|
|
student.FirstName = firstNameTextBox.Text;
|
|
student.LastName = lastNameTextBox.Text;
|
|
student.DOB = dob_dateTimePicker.Value.ToString();
|
|
student.gender = genderComboBox.Text;
|
|
student.address = addressTextBox.Text;
|
|
student.program_id = programComboBox.SelectedIndex + 1;
|
|
student.school_id = schoolComboBox.SelectedIndex + 1;
|
|
student.GradeLevel = gradeLevelComboBox.Text;
|
|
|
|
if (student_pictureBox.Tag.ToString() == "DefaultImage")
|
|
student.photo = null;
|
|
else
|
|
student.photo = ObjectToByteArray(student_pictureBox.Image);
|
|
|
|
//collect parent
|
|
Parent parent = new Parent();
|
|
parent.LastName = parent_lastNameTextBox.Text;
|
|
parent.FirstName = parent_firstNameTextBox.Text;
|
|
parent.PhoneNumber = long.Parse(phoneNumberNumericUpDown.Value.ToString());
|
|
parent.EmailAddress = emailAddressTextBox.Text;
|
|
|
|
//save data to sqldb.
|
|
//update existing student.
|
|
if(SqliteDataAccess.GetStudentByDbID(Int32.Parse(studentDbID.Value.ToString())) != null)
|
|
{
|
|
student.id = Int32.Parse(studentDbID.Value.ToString());
|
|
parent.parent_id = Int32.Parse(parentDbID.Value.ToString());
|
|
|
|
SqliteDataAccess.UpdateStudent(student);
|
|
SqliteDataAccess.UpdateParent(parent);
|
|
MessageBox.Show("Student and parent successfully updated.");
|
|
}
|
|
else //new student
|
|
{
|
|
student.parent_id = SqliteDataAccess.InsertNewParent(parent);
|
|
SqliteDataAccess.InsertNewStudent(student);
|
|
MessageBox.Show("Student successfully added");
|
|
ClearForm();
|
|
}
|
|
}
|
|
|
|
/* Convert student photo to byte array for saving to db.
|
|
* Also used to see if the photo is too large for the db blob type.
|
|
* INPUT: Object
|
|
* OUPUT byte[] array
|
|
*/
|
|
private byte[] ObjectToByteArray(Object obj)
|
|
{
|
|
if (obj == null)
|
|
return null;
|
|
|
|
BinaryFormatter bf = new BinaryFormatter();
|
|
MemoryStream ms = new MemoryStream();
|
|
bf.Serialize(ms, obj);
|
|
|
|
return ms.ToArray();
|
|
}
|
|
|
|
/* Convert student photo from byte array to object.
|
|
* Convert a byte array to an Object
|
|
* INPUT: byte[] array
|
|
* OUTPUT: object
|
|
*/
|
|
private Object ByteArrayToObject(byte[] arrBytes)
|
|
|
|
{
|
|
MemoryStream memStream = new MemoryStream();
|
|
BinaryFormatter binForm = new BinaryFormatter();
|
|
|
|
memStream.Write(arrBytes, 0, arrBytes.Length);
|
|
memStream.Seek(0, SeekOrigin.Begin);
|
|
Object obj = (Object)binForm.Deserialize(memStream);
|
|
|
|
return obj;
|
|
}
|
|
|
|
// Basic input validation on a string given a textbox control
|
|
// ensures only values a-z, with length two or greater.
|
|
private void String_TextBox_Validating(object sender, CancelEventArgs e)
|
|
{
|
|
TextBox tb = (TextBox)sender;
|
|
if (!Regex.IsMatch(tb.Text, "[A-Za-z]{2,}"))
|
|
errorProvider1.SetError(tb, "Enter a value a-z only of length two or longer.");
|
|
else
|
|
errorProvider1.SetError(tb, "");
|
|
}
|
|
|
|
//Validates any given combobox to ensure an item is selected.
|
|
private void ComboBox_Validating(object sender, CancelEventArgs e)
|
|
{
|
|
ComboBox cb = (ComboBox)sender;
|
|
|
|
if (cb.SelectedIndex < 0)
|
|
{ errorProvider1.SetError(cb, "Select an item."); }
|
|
else
|
|
{ errorProvider1.SetError(cb, ""); }
|
|
}
|
|
|
|
/* Does not really check to see if it is a valid phone number
|
|
* since the phone companies introduce new area codes all the damn time.
|
|
* Only checks to see the most likely case that the user of the program
|
|
* has left the phone number value as the default minimum value.
|
|
*/
|
|
private void phoneNumberNumericUpDown_Validating(object sender, CancelEventArgs e)
|
|
{
|
|
if(phoneNumberNumericUpDown.Value == phoneNumberNumericUpDown.Minimum
|
|
|| phoneNumberNumericUpDown.Value == phoneNumberNumericUpDown.Maximum)
|
|
{ errorProvider1.SetError(phoneNumberNumericUpDown, "Type in a valid phone number"); }
|
|
else
|
|
{ errorProvider1.SetError(phoneNumberNumericUpDown, ""); }
|
|
}
|
|
|
|
/* Checks to see if provided email address is valid
|
|
* by matching against an email address regex i stole from the interweb.
|
|
*/
|
|
private void emailAddressTextBox_Validating(object sender, CancelEventArgs e)
|
|
{
|
|
//email address regex
|
|
//^[a-zA-Z0-9._-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,5}$
|
|
if (!Regex.IsMatch(emailAddressTextBox.Text, @"^[a-zA-Z0-9._-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,5}$"))
|
|
errorProvider1.SetError(emailAddressTextBox, "Enter a valid email address: user@domain.com");
|
|
else
|
|
errorProvider1.SetError(emailAddressTextBox, "");
|
|
}
|
|
|
|
/* Validation of DOB Date Time Picker does not really do any validation
|
|
* other than check the basic, most probable case of the date is still today.
|
|
* Yes, the Student can literally be born yesterday, and in 12th grade.
|
|
* Paradox, ahoy!
|
|
*/
|
|
private void dob_dateTimePicker_Validating(object sender, CancelEventArgs e)
|
|
{
|
|
if(dob_dateTimePicker.Value == DateTime.Now)
|
|
{ errorProvider1.SetError(dob_dateTimePicker, "Please choose DOB."); }
|
|
else
|
|
{ errorProvider1.SetError(dob_dateTimePicker, ""); }
|
|
}
|
|
|
|
//Since not sure if student ID textbox can contain numbers
|
|
//this is seperate from generic textbox which is only letters.
|
|
private void student_idTextBox_Validating(object sender, CancelEventArgs e)
|
|
{
|
|
TextBox tb = (TextBox)sender;
|
|
if (!Regex.IsMatch(tb.Text, "[A-Za-z0-9]{2,}"))
|
|
errorProvider1.SetError(tb, "Enter a value a-z0-9 only of length two or longer.");
|
|
else
|
|
errorProvider1.SetError(tb, "");
|
|
|
|
}
|
|
|
|
//TODO: Remove Chaos Monkey button prior to release
|
|
private void btnChaosMonkey_Click(object sender, EventArgs e)
|
|
{
|
|
MessageBox.Show("CHAOS MONKEY");
|
|
return;
|
|
}
|
|
|
|
//Button to clear the form, exactly as it says on the tin.
|
|
private void clear_button_Click(object sender, EventArgs e)
|
|
{
|
|
ClearForm();
|
|
}
|
|
|
|
/* clears the form
|
|
* Does this by looping through all the controls and setting them to null
|
|
* or to default values. Be sure to set student textbox tag to DefaultImage.
|
|
*/
|
|
private void ClearForm()
|
|
{
|
|
foreach(Control c in panel1.Controls)
|
|
{
|
|
if (c is TextBox)
|
|
{
|
|
TextBox tb = (TextBox)c;
|
|
tb.Text = null;
|
|
}
|
|
|
|
if(c is ComboBox)
|
|
{
|
|
ComboBox cb = (ComboBox)c;
|
|
cb.SelectedIndex = -1;
|
|
}
|
|
|
|
if(c is DateTimePicker)
|
|
{
|
|
DateTimePicker dtp = (DateTimePicker)c;
|
|
dtp.Value = DateTime.Today;
|
|
}
|
|
|
|
if(c is NumericUpDown)
|
|
{
|
|
NumericUpDown nud = (NumericUpDown)c;
|
|
nud.Value = nud.Minimum;
|
|
}
|
|
|
|
//Be sure to set the student textbox tag to DefaultImage
|
|
//or you will be in for a really bad time.
|
|
if(c is PictureBox)
|
|
{
|
|
PictureBox pb = (PictureBox)c;
|
|
pb.Image = Properties.Resources.student;
|
|
pb.Tag = "DefaultImage";
|
|
}
|
|
} // foreach
|
|
|
|
//clear dbid values
|
|
studentDbID.Value = 0;
|
|
parentDbID.Value = 0;
|
|
|
|
//clear parent data
|
|
parent_lastNameTextBox.Text = string.Empty;
|
|
parent_firstNameTextBox.Text = string.Empty;
|
|
phoneNumberNumericUpDown.Value = phoneNumberNumericUpDown.Minimum;
|
|
emailAddressTextBox.Text = string.Empty;
|
|
|
|
ClearFoundStudentsComboBox();
|
|
ClearFormErrors();
|
|
} //ClearForm()
|
|
|
|
/* Clears form errors out of the errorprovider.
|
|
* INPUTS: no args
|
|
* OUTPUT: void
|
|
*/
|
|
private void ClearFormErrors()
|
|
{
|
|
//clear form errors
|
|
foreach (Control c in errorProvider1.ContainerControl.Controls)
|
|
{
|
|
errorProvider1.SetError(c, "");
|
|
}
|
|
}
|
|
|
|
//Clears the found student combobox.
|
|
private void ClearFoundStudentsComboBox()
|
|
{
|
|
foundStudents_comboBox.DataSource = null;
|
|
foundStudents_comboBox.Items.Clear();
|
|
foundStudents_comboBox.Text = string.Empty;
|
|
foundStudents_comboBox.SelectedIndex = -1;
|
|
}
|
|
|
|
/* Implements main search functionality by taking in whatever
|
|
* is typed in the search box. If the enter key is pressed,
|
|
* will execute search of the db based on last name and return
|
|
* found results or not found to the foundStudents combobox.
|
|
*/
|
|
private void search_textBox_KeyDown(object sender, KeyEventArgs e)
|
|
{
|
|
List<Student> foundStudents = new List<Student>();
|
|
|
|
if (e.KeyCode == Keys.Enter)
|
|
{
|
|
ClearFoundStudentsComboBox();
|
|
|
|
foundStudents = SqliteDataAccess.FindStudentByLastname(search_textBox.Text);
|
|
|
|
foundStudents_comboBox.DataSource = foundStudents;
|
|
foundStudents_comboBox.DisplayMember = "DisplayName";
|
|
foundStudents_comboBox.Text = (foundStudents.Count > 0) ? "Students found, click here" : "No students found";
|
|
}
|
|
}
|
|
|
|
//Do nothing if the user tries to type in the found student combobox.
|
|
private void foundStudents_comboBox_KeyDown(object sender, KeyEventArgs e)
|
|
{
|
|
e.SuppressKeyPress = true;
|
|
}
|
|
|
|
/* When the user clicks on a found student,
|
|
* automatically populate the form based on the found student
|
|
* and that student's parent.
|
|
*/
|
|
private void foundStudents_comboBox_SelectionChangeCommitted(object sender, EventArgs e)
|
|
{
|
|
Student foundStudent = foundStudents_comboBox.SelectedItem as Student;
|
|
ClearForm();
|
|
|
|
//The found students combo box is not completely
|
|
//cleared upon clearform. Do this now.
|
|
//ClearFoundStudentsComboBox();
|
|
|
|
//Populate the form based on the found student.
|
|
studentDbID.Value = foundStudent.id;
|
|
lastNameTextBox.Text = foundStudent.LastName;
|
|
firstNameTextBox.Text = foundStudent.FirstName;
|
|
programComboBox.SelectedIndex = foundStudent.program_id;
|
|
schoolComboBox.SelectedIndex = foundStudent.school_id;
|
|
addressTextBox.Text = foundStudent.address;
|
|
student_idTextBox.Text = foundStudent.student_id;
|
|
dob_dateTimePicker.Value = DateTime.Parse(foundStudent.DOB);
|
|
|
|
//May need to select the index on these comboboxes.
|
|
genderComboBox.Text = foundStudent.gender;
|
|
gradeLevelComboBox.Text = foundStudent.GradeLevel;
|
|
|
|
//TODO: Test
|
|
student_pictureBox.Image = (foundStudent.photo != null) ? (Image)ByteArrayToObject(foundStudent.photo) : Properties.Resources.student;
|
|
|
|
//Now populate the parent.
|
|
Parent stuParent = new Parent();
|
|
stuParent = SqliteDataAccess.GetParentByID(foundStudent.parent_id);
|
|
|
|
parent_lastNameTextBox.Text = stuParent.LastName;
|
|
parent_firstNameTextBox.Text = stuParent.FirstName;
|
|
phoneNumberNumericUpDown.Value = stuParent.PhoneNumber;
|
|
emailAddressTextBox.Text = stuParent.EmailAddress;
|
|
parentDbID.Value = stuParent.parent_id;
|
|
|
|
//Clear form errors, if any.
|
|
ClearFormErrors();
|
|
|
|
} //foundStudents_comboBox_SelectionChangeCommitted
|
|
|
|
/* Deletes a student.
|
|
* Note that you must FIRST, FIND the student, then click on them
|
|
* in the drop-down menu to have the form populate, then this
|
|
* function will work to delete both the student and the parent
|
|
* from the backend database.
|
|
*/
|
|
private void delete_button_Click(object sender, EventArgs e)
|
|
{
|
|
if (studentDbID.Value <= 0)
|
|
return;
|
|
|
|
//collect up the student and parent.
|
|
Student stu = SqliteDataAccess.GetStudentByDbID(Int32.Parse(studentDbID.Value.ToString()));
|
|
Parent par = SqliteDataAccess.GetParentByID(stu.parent_id);
|
|
|
|
//Nerf 'em.
|
|
SqliteDataAccess.DeleteStudent(stu);
|
|
SqliteDataAccess.DeleteParent(par);
|
|
|
|
//Dispose of the objects.
|
|
ClearForm();
|
|
MessageBox.Show("Student deleted from database.");
|
|
}
|
|
|
|
#region redundancy
|
|
/* This whole region of code simply implements the File menu
|
|
* which only calls the previously programmed delegates for saving a student,
|
|
* or creating a new student. Henceforth, I present to you the redundancy region.
|
|
*/
|
|
private void exitWithoutSavingToolStripMenuItem_Click(object sender, EventArgs e)
|
|
{
|
|
Environment.Exit(0);
|
|
}
|
|
|
|
private void exitsavesDataToolStripMenuItem_Click(object sender, EventArgs e)
|
|
{
|
|
//Error handling happens on the save_button_Click event, so no need to do it here.
|
|
save_button_Click(save_button, EventArgs.Empty);
|
|
|
|
//If there's an error, oh well, we tried, exit anyway.
|
|
Environment.Exit(0);
|
|
}
|
|
#endregion
|
|
|
|
private void studentBySchoolToolStripMenuItem_Click(object sender, EventArgs e)
|
|
{
|
|
MessageBox.Show("This function will generate a report of students by school.\nIt's not done yet.");
|
|
return;
|
|
}
|
|
|
|
private void studentsByProgramToolStripMenuItem_Click(object sender, EventArgs e)
|
|
{
|
|
MessageBox.Show("This function will generate a report of students by program.\nIt's not done yet.");
|
|
return;
|
|
}
|
|
} //Form1
|
|
}
|