CHAPTER 25

Implementing the user interface for a Universal Windows Platform app

After completing the chapter, you will be able to:

  • Describe the features of a typical Universal Windows Platform app.

  • Implement a scalable user interface for a Universal Windows Platform app that can adapt to different form factors and device orientations.

  • Create and apply styles to a Universal Windows Platform app.

Recent versions of Windows have introduced a platform for building and running highly interactive applications with continuously connected, touch-driven user interfaces and support for embedded device sensors. An updated application security and life-cycle model changed the way that users and applications work together. This platform is called the Windows Runtime (WinRT), and I have referred to it occasionally throughout this book. You can use Visual Studio to build WinRT applications that can adapt themselves to a variety of device form factors, ranging from handheld tablets to desktop PCs with large, high-resolution screens. Using Windows 8 and Visual Studio 2013, you could also publish these applications in the Windows Store as Windows Store apps.

Separately, you could use the Windows Phone SDK 8.0 (integrated into Visual Studio) to design and implement applications that run on Windows Phone 8 devices. These applications share many similarities with their tablet and desktop-oriented siblings, but they operate in a more restricted environment, typically with fewer resources and a requirement to support a different user interface layout. Consequently, Windows Phone 8 applications use a different version of the WinRT, called the Windows Phone Runtime, and you can market Windows Phone 8 applications as Windows Phone Store apps. You could create a class library with which to share application and business logic between a Windows tablet/desktop application and a Windows Phone 8 application by using the Portable Class Library template in Visual Studio, but Windows Store apps and Windows Phone Store apps are distinct beasts with differences in the features that they can make available.

Subsequently, Microsoft sought to converge these platforms and reduce the number of differences. This strategy has culminated in Windows 10 with Universal Windows Platform apps. A Universal Windows Platform app uses an amended version of WinRT called the Universal Windows Platform (UWP). Using the UWP, you can build applications that will run on the widest range of Windows 10 devices without the need to maintain separate code bases. In addition to many phones, tablets, and desktop computers, UWP is also available on Xbox.

Images

Note The UWP defines a core set of features and functionality. The UWP divides devices into device families: the desktop device family, the mobile device family, the Xbox device family, and so on. Each device family defines the set of APIs and devices on which those APIs are implemented. Additionally, the Universal device family defines a core set of features and functionality that is available across all device families. The libraries available for each device family include conditional methods that enable an app to test on which device family it is currently running.

The purpose of this chapter is to provide a brief description of the concepts that underpin the UWP and to help you get started using Visual Studio 2017 to build apps that operate in this environment. In this chapter, you will learn about some of the features and tools included with Visual Studio 2017 for building UWP apps, and you will construct an app that conforms to the Windows 10 look and feel. You will concentrate on learning how to implement a user interface (UI) that scales and adapts to different device resolutions and form factors, and how to apply styling to give the app a distinctive look and feel. Subsequent chapters will focus on the functionality and other features of the app.

Images

Note There is not enough space in a book such as this to provide a comprehensive treatise on building UWP apps. Rather, these final chapters concentrate on the basic principles of building an interactive app that uses the Windows 10 UI. For detailed information on writing UWP apps, visit the “Guide to Universal Windows Platform (UWP) apps” page on the Microsoft website at https://msdn.microsoft.com/library/dn894631.aspx.

Features of a Universal Windows Platform app

Many modern handheld and tablet devices make it possible for users to interact with apps by using touch. You should design your UWP apps based on this style of user experience (UX). Windows 10 includes an extensive collection of touch-based controls that also work with a mouse and keyboard. You don’t need to separate the touch and mouse features in your apps; simply design your apps for touch, and users can still operate them by using the mouse and keyboard if they prefer or when they are using a device that does not support touch interaction.

The way in which the graphical user interface (GUI) responds to gestures to provide feedback to the user can greatly enhance the professional feel of your apps. The UWP app templates included with Visual Studio 2017 include an animation library that you can use in your apps to standardize this feedback and blend in seamlessly with the operating system and software that Microsoft provides.

Images

Note The term gesture refers to the manual touch-oriented operations that a user can perform. For example, a user can tap an item with a finger, and this gesture typically responds in the same way that you would expect a mouse click to behave. However, gestures can be far more expressive than the simple operations that can be captured by using a mouse. For example, the rotate gesture involves the user placing two fingers on the screen and tracing the arc of a circle with them; in a typical Windows 10 app, this gesture should cause the UI to rotate the selected object in the direction indicated by the movement of the user’s fingers. Other gestures include pinching to zoom in on an item to display more detail, pressing and holding to reveal more information about an item (similar to right-clicking the mouse click), and sliding to select an item and drag it across the screen.

The UWP is intended to run on a wide range of devices with varying screen sizes and resolutions. Therefore, when you implement a UWP app, you need to construct your software so that it adapts to the environment in which it is running, scaling automatically to the screen size and orientation of the device. This approach opens your software to an increasingly broad market. Additionally, many modern devices can also detect their orientation and the speed at which the user changes this orientation through the use of built-in sensors and accelerometers. UWP apps can adapt their layout as the user tilts or rotates a device, making it possible for the user to work in a mode that is most comfortable for that individual. You should also understand that mobility is a key requirement for many modern apps, and with UWP apps, users can roam and their data can migrate through the cloud to whatever device they happen to be running your app on at a particular moment.

The lifetime of a UWP app is somewhat different from that of a traditional desktop app. You should design apps that can run on devices such as smartphones to suspend execution when the user switches focus to another app and then to resume running when the focus returns. This approach can help to conserve resources and battery life on a constrained device. Windows might actually decide to close a suspended app if it determines that it needs to release system resources such as memory. When the app next runs, it should be able to resume where it left off. This means that you need to be prepared to manage app state information in your code, save it to hard disk, and restore it at the appropriate juncture.

Images

Note You can find more information about how to manage the life cycle of a UWP app at the page “Guidelines for app suspend and resume” on the Microsoft website at https://msdn.microsoft.com/library/windows/apps/hh465088.aspx.

When you build a new UWP app, you can package it by using the tools provided with Visual Studio 2017 and upload it to the Windows Store. Other users can then connect to the Store, download your app, and install it. You can charge a fee for your apps, or you can make them available at no cost. This distribution and deployment mechanism depends on your apps being trustworthy and conforming to security policies specified by Microsoft. When you upload an app to the Windows Store, it undergoes a number of checks to verify that it does not contain malicious code and that it conforms to the security requirements of a UWP app. These security constraints dictate how your app accesses resources on the computer on which it is installed. For example, by default, a UWP app cannot write directly to the file system or listen for incoming requests from the network (two of the behaviors commonly exhibited by viruses and other malware). However, if your app needs to perform restricted operations, you can specify them as capabilities in the app’s manifest data held in the Package.appxmanifest file. This information is recorded in the metadata of your app and signals Microsoft to perform additional tests to verify the way in which your app uses these features.

The Package.appxmanifest file is an XML document, but you can edit it in Visual Studio by using the Manifest Designer. The following image shows an example. Here, the Capabilities tab is being used to specify the restricted operations that the application can perform.

The Manifest Designer in Visual Studio. The Capabilities tab enables you to specify which restricted operations that application performs.

In this example, the application declares that it needs to:

  • Receive incoming data from the Internet but cannot act as a server and has no local network access.

  • Access GPS information that provides information about the location of the device.

  • Read and write files held in the user’s Pictures folder.

The user is made aware of these requirements, and in all cases, the user can disable the settings after installing the app; the application must detect when this has occurred and be prepared to fall back to an alternative solution or disable the functionality that requires these features.

Images

Note You can find more information about the capabilities that UWP apps support on the “App capability declarations” page on the Microsoft website at http://msdn.microsoft.com/library/windows/apps/hh464936.aspx.

Enough theory; let’s get started building a UWP app.

Using the Blank App template to build a Universal Windows Platform app

The simplest way to build a UWP app is to use the UWP app templates included with Visual Studio 2017 on Windows 10. Many of the GUI-based applications implemented in earlier chapters have made use of the Blank App template, and this is a good place to start.

In the following exercises, you will design the user interface for a simple app for a fictitious company called Adventure Works. This company manufactures and supplies bicycles and associated paraphernalia. The app will enable a user to enter and modify the details of Adventure Works’s customers.

Create the Adventure Works Customers app

  1. Start Visual Studio 2017 if it is not already running.

  2. On the File menu, point to New, and then click Project.

  3. In the New Project dialog box, in the left pane, expand Visual C#, and then click Windows Universal.

  4. In the middle pane, click the Blank App (Universal Windows) icon.

  5. In the Name field, type Customers.

  6. In the Location field, type Microsoft PressVCSBSChapter 25 in your Documents folder.

  7. Click OK.

  8. In the New Universal Windows Project dialog box, accept the default values for the Target Version and Minimum Version drop-down list boxes, and then click OK.

    The new app is created, and the Overview page is displayed. This page contains links to information that you can use to start creating, configuring, and deploying Universal Windows apps.

    The Overview page for a newly created when Visual Studio creates a new Universal Windows application.
  9. In Solution Explorer, double-click MainPage.xaml.

    The Design View window appears and displays a blank page. You can drag controls from the Toolbox to add the various controls required by the app, as demonstrated in Chapter 1, “Welcome to C#.” However, for this exercise, it is more instructive to concentrate on the XAML markup that defines the layout for the form. If you examine this markup, it should look like this:

    <Page
        x:Class="Customers.MainPage"
       xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
       xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
       xmlns:local="using:Customers"
       xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
       xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
       mc:Ignorable="d">
       <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
        </Grid>
    </Page>

    The form starts with the XAML <Page> tag and finishes with a closing </Page> tag. Everything between these tags defines the content of the page.

    The attributes of the <Page> tag contain a number of declarations of the form xmlns:id = “…”. These are XAML namespace declarations, and they operate similarly to C# using directives since they bring items into scope. Many of the controls and other items that you can add to a page are defined in these XAML namespaces, and you can ignore most of these declarations. However, there is one rather curious-looking declaration to which you should pay attention:

    xmlns:local="using:Customers"

    This declaration brings the items in the C# Customers namespace into scope. You can reference classes and other types in this namespace in your XAML code by prefixing them with local. The Customers namespace is the namespace generated for the code in your app.

  10. In Solution Explorer, expand MainPage.xaml, and then double-click MainPage.xaml.cs to display it in the Code and Text Editor window.

  11. Remember from the exercises earlier in this book that this is the C# file that contains the app logic and event handlers for the form. It looks like this (the using directives at the top of the file have been omitted to save space):

    // The Blank Page item template is documented at   http://go.microsoft.com/fwlink/?LinkId=402352&clcid=0x409
    namespace Customers
    {
        /// <summary>
        /// An empty page that can be used on its own or navigated to within a Frame.
        /// </summary>
        public sealed partial class MainPage : Page
        {
            public MainPage()
            {
                this.InitializeComponent();
            }
        }
    }

    This file defines the types in the Customers namespace. The page is implemented by a class called MainPage, and it inherits from the Page class. The Page class implements the default functionality of an XAML page for a UWP app, so all you have to do is write the code that defines the logic specific to your app in the MainPage class.

  12. Return to the MainPage.xaml file in the Design View window. If you look at the XAML markup for the page, you should notice that the <Page> tag includes the following attribute:

    x:Class="Customers.MainPage"

    This attribute connects the XAML markup that defines the layout of the page to the MainPage class that provides the logic behind the page.

That’s the basic plumbing of a simple UWP app. Of course, what makes a graphical app valuable is the way in which it presents information to a user. This is not always as simple as it sounds. Designing an attractive and easy-to-use graphical interface requires specialist skills that not all developers have (I know, because I lack them myself). However, many graphic artists who do have these skills are not programmers, so although they might be able to design a wonderful user interface, they might not be able to implement the logic required to make it useful. Fortunately, Visual Studio 2017 makes it possible for you to separate the user interface design from the business logic so that a graphic artist and a developer can cooperate to build a really cool-looking app that also works well. All a developer has to do is concentrate on the basic layout of the app and let a graphic artist provide the styling.

Implementing a scalable user interface

The key to laying out the user interface for a UWP app is to understand how to make it scale and adapt to the different form factors available for the devices on which users might run the app. In the following exercises, you will investigate how to achieve this scaling.

Lay out the page for the Customers app

  1. In the toolbar at the top of the Design View window, notice the drop-down list box that enables you to select the resolution and form factor of the design surface and a pair of buttons that enable you to select the orientation (portrait or landscape) for devices that support rotations (tablets and phones do; desktops, Xbox, Surface Hub, IoT devices, and HoloLens devices don’t). The intent is that you can use these options to quickly see how a user interface will appear on different devices.

    The form factors and orientations available for the user interface in a Universal Windows application.

    The default layout is for a Surface Book with a 13.5-inch screen in the landscape orientation; this form factor does not support portrait mode.

  2. In the drop-down list box, select 8” Tablet (1280 x 800). This is the form factor for a tablet device that supports rotations, and both landscape and portrait modes are available.

    Finally, click 13.3” Desktop. This is the form factor that you will use for the Customers application. This form factor defaults to the landscape orientation.

    Images

    Note You might find that the page layout in the Design View window appears too small (or too large). You can zoom in and out by using the Zoom drop-down list box at the bottom left of the Design View window.

  3. Review the XAML markup for the MainPage page.

    The page contains a single Grid control:

    <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
    </Grid>

    Images

    Note Don’t worry about the way in which the Background property is specified for the Grid control. This is an example of using a style, and you will learn about using styles later in this chapter.

    Understanding how the Grid control works is fundamental to building scalable and flexible user interfaces. The Page element can contain only a single item, and if you want, you can replace the Grid control with a Button, as shown in the example that follows:

    Images

    Note Don’t type the following code. It is shown for illustrative purposes only.

    <Page
       ...
       <Button Content="Click Me"/>
    </Page>

    However, the resulting app is probably not very useful; a form that contains a button and that displays nothing else is unlikely to win an award as the world’s greatest app. If you attempt to add a second control, such as a TextBox, to the page, your code will not compile and the errors shown in the following image will occur:

    The errors caused by adding multiple controls directly to a page in XAML.

    The purpose of the Grid control is to facilitate adding multiple items to a page. The Grid control is an example of a container control; it can contain a number of other controls, and you can specify the position of these other controls within the grid. Other container controls are also available. For example, the StackPanel control automatically places the controls it contains in a vertical arrangement, with each control positioned directly below its immediate predecessor.

    In this app, you will use a Grid to hold the controls necessary for a user to be able to enter and view data for a customer.

  4. Add a TextBlock control to the page, either by dragging it from the Toolbox or by typing the text <TextBlock /> directly into the XAML pane, on the blank line after the opening <Grid> tag, like this:

    <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
        <TextBlock />
    </Grid>

    Images

    Tip if the Toolbox does not appear, click Toolbox on the View menu, and it should be displayed in the toolbar to the left. Click Common XAML Controls to display the contents of the Toolbox. Also, note that you can type the code for a control directly into the XAML window for a page; you do not have to drag controls from the Toolbox.

  5. This TextBlock provides the title for the page. Set the properties of the TextBlock control by using the values in the following table:

    Property

    Value

    HorizontalAlignment

    Left

    Margin

    400,90,0,0

    TextWrapping

    Wrap

    Text

    Adventure Works Customers

    VerticalAlignment

    Top

    FontSize

    50

    You can set these properties by using the Properties window or by typing the equivalent XAML markup into the XAML window, as shown here in bold:

    <TextBlock HorizontalAlignment="Left" Margin="400,90,0,0" TextWrapping="Wrap"
    Text="Adventure Works Customers" VerticalAlignment="Top" FontSize="50"/>

    The resulting text should appear in the Design View window, like this:

    The Design View window showing the TextBlock control that was added to the page.

    Notice that when you drag a control from the Toolbox to a form, connectors appear that specify the distance of two of the sides of the control from the edge of the container control in which it is placed. In the preceding example, these connectors for the TextBlock control are labeled with the values 400 (from the left edge of the grid) and 90 (from the top edge of the grid). At run time, if the Grid control is resized, the TextBlock will move to retain these distances, which in this case might cause the distance of the TextBlock in pixels from the right and bottom edges of the Grid to change. You can specify the edge or edges to which a control is anchored by setting the HorizontalAlignment and VerticalAlignment properties. The Margin property specifies the distance from the anchored edges. Again, in this example, the HorizontalAlignment property of the TextBlock is set to Left and the VerticalAlignment property is set to Top, which is why the control is anchored to the left and top edges of the grid. The Margin property contains four values that specify the distance of the left, top, right, and bottom sides (in that order) of the control from the corresponding edge of the container. If one side of a control is not anchored to an edge of the container, you can set the corresponding value in the Margin property to 0.

  6. Add four more TextBlock controls to the page. These TextBlock controls are labels that help the user identify the data that is displayed on the page. Use the values in the following table to set the properties of these controls:

    Control

    Property

    Value

    First Label

    HorizontalAlignment

    Left

    Margin

    330,190,0,0

    TextWrapping

    Wrap

    Text

    ID

    VerticalAlignment

    Top

    FontSize

    20

    Second Label

    HorizontalAlignment

    Left

    Margin

    460,190,0,0

    TextWrapping

    Wrap

    Text

    Title

    VerticalAlignment

    Top

    FontSize

    20

    Third Label

    HorizontalAlignment

    Left

    Margin

    620,190,0,0

    TextWrapping

    Wrap

    Text

    First Name

    VerticalAlignment

    Top

    FontSize

    20

    Fourth Label

    HorizontalAlignment

    Left

    Margin

    975,190,0,0

    TextWrapping

    Wrap

    Text

    Last Name

    VerticalAlignment

    Top

    FontSize

    20

    As before, you can either drag the controls from the Toolbox and use the Properties window to set their properties, or you can type the following XAML markup into the XAML pane, after the existing TextBlock control and before the closing </Grid> tag:

    <TextBlock HorizontalAlignment="Left" Margin="330,190,0,0" TextWrapping="Wrap"
    Text="ID" VerticalAlignment="Top" FontSize="20"/>
    <TextBlock HorizontalAlignment="Left" Margin="460,190,0,0" TextWrapping="Wrap"
    Text="Title" VerticalAlignment="Top" FontSize="20"/>
    <TextBlock HorizontalAlignment="Left" Margin="620,190,0,0" TextWrapping="Wrap"
    Text="First Name" VerticalAlignment="Top" FontSize="20"/>
    <TextBlock HorizontalAlignment="Left" Margin="975,190,0,0" TextWrapping="Wrap"
    Text="Last Name" VerticalAlignment="Top" FontSize="20"/>

  7. Below the TextBlock controls, add three TextBox controls that display the text ID, First Name, and Last Name. Use the following table to set the values of these controls. Notice that the Text property should be set to the empty string (“”). Also notice that the id TextBox control is marked as read-only. This is because customer IDs will be generated automatically in the code that you add later:

    Control

    Property

    Value

    First TextBox

    x:Name

    id

    HorizontalAlignment

    Left

    Margin

    300,240,0,0

    TextWrapping

    Wrap

    Text

    VerticalAlignment

    Top

    FontSize

    20

    IsReadOnly

    True

    Second TextBox

    x:Name

    firstName

    HorizontalAlignment

    Left

    Margin

    550,240,0,0

    TextWrapping

    Wrap

    Text

    VerticalAlignment

    Top

    FontSize

    20

    Third TextBox

    x:Name

    lastName

    HorizontalAlignment

    Left

    Margin

    875,240,0,0

    TextWrapping

    Wrap

    Text

    VerticalAlignment

    Top

    FontSize

    20

    The following code shows the equivalent XAML markup for these controls:

    <TextBox x:Name="id" HorizontalAlignment="Left" Margin="300,240,0,0" TextWrapping="Wrap"
    Text="" VerticalAlignment="Top" FontSize="20" IsReadOnly="True"/>
    <TextBox x:Name="firstName" HorizontalAlignment="Left" Margin="550,240,0,0"
    TextWrapping="Wrap" Text="" VerticalAlignment="Top" Width="300" FontSize="20"/>
    <TextBox x:Name="lastName" HorizontalAlignment="Left" Margin="875,240,0,0"
    TextWrapping="Wrap" Text="" VerticalAlignment="Top" Width="300" FontSize="20"/>

    The Name property is not required for a control, but it is useful if you want to refer to the control in the C# code for the app. Notice that the Name property is prefixed with x:. This is a reference to the XML namespace http://schemas.microsoft.com/winfx/2006/xaml specified in the Page attributes at the top of the XAML markup. This namespace defines the Name property for all controls.

    Images

    Note It is not necessary to understand why the Name property is defined this way, but for more information, you can read the article “x:Name Directive” at http://msdn.microsoft.com/library/ms752290.aspx.

    The Width property specifies the width of the control, and the TextWrapping property indicates what happens if the user attempts to enter information into the control that exceeds its width. In this case, all the TextBox controls will wrap the text onto another line of the same width (the control will expand vertically). The alternative value, NoWrap, causes the text to scroll horizontally as the user enters it.

  8. Add a ComboBox control to the form, placing it below the Title TextBlock control, between the id and firstName TextBox controls. Set the properties of this control as follows:

    Property

    Value

    x:Name

    title

    HorizontalAlignment

    Left

    Margin

    420,240,0,0

    VerticalAlignment

    Top

    Width

    100

    FontSize

    20

    The equivalent XAML markup for this control is as follows:

    <ComboBox x:Name="title" HorizontalAlignment="Left" Margin="420,240,0,0"
    VerticalAlignment="Top" Width="100" FontSize="20"/>

    You use a ComboBox control to display a list of values from which the user can select.

  9. In the XAML pane, replace the definition of the ComboBox control and add four ComboBoxItem controls, as follows in bold:

    <ComboBox x:Name="title" HorizontalAlignment="Left" Margin="420,240,0,0"
      VerticalAlignment="Top" Width="75" FontSize="20">
        <ComboBoxItem Content="Mr"/>
        <ComboBoxItem Content="Mrs"/>
        <ComboBoxItem Content="Ms"/>
        <ComboBoxItem Content="Miss"/>
    </ComboBox>

    The ComboxBoxItem elements are displayed in a drop-down list when the app runs, and the user can select one of them.

    There is one important syntactical point to notice in this code; the ComboBox markup has been split into an opening <ComboBox> tag and a closing </ComboBox> tag. You place the ComboBoxItem controls between these opening and closing tags.

    Images

    Note A ComboBox control can display simple elements such as a set of ComboBoxItem controls that display text, but it can also contain more complex elements such as buttons, check boxes, and radio buttons. If you are adding simple ComboBoxItem controls, it is probably easier to type the XAML markup by hand, but if you are adding more complex controls, the Object Collection Editor available in the Properties window can prove very useful. However, you should avoid trying to be too clever in a combo box; the best apps are those that provide the most intuitive UIs, and embedding complex controls in a combo box can be confusing to a user.

  10. Add two more TextBox controls and two more TextBlock controls to the form. With the TextBox controls, the user will be able to enter an email address and a telephone number for the customer, and the TextBlock controls provide the labels for the text boxes. Use the values in the following table to set the properties of the controls.

    Control

    Property

    Value

    First TextBlock

    HorizontalAlignment

    Left

    Margin

    300,390,0,0

    TextWrapping

    Wrap

    Text

    Email

    VerticalAlignment

    Top

    FontSize

    20

    First TextBox

    x:Name

    email

    HorizontalAlignment

    Left

    Margin

    450,390,0,0

    TextWrapping

    Wrap

    Text

    Leave Empty

    VerticalAlignment

    Top

    Width

    400

    FontSize

    20

    Second TextBlock

    HorizontalAlignment

    Left

    Margin

    300,540,0,0

    TextWrapping

    Wrap

    Text

    Phone

    VerticalAlignment

    Top

    FontSize

    20

    Second TextBox

    x:Name

    phone

    HorizontalAlignment

    Left

    Margin

    450,540,0,0

    TextWrapping

    Wrap

    Text

    Leave Empty

    VerticalAlignment

    Top

    Width

    200

    FontSize

    20

    The XAML markup for these controls should look like this:

    <TextBlock HorizontalAlignment="Left" Margin="300,390,0,0" TextWrapping="Wrap"
    Text="Email" VerticalAlignment="Top" FontSize="20"/>
    <TextBox x:Name="email" HorizontalAlignment="Left" Margin="450,390,0,0"
    TextWrapping="Wrap" Text="" VerticalAlignment="Top" Width="400" FontSize="20"/>
    <TextBlock HorizontalAlignment="Left" Margin="300,540,0,0" TextWrapping="Wrap"
    Text="Phone" VerticalAlignment="Top" FontSize="20"/>
    <TextBox x:Name="phone" HorizontalAlignment="Left" Margin="450,540,0,0"
    TextWrapping="Wrap" Text="" VerticalAlignment="Top" Width="200" FontSize="20"/>

    The completed form in the Design View window should look like this:

    The Design View window showing the completed page.
  11. On the Debug menu, click Start Debugging to build and run the app.

    The app starts and displays the form. You can enter data into the form and select a title from the combo box, but you cannot do much else yet. However, a much bigger problem is that, depending on the resolution of your screen, the form looks awful (if the form looks fine, drag the right-hand edge to make it narrower). The right side of the display has been cut off, much of the text has wrapped around, and the Last Name text box has been truncated:

    The Customers application is running. The runtime has adjusted the page to fit on the screen, causing text wrapping and truncation.
  12. Click and drag the right side of the window to expand the display so that the text and controls are displayed as they appeared in the Design View window in Visual Studio. This is the optimal size of the form as it was designed.

  13. Resize the window displaying the Customer app to its minimum width. This time, much of the form disappears. Some of the TextBlock content wraps, but the form is clearly not usable in this view.

  14. Return to Visual Studio, and on the Debug menu, click Stop Debugging.

That was a salutary lesson in being careful about how you lay out an app. Although the app looked fine when it ran in a window that was the same size as the Design View, as soon as you resized the window to a narrower view, it became less useful (or even completely useless). Additionally, the app assumes that the user will be viewing the screen on a device in the landscape orientation. If you temporarily switch the Design View window to the 12” Tablet form factor and click the Portrait orientation button, you can see what the form would look like if the user ran the app on a tablet that supports different orientations and rotated the device to switch to portrait mode. (Don’t forget to switch back to the 13.3” Desktop form factor afterward.)

The issue is that the layout technique shown so far does not scale and adapt to different form factors and orientations. Fortunately, you can use the properties of the Grid control and another feature called the Visual State Manager to solve these problems.

Using the Simulator to test a Universal Windows Platform app

Even if you don’t have a tablet computer, you can still test your UWP apps and see how they behave on a mobile device by using the Simulator provided with Visual Studio 2017. The Simulator mimics a tablet device, providing you with the ability to emulate user gestures such as pinching and swiping objects, as well as rotating and changing the resolution of the device.

To run an app in the Simulator, open the Debug Target drop-down list box on the Visual Studio toolbar. By default, the debug target is set to Local Machine, which causes the app to run full-screen on your computer, but you can select Simulator from this list, which starts the Simulator when you debug the app. Note that you can also set the debug target to a different computer if you need to perform remote debugging (you will be prompted for the network address of the remote computer when you select this option). The following image shows the Debug Target list:

The Debug Target list in Visual Studio, enabling you to select the device on which to run the application for debugging.

After you have selected the Simulator, when you run the app from the Debug menu in Visual Studio, the Simulator starts and displays your app. The toolbar down the right side of the Simulator window contains a selection of tools with which you can emulate user gestures by using the mouse. You can even simulate the location of the user if the app requires information about the geographic position of the device. However, for testing the layout of an app, the most important tools are Rotate Clockwise, Rotate Counterclockwise, and Change Resolution. The following image shows the Customers app running in the Simulator. The app has been maximized to occupy the full screen. The labels describe the function of each of the buttons for the Simulator.

The Simulator running the Customers app. The Simulator buttons are labeled.

Images

Note The screenshots in this section were captured on a computer with the Simulator running at a resolution of 1366 x 768 (representing a 10.6-inch display). If you are using a different display resolution, you might need to click the Change Resolution button and switch to 1366 × 768 to get the same results as shown here.

The following image shows the same app after the user has clicked the Rotate Clockwise button, which causes the app to run in the portrait orientation:

The Simulator showing the Customers application running in portrait mode (rotated 90 degrees).

You can also try to see how the app behaves if you change the resolution of the Simulator. The following image shows the Customers app running when the Simulator is set to a high-resolution device (2560 × 1440, the typical resolution of a 27-inch monitor). You can see that the display for the app is squeezed into the upper-left corner of the screen:

The Simulator running the Customers application using the resolution for a 27-inch monitor.

The Simulator behaves exactly like a Windows 10 computer (it is, in fact, a remote-desktop connection to your own computer). To stop the Simulator, click the Windows button (in the Simulator, not on your desktop), click Power, and then click Disconnect.

You should notice that Visual Studio also supports emulators for specific mobile devices. Some may be listed in the Simulator drop-down list box, but you can install new emulators as they become available by selecting Download New Emulators.

Implementing a tabular layout by using a Grid control

You can use the Grid control to implement a tabular layout. A Grid contains rows and columns, and you can specify in which rows and columns other controls should be placed. The beauty of the Grid control is that you can specify the sizes of the rows and columns that it contains as relative values; as the grid shrinks or grows to adapt itself to the different form factors and orientations to which users might switch, the rows and columns can shrink and grow in proportion to the grid. The intersection of a row and a column in a grid defines a cell, and if you position controls in cells, they will move as the rows and columns shrink and grow. Therefore, the key to implementing a scalable UI is to break it down into a collection of cells and place related elements in the same cell. A cell can contain another grid, giving you the ability to fine-tune the exact positioning of each element.

If you consider the Customers app, you can see that the UI breaks down into two main areas: a heading containing the title and the body containing the customers’ details. Allowing for some spacing between these areas and a margin at the bottom of the form, you can assign relative sizes to each of these areas, as shown in the following diagram:

The relative sizes of different #8220;rows#8221; on the page in the Customers application.

The diagram shows only rough approximations, but the row for the heading is twice as high as the row for the spacer below it. The row for the body is ten times as high as the spacer, and the bottom margin is twice the height of the spacer.

To hold the elements in each area, you can define a grid with four rows and place the appropriate items in each row. However, the body of the form can be described by another, more complex grid, as shown in the following diagram:

The areas of the page in the Customers application mapped out to show their relative sizes.

Again, the height of each row is specified in relative terms, as is the width of each column. Also, you can clearly see that the TextBox elements for Email and Phone do not quite fit this grid pattern. If you were being pedantic, you might choose to define further grids inside the body of the form to make these items fit. However, you should keep in mind the purpose of this grid, which is to define the relative positioning and spacing of elements. Therefore, it is acceptable for an element to extend beyond the boundaries of a cell in the grid arrangement.

In the next exercise, you will modify the layout of the Customers app to use this grid format to position the controls.

Modify the layout to scale to different form factors and orientations

  1. In the XAML pane for the Customers app, add another Grid inside the existing Grid element, before the first TextBlock control. Give this new Grid a margin of 10 pixels from the left and right edges of the parent Grid and 20 pixels from the top and bottom, as shown in bold in the following code:

    <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
       <Grid Margin="10,20,10,20">
       </Grid>
       <TextBlock HorizontalAlignment="Left" TextWrapping="Wrap"
    Text="Adventure Works Customers" ... />
       ...
    </Grid>

    You could define the rows and columns as part of the existing Grid, but to maintain a consistent look and feel with other UWP apps, you should leave some blank space to the left and at the top of a page.

  2. Add the following <Grid.RowDefinitions> section shown in bold to the new Grid element.

    <Grid Margin="10,20,10,20">
       <Grid.RowDefinitions>
          <RowDefinition Height="2*"/>
          <RowDefinition Height="*"/>
          <RowDefinition Height="10*"/>
          <RowDefinition Height="2*"/>
       </Grid.RowDefinitions>
    </Grid>

    The <Grid.RowDefinitions> section defines the rows for the grid. In this example, you have defined four rows. You can specify the size of a row as an absolute value specified in pixels, or you can use the * operator to indicate that the sizes are relative and that Windows should calculate the row sizes itself when the app runs, depending on the form factor and resolution of the screen. The values used in this example correspond to the relative row sizes for the header, body, spacer, and bottom margin of the Customers form shown in the earlier diagram.

  3. Move the TextBlock control that contains the text “Adventure Works Customers” into the Grid, directly after the closing </Grid.RowDefinitions> tag but before the closing </Grid> tag.

  4. Add a Grid.Row attribute to the TextBlock control and set the value to 0.

    <Grid Margin="10,20,10,20">
        <Grid.RowDefinitions>
          ...
        </Grid.RowDefinitions>
        <TextBlock Grid.Row="0" ... Text="Adventure Works Customers" ... />
        ...
    </Grid>

    This indicates that the TextBlock should be positioned within the first row of the Grid.  (Grid controls number rows and columns starting at zero.)

    Images

    Note The Grid.Row attribute is an example of an attached property. An attached property is a property that a control receives from the container control in which it is placed. Outside a grid, a TextBlock does not have a Row property (it would be meaningless), but when positioned within a grid, the Row property is attached to the TextBlock, and the TextBlock control can assign it a value. The Grid control then uses this value to determine where to display the TextBlock control. Attached properties are easy to spot because they have the form ContainerType.PropertyName.

  5. Remove the Margin property, and set the HorizontalAlignment and VerticalAlignment properties to Center.

    This will cause the TextBlock to appear centered in the row.

    The XAML markup for the Grid and TextBlock controls should look like this (the changes to the TextBlock are highlighted in bold):

    <Grid Margin="10,20,10,20">
        ...
        </Grid.RowDefinitions>
        <TextBlock Grid.Row="0" HorizontalAlignment="Center" TextWrapping="Wrap"
    Text="Adventure Works Customers" VerticalAlignment="Center" FontSize="50"/>
        ...
    </Grid>

  6. After the TextBlock control, add another nested Grid control. This Grid will be used to lay out the controls in the body of the form and should appear in the third row of the outer Grid (the row of size 10*), so set the Grid.Row property to 2, as shown in bold in the following code:

    <Grid Margin="10,20,10,20">
       <Grid.RowDefinitions>
          <RowDefinition Height="2*"/>
          <RowDefinition Height="*"/>
          <RowDefinition Height="10*"/>
          <RowDefinition Height="2*"/>
       </Grid.RowDefinitions>
       <TextBlock Grid.Row="0" HorizontalAlignment="Center" .../>
       <Grid Grid.Row="2">
       </Grid>
       ...
    </Grid>

  7. Add the following <Grid.RowDefinitions> and <Grid.ColumnDefinitions> sections to the new Grid control:

    <Grid Grid.Row="2">
        <Grid.RowDefinitions>
            <RowDefinition Height="*"/>
            <RowDefinition Height="*"/>
            <RowDefinition Height="2*"/>
            <RowDefinition Height="*"/>
            <RowDefinition Height="2*"/>
            <RowDefinition Height="*"/>
            <RowDefinition Height="4*"/>
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*"/>
            <ColumnDefinition Width="*"/>
            <ColumnDefinition Width="20"/>
            <ColumnDefinition Width="*"/>
            <ColumnDefinition Width="20"/>
            <ColumnDefinition Width="2*"/>
            <ColumnDefinition Width="20"/>
            <ColumnDefinition Width="2*"/>
           <ColumnDefinition Width="*"/>
        </Grid.ColumnDefinitions>
    </Grid>

    These row and column definitions specify the height and width of each of the rows and columns shown earlier in the diagram that depicted the structure of the body of the form. There is a small space of 20 pixels between each of the columns that will hold controls.

  8. Move the TextBlock controls that display the ID, Title, First Name, and Last Name labels inside the nested Grid control, immediately after the closing <Grid.ColumnDefinitions> tag.

  9. Set the Grid.Row property for each TextBlock control to 0 (these labels will appear in the first row of the grid). Set the Grid.Column property for the ID label to 1, the Grid.Column property for the Title label to 3, the Grid.Column property for the First Name label to 5, and the Grid.Column property for the Last Name label to 7.

  10. Remove the Margin property from each of the TextBlock controls, and set the HorizontalAlignment and VerticalAlignment properties to Center. The XAML markup for these controls should look like this (the changes are highlighted in bold):

    <Grid Grid.Row="2">
       <Grid.RowDefinitions>
          ...
       </Grid.RowDefinitions>
       <Grid.ColumnDefinitions>
          ...
       </Grid.ColumnDefinitions>
       <TextBlock Grid.Row="0" Grid.Column="1" HorizontalAlignment="Center"
    TextWrapping="Wrap" Text="ID" VerticalAlignment="Center" FontSize="20"/>
      <TextBlock Grid.Row="0" Grid.Column="3" HorizontalAlignment="Center"
    TextWrapping="Wrap" Text="Title" VerticalAlignment="Center" FontSize="20"/>
      <TextBlock Grid.Row="0" Grid.Column="5" HorizontalAlignment="Center"
    TextWrapping="Wrap" Text="First Name" VerticalAlignment="Center" FontSize="20"/>
      <TextBlock Grid.Row="0" Grid.Column="7" HorizontalAlignment="Center"
    TextWrapping="Wrap" Text="Last Name" VerticalAlignment="Center" FontSize="20"/>
    </Grid>

  11. Move the id, firstName, and lastName TextBox controls and the title ComboBox control inside the nested Grid control, immediately after the Last Name TextBlock control.

    Place these controls in row 1 of the Grid control. Put the id control in column 1, the title control in column 3, the firstName control in column 5, and the lastName control in column 7.

    Remove the Margin of each of these controls, and set the VerticalAlignment property to Center. Remove the Width property, and set the HorizontalAlignment property to Stretch. This causes the control to occupy the entire cell when it is displayed, and the control shrinks or grows as the size of the cell changes.

    The completed XAML markup for these controls should look like this, with changes highlighted in bold:

    <Grid Grid.Row="2">
    <Grid.RowDefinitions>
    ...
    </Grid.RowDefinitions>
    <Grid.ColumnDefinitions>
    ...
    </Grid.ColumnDefinitions>
    ...
       <TextBlock Grid.Row="0" Grid.Column="7" ... Text="Last Name" .../>
       <TextBox Grid.Row="1" Grid.Column="1" x:Name="id" HorizontalAlignment="Stretch"
    TextWrapping="Wrap" Text="" VerticalAlignment="Center" FontSize="20" IsReadOnly="True"/>
       <TextBox Grid.Row="1" Grid.Column="5" x:Name="firstName" HorizontalAlignment="Stretch"
    TextWrapping="Wrap" Text="" VerticalAlignment="Center" FontSize="20"/>
       <TextBox Grid.Row="1" Grid.Column="7" x:Name="lastName" HorizontalAlignment="Stretch"
    TextWrapping="Wrap" Text="" VerticalAlignment="Center" FontSize="20"/>
       <ComboBox Grid.Row="1" Grid.Column="3" x:Name="title" HorizontalAlignment="Stretch"
       VerticalAlignment="Center" FontSize="20">
         <ComboBoxItem Content="Mr"/>
         <ComboBoxItem Content="Mrs"/>
         <ComboBoxItem Content="Ms"/>
         <ComboBoxItem Content="Miss"/>
       </ComboBox>
    </Grid>

  12. Move the TextBlock control for the Email label and the email TextBox control to the nested Grid control, immediately after the closing tag of the title ComboBox control.

    Place these controls in row 3 of the Grid control. Put the Email label in column 1 and the email TextBox control in column 3. Additionally, set the Grid.ColumnSpan property for the email TextBox control to 5; this way, the column can spread to the value specified by its Width property across five columns, as shown in the earlier diagram.

    Set the HorizontalAlignment property of the Email label control to Center, but leave the HorizontalAlignment property of the email TextBox set to Left; this control should remain left-justified against the first column that it spans rather than being centered across them all.

    Set the VerticalAlignment property of the Email label and the email TextBox control to Center.

    Remove the Margin property for both of these controls.

    The following XAML markup shows the completed definitions of these controls:

    <Grid Grid.Row="2">
        <Grid.RowDefinitions>
            ...
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            ...
       </Grid.ColumnDefinitions>
        ...
        <ComboBox Grid.Row="1" Grid.Column="3" x:Name="title" ...>
            ...
        </ComboBox>
        <TextBlock Grid.Row="3" Grid.Column="1" HorizontalAlignment="Center"
    TextWrapping="Wrap" Text="Email" VerticalAlignment="Center" FontSize="20"/>
        <TextBox Grid.Row="3" Grid.Column="3" Grid.ColumnSpan="5" x:Name="email"
    HorizontalAlignment="Left" TextWrapping="Wrap" Text="" VerticalAlignment="Center"
    Width="400" FontSize="20"/>
    </Grid>

  13. Move the TextBlock control for the Phone label and phone TextBox control to the nested Grid control, immediately after the email TextBox control.

    Place these controls in row 5 of the Grid control. Put the Phone label in column 1 and the phone TextBox control in column 3. Set the Grid.ColumnSpan property for the phone TextBox control to 3.

    Set the HorizontalAlignment property of the Phone label control to Center, and leave the HorizontalAlignment property of the phone TextBox set to Left.

    Set the VerticalAlignment property of both controls to Center, and remove the Margin property.

    The following XAML markup shows the completed definitions of these controls:

    <Grid Grid.Row="2">
        <Grid.RowDefinitions>
            ...
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            ...
        </Grid.ColumnDefinitions>
            ....
        <TextBox ... x:Name="email" .../>
        <TextBlock Grid.Row="5" Grid.Column="1" HorizontalAlignment="Center"
    TextWrapping="Wrap" Text="Phone" VerticalAlignment="Center" FontSize="20"/>
        <TextBox Grid.Row="5" Grid.Column="3" Grid.ColumnSpan="3" x:Name="phone"
    HorizontalAlignment="Left" TextWrapping="Wrap" Text="" VerticalAlignment="Center"
    Width="200" FontSize="20"/>
    </Grid>

  14. On the Visual Studio toolbar, in the Debug Target list, select Simulator.

    You will run the app in the Simulator so that you can see how the layout adapts to different resolutions and form factors.

  15. On the Debug menu, click Start Debugging.

    The Simulator starts and the Customers app runs. Maximize the app so that it occupies the entire screen in the Simulator. Click Change Resolution, and then configure the Simulator to display the app using a screen resolution of 1366 × 768. Also, ensure that the Simulator is displayed in landscape orientation (click Rotate Clockwise if it is running in portrait orientation). Verify that the controls are evenly spaced in this orientation.

  16. Click the Rotate Clockwise button to rotate the Simulator to portrait orientation.

    The Customers app should adjust the layout of the user interface, and the controls should still be evenly spaced and usable:

    The Customers application running in Portrait mode in the Simulator. The controls are evenly spaced and usable.
  17. Click Rotate Counterclockwise to put the Simulator back to landscape orientation, and then click Change Resolution and switch the resolution of the Simulator to 2560 × 1400.

    Notice that the controls remain evenly spaced on the form, although the labels might be quite difficult to read unless you actually have a 27-inch screen.

  18. Click Change Resolution again and switch the resolution to 1024 × 768.

    Again, notice how the spacing and size of the controls are adjusted to maintain the even balance of the user interface:

    The Customers application is running in Landscape mode on a lower-resolution screen. The controls adjust themselves so that they are evenly spaced.
  19. In the Simulator, double-click the top edge of the form to restore the view as a window, and then drag and resize the window so that the form is displayed in the left half of the screen. Reduce the width of the window to its minimum. This is how the app might appear on a device such as a smartphone.

    All the controls remain visible, but the text for the Phone label and the title wrap, making them difficult to read, and the controls are not particularly easy to use anymore:

    The Customers application is running in a narrow window. Some of the labels wrap, and some of the text boxes are truncated.
  20. In the Simulator, click the Start button, click Settings, click Power, and then click Disconnect.

    The Simulator closes, and you return to Visual Studio.

  21. On the Visual Studio toolbar, in the Debug Target drop-down list box, select Local Machine.

Adapting the layout by using the Visual State Manager

The user interface for the Customers app scales for different resolutions and form factors, but it still does not work well if you reduce the width of the view, and it probably would not look too good on a smartphone, which has an even narrower width. If you think about it, the solution to the problem in these cases is not so much a matter of scaling the controls as actually laying them out in a different way. For example, it would make better sense if the Customers form looked like this in a narrow view:

The #8220;narrow#8221; view for the Customers application. The fields are displayed in a columnar layout.

You can achieve this effect in several ways:

  • You can create several versions of the MainPage.xaml file, one for each device family. Each of these XAML files can be linked to the same code-behind (MainPage.xaml.cs) so that they all run the same code. For example, to create an XAML file for a smartphone, add a folder named DeviceFamily-Mobile (this name is important) to the project and then add a new XAML view named MainPage.xaml to the folder by using the Add New Item menu command. Lay out the controls on this page folder as they should be displayed on a smartphone. The XAML view will be linked automatically to the existing MainPage.xaml.cs file. At runtime, the UWP will select the appropriate view based on the type of device on which the app is running.

  • You can use the Visual State Manager to modify the layout of the page at runtime. All UWP apps implement a Visual State Manager that tracks the visual state of an app. It can detect when the height and width of the window changes, and you can add XAML markup that positions controls depending on the size of the window. This markup can move controls around or display and hide controls.

  • You can use the Visual State Manager to switch between views based on the height and width of the window. This approach is a hybrid combination of the first two options described here, but it is the least messy (you don’t have to write lots of tricky code to calculate the best position for each control) and is also the most flexible (it will work if the window is narrowed on the same device).

You’ll follow the third of these approaches in the next exercises. The first step is to define a layout for the customers’ data that should appear in a narrow view.

Define a layout for the narrow view

  1. In the XAML pane for the Customers app, add the x:Name and Visibility properties shown below in bold to the nested Grid control:

    <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
        <Grid x:Name="customersTabularView" Margin="10,20,10,20" Visibility="Collapsed">
        ...
        </Grid>
    </Grid>

    This Grid control will hold the default view of the form. You will reference this Grid control in other XAML markup later in this set of exercises, hence the requirement to give it a name. The Visibility property specifies whether the control is displayed (Visible) or hidden (Collapsed). The default value is Visible, but for the time being, you will hide this Grid while you define another for displaying the data in a columnar format.

  2. After the closing </Grid> tag for the customersTabularView Grid control, add another Grid control. Set the x:Name property to customersColumnarView, set the Margin property to 10,20,10,20, and set the Visibility property to Visible.

    Images

    Tip You can expand and contract elements in the XAML pane of the Design View window and make the structure easier to read by clicking the + and – signs that appear down the left edge of the XAML markup.

    <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
        <Grid x:Name="customersTabularView" Margin="10,20,10,20" Visibility="Collapsed">
        ...
        </Grid>
        <Grid x:Name="customersColumnarView" Margin="10,20,10,20" Visibility="Visible">
        </Grid>
    </Grid>

    This Grid control will hold the “narrow” view of the form. The fields in this grid will be layed out in a columnar manner as described earlier.

  3. In the customersColumnarView Grid control, add the following row definitions:

    <Grid x:Name="customersColumnarView" Margin="10,20,10,20" Visibility="Visible">
        <Grid.RowDefinitions>
            <RowDefinition Height="*"/>
            <RowDefinition Height="10*"/>
        </Grid.RowDefinitions>
    </Grid>

    You will use the top row to display the title and the second, much larger row to display the controls in which users enter data.

  4. Immediately after the row definitions, add the TextBlock control shown below in bold. This control displays a truncated title, Customers, in the first row of the Grid control. Set FontSize to 30.

    <Grid x:Name="customersColumnarView" Margin="10,20,10,20" Visibility="Visible">
        <Grid.RowDefinitions>
        ...
        </Grid.RowDefinitions>
        <TextBlock Grid.Row="0" HorizontalAlignment="Center" TextWrapping="Wrap"
    Text="Customers" VerticalAlignment="Center" FontSize="30"/>
    </Grid>

  5. Add another Grid control to row 1 of the customersColumnarView Grid control, directly after the TextBlock control that contains the Customers title. This Grid control will display the labels and data-entry controls in two columns, so add the row and columns definitions shown in bold in the following code example to this Grid.

    <TextBlock Grid.Row="0" ... />
    <Grid Grid.Row="1">
        <Grid.ColumnDefinitions>
            <ColumnDefinition/>
            <ColumnDefinition/>
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition/>
            <RowDefinition/>
            <RowDefinition/>
            <RowDefinition/>
            <RowDefinition/>
            <RowDefinition/>
        </Grid.RowDefinitions>
    </Grid>

    Notice that if all the rows or columns in a set have the same height or width, you do not need to specify their size.

  6. Copy the XAML markup for the ID, Title, First Name, and Last Name TextBlock controls from the customersTabularView Grid control to the new Grid control, immediately after the row definitions that you just added. Put the ID control in row 0, the Title control in row 1, the First Name control in row 2, and the Last Name control in row 3. Place all controls in column 0.

    <Grid.RowDefinitions>
    ...
    </Grid.RowDefinitions>
    <TextBlock Grid.Row="0" Grid.Column="0" HorizontalAlignment="Center"
    TextWrapping="Wrap" Text="ID" VerticalAlignment="Center" FontSize="20"/>
    <TextBlock Grid.Row="1" Grid.Column="0" HorizontalAlignment="Center"
    TextWrapping="Wrap" Text="Title" VerticalAlignment="Center" FontSize="20"/>
    <TextBlock Grid.Row="2" Grid.Column="0" HorizontalAlignment="Center"
    TextWrapping="Wrap" Text="First Name" VerticalAlignment="Center" FontSize="20"/>
    <TextBlock Grid.Row="3" Grid.Column="0" HorizontalAlignment="Center"
    TextWrapping="Wrap" Text="Last Name" VerticalAlignment="Center" FontSize="20"/>

  7. Copy the XAML markup for the id, title, firstName, and lastName TextBox and ComboBox controls from the customersTabularView Grid control to the new Grid control, immediately after the TextBox controls. Put the id control in row 0, the title control in row 1, the firstName control in row 2, and the lastName control in row 3. Place all four controls in column 1. Also, change the names of the controls by prefixing them with the letter c (for column). This final change is necessary to avoid clashing with the names of the existing controls in the customersTabularView Grid control.

    <TextBlock Grid.Row="3" Grid.Column="0" HorizontalAlignment="Center"
    extWrapping="Wrap" Text="Last Name" .../>
    <TextBox Grid.Row="0" Grid.Column="1" x:Name="cId" HorizontalAlignment="Stretch"
    TextWrapping="Wrap" Text="" VerticalAlignment="Center" FontSize="20" IsReadOnly="True"/>
    <TextBox Grid.Row="2" Grid.Column="1" x:Name="cFirstName" HorizontalAlignment="Stretch"
    TextWrapping="Wrap" Text="" VerticalAlignment="Center" FontSize="20"/>
    <TextBox Grid.Row="3" Grid.Column="1" x:Name="cLastName" HorizontalAlignment="Stretch"
    TextWrapping="Wrap" Text="" VerticalAlignment="Center" FontSize="20"/>
    <ComboBox Grid.Row="1" Grid.Column="1" x:Name="cTitle" HorizontalAlignment="Stretch"
    VerticalAlignment="Center" FontSize="20">
        <ComboBoxItem Content="Mr"/>
        <ComboBoxItem Content="Mrs"/>
        <ComboBoxItem Content="Ms"/>
        <ComboBoxItem Content="Miss"/>
    </ComboBox>

  8. Copy the TextBlock and TextBox controls for the email address and telephone number from the customersTabularView Grid control to the new Grid control, placing them after the cTitle ComboBox control. Place the TextBlock controls in column 0, in rows 4 and 5, and the TextBox controls in column 1, in rows 4 and 5. Change the name of the email TextBox control to cEmail and the name of the phone TextBox control to cPhone. Remove the Width properties of the cEmail and cPhone controls, and set their HorizontalAlignment properties to Stretch.

    <ComboBox ...>
    ...
    </ComboBox>
    <TextBlock Grid.Row="4" Grid.Column="0" HorizontalAlignment="Center" TextWrapping="Wrap"
    Text="Email" VerticalAlignment="Center" FontSize="20"/>
    <TextBox Grid.Row="4" Grid.Column="1" x:Name="cEmail" HorizontalAlignment="Stretch"
    TextWrapping="Wrap" Text="" VerticalAlignment="Center" FontSize="20"/>
    <TextBlock Grid.Row="5" Grid.Column="0" HorizontalAlignment="Center" TextWrapping="Wrap"
    Text="Phone" VerticalAlignment="Center" FontSize="20"/>
    <TextBox Grid.Row="5" Grid.Column="1" x:Name="cPhone" HorizontalAlignment="Stretch"
    TextWrapping="Wrap" Text="" VerticalAlignment="Center" FontSize="20"/>

    The Design View window should display the columnar layout like this:

    The Design View window for the Customers application showing the completed columnar layout view.
  9. Return to the XAML markup for the customersTabularView Grid control and set the Visibility property to Visible.

    <Grid x:Name="customersTabularView" Margin="10,20,10,20" Visibility="Visible">

  10. In the XAML markup for the customersColumnarView Grid control, set the Visibility property to Collapsed.

    <Grid x:Name="customersColumnarView" Margin="10,20,10,20" Visibility="Collapsed">

    The Design View window should display the original tabular layout of the Customers form. This is the default view that will be used by the app.

You have now defined the layout that will appear in the narrow view. You might be concerned that in essence all you have done is duplicated many of the controls and laid them out in a different manner. If you run the form and switch between views, how will data in one view transfer to the other? For example, if you enter the details for a customer when the app is running full screen, and then you switch to the narrow view, the newly displayed controls will not contain the same data that you just entered. UWP apps address this problem by using data binding. This is a technique by which you can associate the same piece of data to multiple controls, and as the data changes, all controls display the updated information. You will see how this works in Chapter 26. For the time being, you need to consider only how to use the Visual State Manager to switch between layouts when the view changes.

You can use triggers that alert the Visual State Manager when some aspect (such as the height or width) of the display changes. You can define the visual state transitions performed by these triggers in the XAML markup of your app. This is what you will do in the next exercise.

Use the Visual State Manager to modify the layout

  1. In the XAML pane for the Customers app, after the closing </Grid> tag for the customersColumnarView Grid control, add the following markup:

    <Grid x:Name="customersColumnarView" Margin="10,20,10,20" Visibility="Visible">
        ...
    </Grid>
    <VisualStateManager.VisualStateGroups>
        <VisualStateGroup>
            <VisualState x:Name="TabularLayout">
            </VisualState>
        </VisualStateGroup>
    </VisualStateManager.VisualStateGroups>

    You define the visual state transitions by implementing one or more visual state groups. Each visual state group specifies the transitions that should occur when the Visual State Manager switches to this state. Each state should be given a meaningful name to help you identify its purpose.

  2. Add the following visual state trigger shown in bold to the visual state group:

    <VisualStateManager.VisualStateGroups>
        <VisualStateGroup>
            <VisualState x:Name="TabularLayout">
                <VisualState.StateTriggers>
                    <AdaptiveTrigger MinWindowWidth="660"/>
                </VisualState.StateTriggers>
            </VisualState>
        </VisualStateGroup>
    </VisualStateManager.VisualStateGroups>

    This trigger will fire whenever the width of the window drops below 660 pixels. This is the width at which the controls and labels on the Customers form start to wrap and become difficult to use.

  3. After the trigger definition, add the following code shown in bold to the XAML markup:

    <VisualStateManager.VisualStateGroups>
        <VisualStateGroup>
            <VisualState x:Name="TabularLayout">
                <VisualState.StateTriggers>
                    <AdaptiveTrigger MinWindowWidth="660"/>
                </VisualState.StateTriggers>
                <VisualState.Setters>
                    <Setter Target="customersTabularView.Visibility" Value="Visible"/>
                    <Setter Target="customersColumnarView.Visibility" Value="Collapsed"/>
                </VisualState.Setters>
            </VisualState>
        </VisualStateGroup>
    </VisualStateManager.VisualStateGroups>

    This code specifies the actions that occur when the trigger is fired. In this example, the actions are defined by using Setter elements. A Setter element specifies a property to set and the value to which the property should be set. For this view, the Setter commands change the values of specified properties; the customersTabularView Grid control is made visible and the customersColumnarView Grid control is collapsed (made invisible).

  4. After the TabularLayout visual state definition, add the following markup which defines the equivalent functionality to switch to the columnar view:

    <VisualStateManager.VisualStateGroups>
        <VisualStateGroup>
            <VisualState x:Name="TabularLayout">
                ...
            </VisualState>
            <VisualState x:Name="ColumnarLayout">
                <VisualState.StateTriggers>
                    <AdaptiveTrigger MinWindowWidth="0"/>
                </VisualState.StateTriggers>
                <VisualState.Setters>
                    <Setter Target="customersTabularView.Visibility" Value="Collapsed"/>
                    <Setter Target="customersColumnarView.Visibility" Value="Visible"/>
                </VisualState.Setters>
            </VisualState>
        </VisualStateGroup>
    </VisualStateManager.VisualStateGroups>

    When the window width drops below 660 pixels, the app switches to the ColumnarLayout state; the customersTabularView Grid control is collapsed and the customersColumnarView Grid control is made visible.

  5. In the toolbar, ensure that the Debug Target is set to Local Machine, and then on the Debug menu, click Start Debugging.

    The app starts and displays the Customer form full screen. The data is displayed using the tabular layout.

    Images

    Note If you are using a display with a resolution of less than 1366 × 768, start the app running in the Simulator as described earlier. Configure the Simulator with a resolution of 1366 × 768.

  6. Resize the Customer app window to display the form in a narrow view. When the window width drops below 660 pixels, the display switches to the columnar layout.

  7. Resize the Customer app window to make it wider than 660 pixels (or maximize it to full screen).

    The Customer form reverts to the tabular layout.

  8. Return to Visual Studio and stop debugging.

Applying styles to a UI

Now that you have the mechanics of the basic layout of the app resolved, the next step is to apply some styling to make the UI look more attractive. The controls in a UWP app have a varied range of properties that you can use to change features such as the font, color, size, and other attributes of an element. You can set these properties individually for each control, but this approach can become cumbersome and repetitive if you need to apply the same styling to a number of controls. Also, the best apps apply a consistent styling across the UI, and it is difficult to maintain consistency if you have to repeatedly set the same properties and values as you add or change controls. The more times you have to do the same thing, the greater the chances are that you will get it wrong at least once!

With UWP apps, you can define reusable styles. You can implement them as app-wide resources by creating a resource dictionary, and then they are available to all controls in all pages in an app. You can also define local resources that apply to only a single page in the XAML markup for that page. In the following exercise, you will define some simple styles for the Customers app and apply these styles to the controls on the Customers form.

Define styles for the Customers form

  1. In Solution Explorer, right-click the Customers project, point to Add, and then click New Item.

  2. In the Add New Item - Customers dialog box, click Resource Dictionary. In the Name box, type AppStyles.xaml, and then click Add.

    The AppStyles.xaml file appears in the Code and Text Editor window. A resource dictionary is an XAML file that contains resources that the app can use. The AppStyles.xaml file looks like this:

    <ResourceDictionary
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="using:Customers">
    </ResourceDictionary>

    Styles are one example of a resource, but you can also add other items. In fact, the first resource that you will add is not actually a style but an ImageBrush that will be used to paint the background of the outermost Grid control on the Customers form.

  3. In Solution Explorer, right-click the Customers project, point to Add, and then click New Folder. Change the name of the new folder to Images.

  4. Right-click the Images folder, point to Add, and then click Existing Item.

  5. In the Add Existing Item - Customers dialog box, browse to the Microsoft PressVCSBS Chapter 25Resources folder in your Documents folder, click wood.jpg, and then click Add.

    The wood.jpg file is added to the Images folder in the Customers project. This file contains an image of a tasteful wooden background that you will use for the Customers form.

  6. In the Code and Text Editor window displaying the AppStyles.xaml file, add the following XAML markup shown in bold:

    <ResourceDictionary
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:Customers">

        <ImageBrush x:Key="WoodBrush" ImageSource="Images/wood.jpg"/>
    </ResourceDictionary>

    This markup creates an ImageBrush resource called WoodBrush that is based on the wood.jpg file. You can use this image brush to set the background of a control, and it will display the image in the wood.jpg file.

  7. Underneath the ImageBrush resource, add the following style shown in bold to the AppStyles.xaml file:

    <ResourceDictionary
        ...>
        <ImageBrush x:Key="WoodBrush" ImageSource="Images/wood.jpg"/>
        <Style x:Key="GridStyle" TargetType="Grid">
            <Setter Property="Background" Value="{StaticResource WoodBrush}"/>
        </Style>
    </ResourceDictionary>

    This markup shows how to define a style. A Style element should have a name (a key that enables it to be referenced elsewhere in the app), and it should specify the type of control to which the style can be applied. You are going to use this style with the Grid control.

    The body of a style consists of one or more Setter elements. In this example, the Background property is set to the WoodBrush ImageBrush resource. The syntax is a little curious, though. In a value, you can either reference one of the appropriate system-defined values for the property (such as “Red” if you want to set the background to a solid red color) or specify a resource that you have defined elsewhere. To reference a resource defined elsewhere, you use the StaticResource keyword and then place the entire expression in curly braces.

  8. Before you can use this style, you must update the global resource dictionary for the app in the App.xaml file by adding a reference to the AppStyles.xaml file. In Solution Explorer, double-click App.xaml to display it in the Code and Text Editor window. The App.xaml file looks like this:

    <Application
        x:Class="Customers.App"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="using:Customers"
    RequestedTheme="Light">

    </Application>

    Currently, the App.xaml file defines only the app object and brings a few namespaces into scope; the global resource dictionary is empty.

  9. Add to the App.xaml file the code shown here in bold:

    <Application
        x:Class="Customers.App"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="using:Customers"
    RequestedTheme="Light">

        <Application.Resources>
            <ResourceDictionary>
                <ResourceDictionary.MergedDictionaries>
                    <ResourceDictionary Source="AppStyles.xaml"/>
                </ResourceDictionary.MergedDictionaries>
            </ResourceDictionary>
        </Application.Resources>
    </Application>

    This markup adds the resources defined in the AppStyles.xaml file to the list of resources available in the global resource dictionary. These resources are now available for use throughout the app.

  10. Switch to the MainPage.xaml file displaying the UI for the Customers form. In the XAML pane, find the outermost Grid control:

    <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">

    In the XAML markup for this control, replace the Background property with a Style property that references the GridStyle style, as shown in bold in the following code:

    <Grid Style="{StaticResource GridStyle}">

  11. On the Build menu, click Rebuild Solution.

    The background of the Grid control in the Design View window should switch and display a wooden panel, like this:

    The Customers form painted using the wooden background brush.

    Images

    Note Ideally, you should ensure that any background image that you apply to a page or control maintains its aesthetics as the device form factor and orientation change. An image that looks cool on a 30-inch monitor might appear distorted and squashed on a Windows phone. It might be necessary to provide alternative backgrounds for different views and orientations and use the Visual State Manager to modify the Background property of a control to switch between them as the visual state changes.

  12. Return to AppStyles.xaml in the Code and Text Editor window and add the following FontStyle style after the GridStyle style:

    <Style x:Key="GridStyle" TargetType="Grid">
        ...
    </Style>
    <Style x:Key="FontStyle" TargetType="TextBlock">
        <Setter Property="FontFamily" Value="Segoe Print"/>
    </Style>

    This style applies to TextBlock elements and changes the font to Segoe Print. This font resembles a handwriting style.

    At this stage, it would be possible to reference the FontStyle style in every TextBlock control that required this font, but this approach would not provide any advantage over simply setting the FontFamily directly in the markup for each control. The real power of styles occurs when you combine multiple properties, as you will see in the next few steps.

  13. Add the HeaderStyle style shown here to the AppStyles.xaml file:

    <Style x:Key="FontStyle" TargetType="TextBlock">
        ...
    </Style>
    <Style x:Key="HeaderStyle" TargetType="TextBlock" BasedOn="{StaticResource FontStyle}">
        <Setter Property="HorizontalAlignment" Value="Center"/>
        <Setter Property="TextWrapping" Value="Wrap"/>
        <Setter Property="VerticalAlignment" Value="Center"/>
        <Setter Property="Foreground" Value="SteelBlue"/>
    </Style>

    This is a composite style that sets the HorizontalAlignment, TextWrapping, VerticalAlignment, and Foreground properties of a TextBlock. Additionally, the HeaderStyle style references the FontStyle style by using the BasedOn property. The BasedOn property provides a simple form of inheritance for styles.

    You will use this style to format the labels that appear at the top of the customersTabularView and customersColumnarView controls. However, these headings have different font sizes (the heading for the tabular layout is bigger than that of the columnar layout), so you will create two more styles that extend the HeaderStyle style.

  14. Add the following styles to the AppStyles.xaml file:

    <Style x:Key="HeaderStyle" TargetType="TextBlock" BasedOn="{StaticResource FontStyle}">
        ...
    </Style>
    <Style x:Key="TabularHeaderStyle" TargetType="TextBlock"
    BasedOn="{StaticResource HeaderStyle}">
        <Setter Property="FontSize" Value="40"/>
    </Style>
    <Style x:Key="ColumnarHeaderStyle" TargetType="TextBlock"
    BasedOn="{StaticResource HeaderStyle}">
        <Setter Property="FontSize" Value="30"/>
    </Style>

    Note that the font sizes for these styles are slightly smaller than the font sizes currently used by the headings in the Grid controls. This is because the Segoe Print font is bigger than the default font.

  15. Switch back to the MainPage.xaml file and find the XAML markup for the TextBlock control for the Adventure Works Customers label in the customersTabularView Grid control:

    <TextBlock Grid.Row="0" HorizontalAlignment="Center" TextWrapping="Wrap"
    Text="Adventure Works Customers" VerticalAlignment="Center" FontSize="50"/>

  16. Change the properties of this control to reference the TabularHeaderStyle style, as shown in bold in the following code:

    <TextBlock Grid.Row="0" Style="{StaticResource TabularHeaderStyle}"
    Text="Adventure Works Customers"/>

    The heading displayed in the Design View window should change color, size, and font and look like this:

    The restyled heading of the Customers form in the tabular view.
  17. Find the XAML markup for the TextBlock control for the Customers label in the customersColumnarView Grid control:

    <TextBlock Grid.Row="0" HorizontalAlignment="Center" TextWrapping="Wrap"
    Text="Customers" VerticalAlignment="Center" FontSize="30"/>

    Modify the markup of this control to reference the ColumnarHeaderStyle style, as shown here in bold:

    <TextBlock Grid.Row="0" Style="{StaticResource ColumnarHeaderStyle}"
    Text="Customers"/>

    Be aware that you won’t see this change in the Design View window because the customersColumnarView Grid control is collapsed by default. However, you will see the effects of this change when you run the app later in this exercise.

  18. Return to the AppStyles.xaml file in the Code and Text Editor window. Modify the HeaderStyle style with the additional property Setter elements shown in bold in the following example:

    <Style x:Key="HeaderStyle" TargetType="TextBlock" BasedOn="{StaticResource FontStyle}">
        <Setter Property="HorizontalAlignment" Value="Center"/>
        <Setter Property="TextWrapping" Value="Wrap"/>
        <Setter Property="VerticalAlignment" Value="Center"/>
        <Setter Property="Foreground" Value="SteelBlue"/>
        <Setter Property="RenderTransformOrigin" Value="0.5,0.5"/>
        <Setter Property="RenderTransform">
            <Setter.Value>
                <CompositeTransform Rotation="-5"/>
            </Setter.Value>
        </Setter>
    </Style>

    These elements rotate the text displayed in the header about its midpoint by an angle of 5 degrees by using a transformation.

    Images

    Note This example shows a simple transformation. Using the RenderTransform property, you can perform a variety of other transformations to an item, and you can combine multiple transformations. For example, you can translate (move) an item on the x- and y-axes, skew the item (make it lean), and scale an element.

    You should also notice that the value of the RenderTransform property is itself another property/value pair (the property is Rotation, and the value is –5). In cases such as this, you specify the value by using the <Setter.Value> tag.

  19. Switch to the MainPage.xaml file. In the Design View window, the title should now be displayed at a jaunty angle (you might need to rebuild the application first before the updated style is applied):

    The header for the Customers form. The title is displayed at an upwards angle.
  20. In the AppStyles.xaml file, add the following style:

    <Style x:Key="LabelStyle" TargetType="TextBlock" BasedOn="{StaticResource FontStyle}">
        <Setter Property="FontSize" Value="20"/>
        <Setter Property="HorizontalAlignment" Value="Center"/>
        <Setter Property="TextWrapping" Value="Wrap"/>
        <Setter Property="VerticalAlignment" Value="Center"/>
        <Setter Property="Foreground" Value="AntiqueWhite"/>
    </Style>

    You will apply this style to the TextBlock elements that provide the labels for the various TextBox and ComboBox controls that the user employs to enter customer information. The style references the same font style as the headings but sets the other properties to values more appropriate for the labels.

  21. Go back to the MainPage.xaml file. In the XAML pane, modify the markup for the TextBlock controls for each of the labels in the customersTabularView and customersColumnarView Grid controls. Remove the HorizontalAlignment, TextWrapping, VerticalAlignment, and FontSize properties, and reference the LabelStyle style, as shown here in bold:

    <Grid x:Name="customersTabularView" Margin="10,20,10,20" Visibility="Visible">
        ...
        <Grid Grid.Row="2">
            ...
            <TextBlock Grid.Row="0" Grid.Column="1" Style="{StaticResource LabelStyle}"
    Text="ID"/>
            <TextBlock Grid.Row="0" Grid.Column="3" Style="{StaticResource LabelStyle}"
    Text="Title"/>
            <TextBlock Grid.Row="0" Grid.Column="5" Style="{StaticResource LabelStyle}"
    Text="First Name"/>
            <TextBlock Grid.Row="0" Grid.Column="7" Style="{StaticResource LabelStyle}"
    Text="Last Name"/>
            ...
            <TextBlock Grid.Row="3" Grid.Column="1" Style="{StaticResource LabelStyle}"
    Text="Email"/>
            ...
            <TextBlock Grid.Row="5" Grid.Column="1" Style="{StaticResource LabelStyle}"
    Text="Phone"/>
            ...
        </Grid>
    </Grid>
    <Grid x:Name="customersColumnarView" Margin="10,20,10,20" Visibility="Collapsed">
        ...
        <Grid Grid.Row="1">
            ...
            <TextBlock Grid.Row="0" Grid.Column="0" Style="{StaticResource LabelStyle}"
    Text="ID"/>
            <TextBlock Grid.Row="1" Grid.Column="0" Style="{StaticResource LabelStyle}"
    Text="Title"/>
            <TextBlock Grid.Row="2" Grid.Column="0" Style="{StaticResource LabelStyle}"
    Text="First Name"/>
            <TextBlock Grid.Row="3" Grid.Column="0" Style="{StaticResource LabelStyle}"
    Text="Last Name"/>
            ...
            <TextBlock Grid.Row="4" Grid.Column="0" Style="{StaticResource LabelStyle}"
    Text="Email"/>
            ...
            <TextBlock Grid.Row="5" Grid.Column="0" Style="{StaticResource LabelStyle}"
    Text="Phone"/>
            ...
        </Grid>
    </Grid>

    The labels on the form should change to the Segoe Print font and be displayed in white, in a font size of 30 points:

    The completed Customers form, showing the styling applied to the controls.
  22. On the Debug menu, click Start Debugging to build and run the app.

    Images

    Note Use the Simulator if you are running on a display with a resolution less than  1366 × 768.

    The Customers form should appear and be styled in the same way that it appears in the Design View window in Visual Studio. Notice that if you enter any text into the various fields on the form, they use the default font and styling for the TextBox controls.

    Images

    Note Although the Segoe Print font is good for labels and titles, it is not recommended as a font for data-entry fields because some of the characters can be difficult to distinguish from one another. For example, the lowercase letter l is very similar to the digit 1, and the uppercase letter O is almost indistinguishable from the digit 0. For this reason, it makes sense to stick with the default font for the TextBox controls.

  23. Resize the window to make it narrower and verify that the styling has been applied to the controls in the customersColumnarView grid. The form should look like this:

    The Customers form running in the columnar view, showing the styling applied to the controls.
  24. Return to Visual Studio and stop debugging.

You can see that by using styles, you can easily implement a number of really cool effects. Also, careful use of styles makes your code much more maintainable than it would be if you set properties on individual controls. For example, if you want to switch the font used by the labels and headings in the Customers app, you need to make only a single change to the FontStyle style. In general, you should use styles wherever possible; besides assisting maintainability, the use of styles helps to keep the XAML markup for your forms clean and uncluttered, and the XAML for a form needs to specify only the controls and layout rather than how the controls should appear on the form. You can also use Microsoft Blend for Visual Studio 2017 to define complex styles that you can integrate into an app. Professional graphics artists can use Blend to develop custom styles and provide these styles in the form of XAML markup to developers building apps. All the developer has to do is add the appropriate Style tags to the user interface elements to reference the corresponding styles.

Summary

In this chapter, you learned how to use the Grid control to implement a user interface that can scale to different device form factors and orientations. You also learned how to use the Visual State Manager to adapt the layout of controls when the user changes the size of the window displaying the app. Finally, you learned how to create custom styles and apply them to the controls on a form. Now that you have defined the user interface, the next challenge is to add functionality to the app, enabling the user to display and update data, which is what you will do in the final chapters.

  • If you want to continue to the next chapter, keep Visual Studio 2017 running and turn to Chapter 26.

  • If you want to exit Visual Studio 2017 now, on the File menu, click Exit. If you see a Save dialog box, click Yes and save the project.

Quick reference

To

Do this

Create a new UWP app

Use one of the UWP templates in Visual Studio 2017, such as the Blank App template.

Implement a user interface that scales to different device form factors and orientations

Use a Grid control. Divide the Grid control into rows and columns, and place controls in these rows and columns rather than specifying an absolute location relative to the edges of the Grid.

Implement a user interface that can adapt to different display widths

Create different layouts for each view that display the controls in an appropriate manner. Use the Visual State Manager to select the layout to display when the visual state changes.

Create custom styles

Add a resource dictionary to the app. Define styles in this dictionary by using the <Style> element, and specify the properties that each style changes. For example:

<Style x:Key="GridStyle" TargetType="Grid">
    <Setter Property="Background" Value="{StaticResource WoodBrush}"/>
</Style>

Apply a custom style to a control

Set the Style property of the control and reference the style by name. For example:

<Grid Style="{StaticResource GridStyle}">

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.15.190.144