Note
Access to this page requires authorization. You can try signing in or changing directories.
Access to this page requires authorization. You can try changing directories.
Now you'll create a page that allows a user to edit a note, and then you'll write the code to save or delete the note.
Tip
You can download or view the code for this tutorial from the GitHub repo. To see the code as it is in this step, see this commit: note page - initial.
First, add the new page to the project:
In the Solution Explorer pane of Visual Studio, right-click on the WinUINotes project > Add > New Item....
In the Add New Item dialog, select WinUI in the template list on the left-side of the window. Next, select the Blank Page (WinUI 3) template. Name the file NotePage.xaml, and then select Add.
The NotePage.xaml file will open in a new tab, displaying all of the XAML markup that represents the UI of the page. Replace the
<Grid> ... </Grid>element in the XAML with the following markup:<Grid Padding="16" RowSpacing="8"> <Grid.RowDefinitions> <RowDefinition Height="*"/> <RowDefinition Height="Auto"/> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition Width="*"/> <ColumnDefinition Width="400"/> <ColumnDefinition Width="*"/> </Grid.ColumnDefinitions> <TextBox x:Name="NoteEditor" AcceptsReturn="True" TextWrapping="Wrap" PlaceholderText="Enter your note" Header="New note" ScrollViewer.VerticalScrollBarVisibility="Auto" Width="400" Grid.Column="1"/> <StackPanel Orientation="Horizontal" HorizontalAlignment="Right" Spacing="4" Grid.Row="1" Grid.Column="1"> <Button Content="Save" Style="{StaticResource AccentButtonStyle}"/> <Button Content="Delete"/> </StackPanel> </Grid>Save the file by pressing CTRL + S, clicking the Save icon in the tool bar, or by selecting the menu File > Save NotePage.xaml.
If you run the app right now, you won't see the note page you just created. That's because you still need to set it as the content of the
Framecontrol inMainWindow.Open MainWindow.xaml and set
NotePageas the SourcePageType on theFrame, like this:<Frame x:Name="rootFrame" Grid.Row="1" SourcePageType="local:NotePage"/>Now when you run the app, the
Framewill load an instance ofNotePageand show it to the user.
Important
XAML namespace (xmlns) mappings are the XAML counterpart to the C# using statement. local: is a prefix that is mapped for you within the XAML pages for your app project (xmlns:local="using:WinUINotes"). It's mapped to refer to the same namespace that's created to contain the x:Class attribute and code for all the XAML files including App.xaml. As long as you define any custom classes you want to use in XAML in this same namespace, you can use the local: prefix to refer to your custom types in XAML.
Let's break down the key parts of the XAML controls placed on the page:
The Grid.RowDefinitions and Grid.ColumnDefinitions define a grid with 2 rows and 3 columns (placed below the title bar).
- The bottom row is automatically (
Auto) sized to fit its content, the two buttons. The top row uses all the remaining vertical space (*). - The middle column is
400epx wide and is where the note editor goes. The columns on either side are empty and split all the remaining horizontal space between them (*).
Note
Because of how the scaling system works, when you design your XAML app, you're designing in effective pixels, not actual physical pixels. Effective pixels (epx) are a virtual unit of measurement, and they're used to express layout dimensions and spacing, independent of screen density.
- The bottom row is automatically (
<TextBox x:Name="NoteEditor" ... > ... </TextBox>is a text entry control (TextBox) configured for multi-line text entry, and is placed in the top center cell of theGrid(Grid.Column="1"). Row and column indexes are 0-based, and by default, controls are placed in row 0 and column 0 of the parentGrid. So this is the equivalent of specifying Row 0, Column 1.<StackPanel Orientation="Horizontal" ... > ... </StackPanel>defines a layout control (StackPanel) that stacks its children either vertically (default) or horizontally. It's placed in the bottom center cell of theGrid(Grid.Row="1" Grid.Column="1").Note
Grid.Row="1" Grid.Column="1"is an example of XAML attached properties. Attached properties let one XAML object set a property that belongs to a different XAML object. Often, as in this case, child elements can use attached properties to inform their parent element of how they are to be presented in the UI.Two
<Button>controls are inside the<StackPanel>and arranged horizontally. You'll add the code to handle the buttons' Click events in the next section.
Learn more in the docs:
Load and save a note
Open the NotePage.xaml.cs code-behind file. When you add a new XAML file, the code-behind contains a single line in the constructor, a call to the InitializeComponent method:
namespace WinUINotes
{
public sealed partial class NotePage : Page
{
public NotePage()
{
this.InitializeComponent();
}
}
}
The InitializeComponent method reads the XAML markup and initializes all of the objects defined by the markup. The objects are connected in their parent-child relationships, and the event handlers defined in code are attached to events set in the XAML.
Now you're going to add code to the NotePage.xaml.cs code-behind file to handle loading and saving notes.
Add the following variable declarations to the
NotePageclass:public sealed partial class NotePage : Page { private StorageFolder storageFolder = ApplicationData.Current.LocalFolder; private StorageFile? noteFile = null; private string fileName = "note.txt";When a note is saved, it's saved to the app's local storage as a text file.
You use the StorageFolder class to access the app's local data folder. This folder is specific to your app, so notes saved here can't be accessed by other apps. You use the StorageFile class to access the text file saved in this folder. The name of the file is represented by the
fileNamevariable. For now, setfileNameto "note.txt".Create an event handler for the note page's Loaded event.
public NotePage() { this.InitializeComponent(); // ↓ Add this. ↓ Loaded += NotePage_Loaded; } // ↓ Add this event handler method. ↓ private async void NotePage_Loaded(object sender, RoutedEventArgs e) { noteFile = (StorageFile)await storageFolder.TryGetItemAsync(fileName); if (noteFile is not null) { NoteEditor.Text = await FileIO.ReadTextAsync(noteFile); } }In this method, you call TryGetItemAsync to retrieve the text file from the folder. If the file doesn't exist, it returns
null. If the file does exist, call ReadTextAsync to read the text from the file into theNoteEditorcontrol's Text property. (Remember,NoteEditoris theTextBoxcontrol you created in the XAML file. You reference it here in your code-behind file using thex:Nameyou assigned to it.)Important
You need to mark this method with the
asynckeyword because the file access calls are asynchronous. In short, if you call a method that ends in...Async(likeTryGetItemAsync), you can add the await operator to the call. This keeps subsequent code from executing until the awaited call completes and keeps your UI responsive. When you useawait, the method that you're calling from needs to be marked with the async keyword. For more info, see Call asynchronous APIs in C#.
Learn more in the docs:
Add event handlers
Next, add the Click event handlers for the for the Save and Delete buttons. Adding event handlers is something that you'll do often while creating your apps, so Visual Studio provides several features to make it easier.
In the NotePage.xaml file, place your cursor after the
Contentattribute in the SaveButtoncontrol. TypeClick=. At this point, Visual Studio should pop up an auto-complete UI that looks like this:
- Press the down-arrow key to select <New Event Handler>, then press Tab. Visual Studio will complete the attribute with
Click="Button_Click"and add an event handler method namedButton_Clickin the NotePage.xaml.cs code-behind file.
Now, you should rename the
Button_Clickmethod to something more meaningful. You'll do that in the following steps.- Press the down-arrow key to select <New Event Handler>, then press Tab. Visual Studio will complete the attribute with
In NotePage.xaml.cs, find the method that was added for you:
private void Button_Click(object sender, RoutedEventArgs e) { }Tip
To locate code in your app, click Search in the Visual Studio title bar and use the Code Search option. Double-click the search result to open the code in the code editor.
Place your cursor before the "B" in
Buttonand typeSave. Wait a moment, and the method name will be highlighted in green.When you hover over the method name, Visual Studio will show a tooltip with a screwdriver or lightbulb icon. Click the down-arrow button next to the icon, then click Rename 'Button_Click' to 'SaveButton_Click'.
Visual Studio will rename the method everywhere in your app, including in the XAML file where you first added it to the
Button.Repeat these steps for the Delete button, and rename the method to
DeleteButton_Click.
Now that the event handlers are hooked up, you can add the code to save and delete the note file.
Add this code in the
SaveButton_Clickmethod to save the file. Notice that you also need to update the method signature with theasynckeyword.private async void SaveButton_Click(object sender, RoutedEventArgs e) { if (noteFile is null) { noteFile = await storageFolder.CreateFileAsync(fileName, CreationCollisionOption.ReplaceExisting); } await FileIO.WriteTextAsync(noteFile, NoteEditor.Text); }In the
SaveButton_Clickmethod, you first check to see ifnoteFilehas been created. If it'snull, then you have to create a new file in the local storage folder with the name represented by thefileNamevariable, and assign the file to thenoteFilevariable. Then, you write the text in theTextBoxcontrol to the file represented bynoteFile.Add this code in the
DeleteButton_Clickmethod to delete the file. You need to update the method signature with theasynckeyword here, too.private async void DeleteButton_Click(object sender, RoutedEventArgs e) { if (noteFile is not null) { await noteFile.DeleteAsync(); noteFile = null; NoteEditor.Text = string.Empty; } }In the
DeleteButton_Clickmethod, you first check to see ifnoteFileexists. If it does, delete the file represented bynoteFilefrom the local storage folder and setnoteFiletonull. Then, reset the text in theTextBoxcontrol to an empty string.Important
After the text file is deleted from the file system, it's important to set
noteFiletonull. Remember thatnoteFileis a StorageFile that provides access to the system file in your app. After the system file is deleted,noteFilestill points to where the system file was, but doesn't know that it no longer exists. If you try to read, write, or delete the system file now, you'll get an error.Save the file by pressing CTRL + S, clicking the Save icon in the tool bar, or by selecting the menu File > Save NotePage.xaml.cs.
The final code for the code-behind file should look like this:
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using System;
using Windows.Storage;
namespace WinUINotes
{
public sealed partial class NotePage : Page
{
private StorageFolder storageFolder = ApplicationData.Current.LocalFolder;
private StorageFile? noteFile = null;
private string fileName = "note.txt";
public NotePage()
{
this.InitializeComponent();
Loaded += NotePage_Loaded;
}
private async void NotePage_Loaded(object sender, RoutedEventArgs e)
{
noteFile = (StorageFile)await storageFolder.TryGetItemAsync(fileName);
if (noteFile is not null)
{
NoteEditor.Text = await FileIO.ReadTextAsync(noteFile);
}
}
private async void SaveButton_Click(object sender, RoutedEventArgs e)
{
if (noteFile is null)
{
noteFile = await storageFolder.CreateFileAsync(fileName, CreationCollisionOption.ReplaceExisting);
}
await FileIO.WriteTextAsync(noteFile, NoteEditor.Text);
}
private async void DeleteButton_Click(object sender, RoutedEventArgs e)
{
if (noteFile is not null)
{
await noteFile.DeleteAsync();
noteFile = null;
NoteEditor.Text = string.Empty;
}
}
}
}
Test the note
With this code in place, you can test the app to make sure the note saves and loads correctly.
- Build and run the project by pressing F5, clicking the Debug "Start" button in the tool bar, or by selecting the menu Run > Start Debugging.
- Type into the text entry box and press the Save button.
- Close the app, then restart it. The note you entered should be loaded from the device's storage.
- Press the Delete button.
- Close the app, restart it. You should be presented with a new blank note.
Important
After you've confirmed that saving and deleting a note works correctly, create and save a new note again. You'll want to have a saved note to test the app in later steps.
Windows developer