Cook up a Storm with WinUI 3.0

Learning the Language: XAML and C#

To begin, let's talk about XAML, or eXtensible Application Markup Language. In the world of WinUI 3.0, XAML is like our recipe book. It tells our application, or in our analogy, our dish, what ingredients to use and how to arrange them.

In the XAML file, we define and arrange our controls, which are like our ingredients. A Button, a TextBox, an Image, these are all controls. And just like ingredients, controls give our app its shape and flavor. Without them, our app wouldn't have any substance.

Now, let's get our hands dirty and add a control to our app. For this example, we'll be using a Button control. Adding a button is as easy as adding salt to your dish. We just need to specify it in our XAML recipe. This is how we do it:

XAML
<Button x:Name="myButton" Content="Water Me!" Click="myButton_Click"></Button>

This line of code adds a button to our application that, when clicked, will call a method named "myButton_Click". This method will be the brains behind our button, telling it what to do when it's clicked. But don't worry, we'll get to that soon!

Our Button control has three parts: x:Name is its unique identifier, similar to a person's name. In programming, we give controls like buttons a name so we can talk about them in the code and the code understand which button we mean. Content holds the text displayed on the button. We use Content instead of placing text between tags so we can change it easily later on. Lastly, Click determines the action when the button is clicked.

Our First C# Code

Imagine, if you will, that we've stepped into a new land where people speak a different language. The words, the grammar, it's all new. This is C#, the language of our app. Don't worry, it's friendlier than you might think.

New Project Comic

C# (pronounced C Sharp) is like the recipe we follow when cooking a meal in our Visual Studio "kitchen". It tells the ingredients (our WinUI 3.0 components) what to do and when to do it. Let's see it in action.

To find the C# code, go to the Solution Explorer again and click the little arrow next to MainWindow.xaml. This shows you the code file related to the xaml-file called MainWindow.xaml.cs. Double-click it to open.

winui-3-basics-3-code-file

Our project's default button is a bit like a pancake that simply tells us, "Hey, I've been flipped," each time we click it. Here's how:

C#
private void myButton_Click(object sender, RoutedEventArgs e)
{
    myButton.Content = "Clicked";
}

Here you can see how all the bits connect to the different parts of the XAML Button control. On Click, it changes its Content (what's written on it) to "Clicked". In technical terms, the "=" symbol means that we are placing the "Clicked" text inside the Content. You can change the text to something else to test it if you'd like.

In programming, a method (also called a function) is like a mini-program inside a program. It's a collection of your orders grouped together to make the program do something. If baking a cake was a method, it would go: gather the ingredients, mix them together, throw it in the oven.

Here's the structure of an example method:

C#
public void MyMethodName(parameter1, parameter2)
{
    // Some code here...
}
  • public is like a permission slip. It tells the computer who is allowed use this method. Public means that anyone can use the method, but we might want to limit this in some cases.
  • void tells you what the method gives back when it finishes its work. Void means it doesn't give anything back. It just does its thing and shuts up.
  • MyMethodName is like giving a name to a pet. When we want to ask the method to do its job, we call its name.
  • (parameter1, parameter2) are like ingredients for a recipe. They are the things the method needs to do its job. A method can require none, one, or many ingredients. It's up to us to give them to it.

Methods are beneficial because they allow us to reuse code without writing the same thing over and over, like a chef reusing a favorite recipe. It also makes our code more organized, easier to understand, and easier to maintain.

Now, our Plant Nanny app needs more than just telling a button to change its content text. We want our app to remind us to water our plants. So, we modify the method to:

C#
private void myButton_Click(object sender, RoutedEventArgs e)
{
    ShowToastNotification("Watering Reminder", "It's time to water your plants.");
}

The idea is that when we click the button, it calls ShowToastNotification, a method that sends us a notification β€” a friendly nudge to hydrate our green friends.

The Toasty Helper

You might wonder, what's a toast notification? Well, it's not about breakfast, but it does pop up, much like a slice of toast from a toaster. It's a message that appears on your screen, gives you some information, and then disappears after a while.

winui-3-basics-3-toast-notification

So, when we click our button, we want a toast notification to pop up and remind us to water our plants. A little like having a friendly garden gnome whispering in your ear, "Hey, it's time to water the ferns."

A Little Snag

But hold on! Now we can't run the application anymore. If we try, we're met with a nasty FAILED note in our Output window.

winui-3-basics-3-build-output-failed

You might also notice that the ShowToastNotification code is underlined with red.

winui-3-basics-3-squiggly-line

This doesn't seem good at all. Let's turn out eyes to the Error List at the bottom left corner to see what's up.

winui-3-basics-3-error-list

Visual Studio is telling us: "Error CS0103 The name 'ShowToastNotification' does not exist in the current context". You can also double-click the error for Visual Studio to highlight the line that's giving it trouble. We're getting the error because our button is trying to call a method - ShowToastNotification - that we haven't created yet. It's like asking someone to pass the syrup when we haven't yet put it on the table.

Let's Bring the Syrup!

So, let's create our method, ShowToastNotification. It can be named anything, but a descriptive name goes a long way in programming. This method will create and show our toast notification. It needs two bits of information to do its job: a title and content for the notification. So we set it up to receive these as parameters. As luck would have it, we already pass the method these two pieces of text where we set up the line of code calling it.

Now, place the method in the code after the closing curly bracket of the myButton_Click method to create a new block, like so...

C#
private void myButton_Click(object sender, RoutedEventArgs e)
{
    ShowToastNotification("Watering Reminder", "It's time to water your plants.");
}

public void ShowToastNotification(string title, string content)
{
}

With our method ready, we move onto creating the notification. We start by getting an XML template. This template tells our program what the toast should look like, including its layout, text, and other visual elements. It's like a recipe for creating a specific style of toast notification.

C#
public void ShowToastNotification(string title, string content)
{
    XmlDocument toastXml = ToastNotificationManager.GetTemplateContent(ToastTemplateType.ToastText02);
}

In the code we are calling another method called "GetTemplateContent". This method is part of a package called ToastNotificationManager. We use it to retrieve the XML template for a specific type of toast. You notice that the method requires a parameter, an ingredient. With the parameter, we tell the method that we want a template called "ToastText02". The "ToastText02" type gives us a layout for a toast notification with two lines of text.

The line of code put the retrieved XML template to a box named "toastXml" so that we can later use it to customize and show the toast notification. The "toastXml" box has a special type XmlDocument, which just means that it's able to handle the XML template we want to put inside it.

Next, we fill in our toast's title and content. We find the elements labeled "text" in our template and replace them with our provided title and content. It's a little like filling in the blanks in a recipe.

C#
XmlNodeList stringElements = toastXml.GetElementsByTagName("text");
stringElements[0].AppendChild(toastXml.CreateTextNode(title));
stringElements[1].AppendChild(toastXml.CreateTextNode(content));

At this point, Visual Studio should be bugging you about a couple of errors.

winui-3-basics-3-more-error

It's not able to find XmlDocument or XmlNodeList. Do we need to create those as well? Not so fast. These packages do ship with Windows 10 SDK (which you might remember us installing), but we have to tell Visual Studio to use them.

The code is already telling Visual Studio to use quite a bit of libraries. If you scroll all the way up, you can see a whole load of lines starting with using. Go ahead and add this line at the end of the list:

C#
using Windows.Data.Xml.Dom;

Windows.Data.Xml.Dom gives us access to the Xml types we need in our code. And as you can see, the errors disappear.

C#
ToastNotification toast = new ToastNotification(toastXml);

In this next line of code, we are creating a new object called "toast" of type "ToastNotification". We are using the XML template we previously defined (stored in the variable "toastXml") as the input to create this new toast notification.

By creating a new object, we can customize and control this specific toast notification separately from other notifications. It allows us to set additional properties, such as the timing, appearance, and behavior of the toast, before we show it on the screen.

Think of it like making a copy of a recipe (XML template) and using that copy to bake a cake (toast notification) with specific flavors and decorations.

Finally, after all the preparation, we need to tell the method to show the notification.

C#
ToastNotificationManager.CreateToastNotifier().Show(toast);

The ToastNotificationManager is like a manager who handles all the toast notifications. The CreateToastNotifier() method creates a notifier specifically for showing these notifications. Then, the Show() method is called on that notifier, and it takes the prepared toast as a parameter. This action triggers the notification to appear on the screen, allowing the user to see the reminder contained within the toast.

Now, if you run the app and push the button, you'll see a notification to water your plants popping up on Windows. Of course, pushing a button to get a notification won't really help you remember things, but when we bind the notification to a timer, it'll be a different story.