Lesson 8 - Birthday reminder in C# .NET WPF - Wiring layers

C# .NET WPF Birthday reminder in C# .NET WPF - Wiring layers

In the previous lesson, Birthday reminder in C# .NET WPF - Logic layer, we programmed most of our application's C# logic layer. In today's lesson, we're going to make the application runnable.

Separation of presentation and logic

Now that we have a complete application presentation layer (form) and a logic layer (classes). We must strictly separate these layers in the application or else the code would become very confusing otherwise. Never perform calculations, file operations, database queries or anything of the sort directly in the form code! Always create a class that provides the required methods and use it in the form. This way, the logic stays within the class. The class shouldn't even "know" that the form exists. It shouldn't display error messages (throwing exceptions is completely fine). The form is responsible for displaying error messages to the user. The form is the part of that application layer that communicates with the user. No other layer should do that.

If it crossed your mind that our simple calculator, which we made in a couple of lessons back, was incorrectly designed, you're right. For the sake of simplicity, we wrote calculations directly into button handling methods. We should have created a class that computed results and called its methods from the form. Both XAML and its code-behind are the application presentation layer. XAML defines what the form looks like, code-behind calls the logic that XAML does not contain.

That is why, today, we're going to demonstrate how to organize code correctly.

Wiring presentation and logic

Let's move to the code-behind of the MainWindow form, add a private attribute of the PersonManager type to the class, and initialize it with a new instance:

private PersonManager personManager = new PersonManager();

Now, a PersonManager instance is created when at startup and the form will communicate with it and perform actions accordingly.

Adding people

Now let's start adding people! First, we'll move to the PersonWindow form code where we'll store a PersonManager instance as a private class attribute as we did in the previous form. However, we won't create an instance here because all of the people would already be loaded in the PersonManager instance we added in the main form. In other words, we would be loading the same people twice (which is redundant and inefficient). What we'll do here, is pass our loaded PersonManager instance through a constructor and store in a variable:

private PersonManager personManager;

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

Now, double-click the OK button and add a person to the manager using the values filled-in by the user into the form controls, respectively. The DatePicker value can be accessed using the SelectedDate property which is of the nullable DateTime? type.

Remember how the Add() method throws an exception when the name is too short, the date is null, and when the date is in the future? Well, in order to complete the cycle, we will have to wrap the code by adding a person into the try block, followed by the catch block. If an exception occurs in the try block, the program immediately jumps to the catch block where, using a MessageBox, it displays the error message. If we didn't set things up like that, the application would terminate when any exception is thrown.

private void okButton_Click(object sender, RoutedEventArgs e)
{
        try
        {
                personManager.Add(nameTextBox.Text, birthdayDatePicker.SelectedDate);
                Close();
        }
        catch (Exception ex)
        {
                MessageBox.Show(ex.Message, "Error", MessageBoxButton.OK, MessageBoxImage.Exclamation);
        }
}

We use the Message property to access the exception message. Also, notice how we set the message box's icon to an exclamation mark. The method doesn't contain any actual logic, so the code for it is relatively short.

This form is now complete. Let's move back to the MainWindow, this time to the graphic designer. Double-click the addButton button and change its handling method to this:

private void addButton_Click(object sender, RoutedEventArgs e)
{
        PersonWindow personWindow = new PersonWindow(personManager);
        personWindow.ShowDialog();
}

Using the addButton() method, we create a new PersonWindow form instance where we will pass the local PersonManager instance. Then, we call the ShowDialog() method on the form instance, which displays a new form (as well as the Show() method) and blocks the rest of the application until the dialog is closed. In other words, we wouldn't be able to work with the main form until we confirm or cancel the dialog. Handling dialogs this way is very common. Especially, when it comes to utility forms used for entering data. In this case, we wouldn't actually mind if the user used opened another entering dialog while entering a new person.

If we run the application now, we'd be able to add people to the ObservableCollec­tion, but we wouldn't see them in the ListBox. Mainly because the ListBox isn't bound to the collection, which we will do using Bindings.

Binding

In order to bind objects, we have to set our window's context first. The DataContext property takes an object whose properties will be bound. Add the following code to your window's constructor:

DataContext = personManager;

The PersonManager instance is now the data context of the main window and is now able to display its properties.

Let's move back to the XAML and add the ItemsSource attribute to the ListBox:

<ListBox Name="personListBox" Grid.Column="0" Grid.Row="2" Margin="0, 0, 0, 10" ItemsSource="{Binding Persons}"/>

What we did here, is define the ListBox's source items as objects of the "Person" type in the context, i.e. the PersonManager.

Run the application and try adding some people. Every time you add someone, they will immediately appear in the ListBox thanks to the binding we have set. A ListBox always displays what the ToString() method of its items returns. In our case, their name is shown. Other controls of the sort, like ComboBoxes, work the same way.

Binding WPF ListBox in C# .NET

Removing users

The removeButton's handler method will look like this:

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

The most important part of the code above is the condition that determines whether an item is selected in the ListBox or not. That way, we can access the selected item through the SelectedItem property. Then we cast the item to a Person because an instance of the Person class is of the object type. Just so you know, the ListBox control isn't generic and has to be able to contain objects of any type. The person is then passed to the PersonManager's Remove() method which then physically removes the person from the ObservableCollec­tion.

Today's project is available for download below. I look forward to seeing you in the next lesson, Birthday reminder in C# .NET WPF - Bindings, when we'll continue working on Bindings.


 

Download

Downloaded 49x (192.8 kB)
Application includes source codes in language C#

 

 

Article has been written for you by David Capka
Avatar
Do you like this article?
1 votes
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.
Activities (10)

 

 

Comments

Avatar
Lennart Völler:12/22/2017 5:58

I like these WPF tutorials a lot. Let's face it. Coding for the console is nice and makes us feel all super geeky and hacker-like but apart from us coders there is nobody who want's to use that. So one has to learn to use Forms!

I have a question regarding Visual Studio and Forms. I found my Solution manager quiet messy and decided to put the forms into a folder. Interestingly this breaks the application completely. VS is not even able the debugger anymore, the app crashed before reaching the Main()-Method.

How can I fix this? There surely has to be a way to move and reorganize your application files after you created them!

 
Reply 12/22/2017 5:58
Avatar
David Capka
ICT.social team
Avatar
Replies to Lennart Völler
David Capka:12/22/2017 12:57

Hello Lennart,
generally it's not a good idea to move autogenerated content, I'd create a new one. The best way to divide your WPF app into folders is to create the folders and create new Windows in them (right click on the folder and choose "Add -> Window..."). Once you've your Windows in the folders ready, modify the App.xaml file to set the default Window in the StartupUri attribute:

<Application x:Class="WpfApp1.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:local="clr-namespace:WpfApp1"
             StartupUri="YourFolder/Window1.xaml">
    <Application.Resources>

    </Application.Resources>
</Application>

Feel free to remove the original MainWindow.xaml file later.

Reply  +1 12/22/2017 12:57
You can walk through a storm and feel the wind but you know you are not the wind.
To maintain the quality of discussion, we only allow registered members to comment. Sign in. If you're new, Sign up, it's free.

2 messages from 2 displayed.