b5530e4a28
Add function to clearformerrors and call it on clearform as well as after a student is found. Bugfix regarding update parent Show message box upon updating student and parent Properly consolidate new student toolstrip and save student toolstrip to the already written event.
496 lines
19 KiB
C#
496 lines
19 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)
|
|
{
|
|
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 this.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 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
|
|
} //Form1
|
|
}
|