Swift week Spring sales
Save up to 80 % on our Swift e-learning courses. Only this week!
Get up to 60 % extra points for free! More info

Lesson 5 - Birthday Reminder - Wiring the Presentation and Logic Layers

In the previous lesson, Birthday Reminder - Logic Layer, we programmed most of the logical layer of the birthday reminder application. In today's tutorial, we're going to make the application up and running.

Separating Presentation and Logic

Now we have completed the presentation part of the application (forms) and the logic part (classes). We strictly separate these 2 layers in all applications, otherwise the code would be very confusing. You should never perform calculations, file manipulations, database queries, and similar things directly in the form code! We always create a class that provides the appropriate methods, and only use this class from the form. Logic stays in the class. The class shouldn't know about the form at all. For example, it shouldn't display error messages, but only throw exceptions in case of error. It's then the form's responsibility to display the error to the user. The form is part of the application that communicates with the user, no other part does that.

If you're now thinking that our simple calculator we created in the first lessons of this course was poorly designed, you are right. To keep things simple, we wrote the calculations straight into the button handler method. With proper design, we'd have a class that calculates the results and we'd call it from the form.

So today we'll take a look at how it's done right.

Wiring Presentation and Logic

We'll go to the source code of the OverviewForm and add a private field of the PersonManager type. We'll also create an instance in the declaration:

private PersonManager personManager = new PersonManager();

The manager instance will be created right after the form is created, then the form will communicate with it to perform actions the user wants.

In the form constructor, we'll set the todayLabel to the current date, and set the DataSource property of the ListBox to the People BindingList of the person manager. This binds the ListBox to the BindingList, so from now on, it's going to display its contents, and if something is added into the list, it's going to be reflected in the ListBox too.

public OverviewForm()
{
    InitializeComponent();

    todayLabel.Text = DateTime.Now.ToLongDateString();
    peopleListBox.DataSource = personManager.People;
}

Adding and Removing People

To finally see something, let's move to adding people. First, we'll go to the code of the PersonForm form. As in the previous form, we'll prepare a person manager as a private field here. But if we created its instance again here, it wouldn't be very useful, since people would be loaded in the manager instance of the main form, and loading them again here would be inefficient. So we'll pass the already loaded manager through the constructor and store it in the prepared variable:

private PersonManager personManager;

public PersonForm(PersonManager personManager)
{
    InitializeComponent();
    this.personManager = personManager;
}

Now we'll double-click the OK button, and add a new person to the manager using the values the user entered into each control. We access the value of a TextBox using the Text property, and the value of a DateTimePicker using the Value property.

You certainly remember that the Add() method is going to throw an exception in case of very short name or a future date. Therefore, we'll put the code adding the person in a try block, followed by a catch block. If an exception occurs inside the try block, the program immediately moves into the catch block where it uses MessageBox to display the error message. If we didn't handle the exception this way, it'd cause the application to crash.

private void okButton_Click(object sender, EventArgs e)
{
    try
    {
        personManager.Add(nameTextBox.Text, birthdayDateTimePicker.Value);
        Close();
    }
    catch (Exception ex)
    {
        MessageBox.Show(ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
    }
}

We access the exception message via the Message property. Notice that we also set the message the exclamation mark icon. We don't place any logic in the method, so it's relatively short.

We've finished this form. Let's go back to the OverviewForm designer, then double-click the addButton and the removeButton.

private void addButton_Click(object sender, EventArgs e)
{
    PersonForm personForm = new PersonForm(personManager);
    personForm.ShowDialog();
}

In the event handler of the addButton, we create a new instance of the PersonForm form and pass it the local person manager. We then call the ShowDialog() method on the instance. This will display the new form (just like the Show() method) and will also block the rest of the application until the dialog is closed. As the result, we aren't able to work with the main form until we confirm or close the dialog. This is how dialogs (helper forms mostly used for entering data) usually work. In our case we actually wouldn't mind if the user was using the app while entering a new person and, for example, opened another input dialog.

The event handler of the removeButton will look like this:

private void removeButton_Click(object sender, EventArgs e)
{
    if (peopleListBox.SelectedItem != null)
    {
        personManager.Remove((Person)peopleListBox.SelectedItem);
    }
}

What's important is the condition that checks whether an item is selected in the ListBox. As you can see, we access the selected item using the SelectedItem property. The item is then converted to the Person type, because it's of the object type by default (that makes the ListBox universal). We pass this person to the Remove() method of the manager, which then performs the physical removal from the collection.

You can now test the app and add or remove people. Added people will appear immediately in the ListBox, thanks to the bindings. ListBox always displays what the ToString() method of the objects returns. For the people, it shows their name. If we wanted to display another property, we'd specify the property name in the DisplayMember property of the ListBox (e.g. to display birthdays, we'd set it to Birthday). Of course, other controls work similarly, like the ComboBox for instance.

Adding and removing

Nearest birthday

In the main form class, we'll add a new private helper method RefreshNearest() to refresh the label showing the nearest birthday.

private void RefreshNearest()
{
    if (personManager.People.Count > 0)
    {
        Person nearest = personManager.FindNearest();
        int age = nearest.CalculateAge();
        if (DateTime.Today != nearest.Birthday)
            age++;
        nearestLabel.Text = nearest.Name + " (" + age + " years old) in " + nearest.RemainingDays() + " days";
    }
    else
        nearestLabel.Text = "No people in list";
}

If there are people in the person manager, we find the person with the nearest birthday. We get that person's age, and if their birthday isn't today, we add one to their age to make it appear as the person's future age. Then we put the person's name into the label text, together with their future age in parentheses and how many days remain until their birthday.

If there are no people in the manager, we display a message to the user in the label.

We'll call the method at the end of the form constructor and also after adding or removing a person (at the end of the button's event handler method, but still inside the conditions, of course).

Person details

Now we only need to display the details about the selected person. We'll double-click the ListBox and Visual Studio will generate the SelectedIndexChanged event handler method (triggered by change of the selected item). Here we'll have to check again whether an item (person) is selected. If so, we'll load it and display its properties in the appropriate labels and set the MonthCalendar to its birth date.

if (peopleListBox.SelectedItem != null)
{
    Person selected = (Person)peopleListBox.SelectedItem;
    birthdayLabel.Text = selected.Birthday.ToLongDateString();
    ageLabel.Text = selected.CalculateAge().ToString();
    birthdayMonthCalendar.SelectionStart = selected.Birthday;
}

You can now try the application.

Anniversary details

In the next lesson, Birthday Reminder - Storing Data and Conclusion, we'll finish saving and loading data from / to a file. The current source code is available to download below as always. If you got stuck somewhere, try to find a mistake.


 

Download

Downloaded 1x (728.3 kB)
Application includes source codes in language C#

 

 

Article has been written for you by David Capka
Avatar
Do you like this article?
No one has rated this quite yet, be the first one!
The author is a programmer, who likes web technologies and being the lead/chief article writer at ICT.social. He shares his knowledge with the community and is always looking to improve. He believes that anyone can do what they set their mind to.
Unicorn College The author learned IT at the Unicorn College - a prestigious college providing education on IT and economics.
Previous article
Birthday Reminder - Logic Layer
All articles in this section
Form Applications in C# .NET Windows Forms
Thumbnail
Next article
Birthday Reminder - Storing Data and Conclusion
Activities (3)

 

 

Comments

To maintain the quality of discussion, we only allow registered members to comment. Sign in. If you're new, Sign up, it's free.

No one has commented yet - be the first!