Поделиться через


Build your design data with Blend - The Lorem Ipsum Power

Lorem Ipsum ?

A short wikipedia definition to start this post :

In publishing and graphic design, lorem ipsum is placeholder text (filler text) commonly used to demonstrate the graphic elements of a document or visual presentation, such as font, typography, and layout, by removing the distraction of meaningful content. The lorem ipsum text is typically a section of a Latin text by Cicero with words altered, added and removed that make it nonsensical in meaning and not proper Latin.

Ok ... but what is the relationship between Cicero and Windows Phone ?

Microsoft has long used the Lorem Ipsum in their products to optimize the layout before we have finalized the content. You can for example use the function = lorem() in Word to see a dummy paragraph (cf. https://support.microsoft.com/kb/212251).

Well, but still no connection with Windows Phone. The purpose of this article is to explain how Blend for Visual Studio can make your life easier by generating dummy data for your Windows Phone interface.

1. The data model

However, before arriving at the Blend step, you still have a minimum of work to build, including the data model that will be used by the application.

In our example, I start developement from an almost empty project (Windows Phone App classical model) and add the following SessionDataSource.cs file:

 using System;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Windows.Media;

namespace Sample.Data
{
    /// <summary>
    /// Session item data model.
    /// </summary>
    public class SessionDataItem : INotifyPropertyChanged
    {
        public SessionDataItem(string title, string speaker, string imagePath, string description)
        {
            this._title = title;
            this._speaker = speaker;
            this._imagePath = imagePath;
            this._description = description;
        }

        private string _title = string.Empty;
        public string Title
        {
            get { return this._title; }
            set { this.SetProperty(ref this._title, value); }
        }

        private string _speaker = string.Empty;
        public string Speaker
        {
            get { return this._speaker; }
            set { this.SetProperty(ref this._speaker, value); }
        }

        private string _imagePath = null;
        public string ImagePath
        {
            get { return this._imagePath; }
            set { this.SetProperty(ref this._imagePath, value); }
        }

        private string _description = string.Empty;
        public string Description
        {
            get { return this._description; }
            set { this.SetProperty(ref this._description, value); }
        }

        /// <summary>
        /// Multicast event for property change notifications.
        /// </summary>
        public event PropertyChangedEventHandler PropertyChanged;

        /// <summary>
        /// Checks if a property already matches a desired value.  Sets the property and
        /// notifies listeners only when necessary.
        /// </summary>
        protected bool SetProperty<T>(ref T storage, T value, [CallerMemberName] string propertyName = null)
        {
            if (object.Equals(storage, value)) return false;

            storage = value;
            this.NotifyPropertyChanged(propertyName);
            return true;
        }

        /// <summary>
        /// Notifies listeners that a property value has changed.
        /// </summary>
        private void NotifyPropertyChanged([CallerMemberName] string propertyName = null)
        {
            PropertyChangedEventHandler handler = PropertyChanged;
            if (null != handler)
            {
                handler(this, new PropertyChangedEventArgs(propertyName));
            }
        }
    }
}

 

The SessionDataItem class declares only four properties (Title, Speaker, ImagePath and Description) ; and implement the INotifyPropertyChanged interface. Not necessarily useful in the following discussion but it is an opportunity to demonstrate usage of the new [CallerMemberName] attribute for Windows Phone 8. This attribute avoid typing the name of the property when calling NotifyPropertyChanged method, the compiler does the work for you ! The purpose is mainly to avoid mistakes when you rename a property.

SetProperty<T> ensures that PorpertyChanged event is only triggered when the value has been changed (and not for each call to the setter).

 

2. Editing a new page

For the purpose of this demonstration, I add a new page to my project named DetailsPage (such as Windows Phone Portrait Page), it is simply composed of a grid with four controls (Image and three TextBlock) to host previous properties content. Nothing complicated, but you can copy the following XAML to save time:

 <phone:PhoneApplicationPage
    x:Class="Sample.DetailsPage"
    xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
    xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
    xmlns:d="https://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="https://schemas.openxmlformats.org/markup-compatibility/2006"
    FontFamily="{StaticResource PhoneFontFamilyNormal}"
    FontSize="{StaticResource PhoneFontSizeNormal}"
    Foreground="{StaticResource PhoneForegroundBrush}"
    SupportedOrientations="Portrait" Orientation="Portrait"
    mc:Ignorable="d"
    shell:SystemTray.IsVisible="True">

    <!--LayoutRoot is the root grid where all page content is placed-->
    <Grid x:Name="LayoutRoot" Background="Transparent">
        <Grid.RowDefinitions>
            <RowDefinition Height="200"/>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>
        <Border Grid.Row="0" Background="Blue">
            <Image Name="BackgroundImage" Stretch="UniformToFill"/>
        </Border>
        <TextBlock Name="TitleTextBlock" Grid.Row="1"/>
        <TextBlock Name="SpeakerTextBlock" Grid.Row="2"/>
        <TextBlock Name="DescriptionTextBlock" TextWrapping="Wrap" Grid.Row="3"/>
    </Grid>
</phone:PhoneApplicationPage>

Save your project. then open the page in Blend for Visual Studio (from context menu available on DetailsPage.xaml). We get the following outcome 'almost' sensational ... well, I admit nothing great, we can do better ... and in addition quickly !

image_thumb5

3. The dummy data to facilitate our design

We enter the heart of the matter; Blend offers a Data section available in the top right corner of the interface. If it is not present, you can activate it from the Window menu.

Create Sample Data from Class… option will generate dummy data file (in our case SessionDataItemSampleData.xaml) whose structure is based on our SessionDataItem class. The purpose is not to use this data during the execution of the application, but simply help design the visual presentation of our interface : you can apply styles, change font, typography, and layout  before hosting the real data.

image_thumb[1]

I suggest now to check out the following video to learn how to use this feature. Not so bad for 3 minutes of work?

What changes have been made to the project by Blend?

- A file in xaml  (SessionDataItemSampleData.xaml) that contains dummy data from our various properties. You may notice in the demonstration that the Lorem Ipsum can be replaced by more specific formats if needed (name, address, phone number, url ...). This file can also be edited manually (eg in the case of images, to target graphics located in your project).

- A Design DataContext (d: DataContext) that points to the previous file, and the associated DataBinding on each control. The final outcome gives:

 <phone:PhoneApplicationPage
    x:Class="Sample.DetailsPage"
    xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
    xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
    xmlns:d="https://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="https://schemas.openxmlformats.org/markup-compatibility/2006"
    FontFamily="{StaticResource PhoneFontFamilyNormal}"
    FontSize="{StaticResource PhoneFontSizeNormal}"
    Foreground="{StaticResource PhoneForegroundBrush}"
    SupportedOrientations="Portrait" Orientation="Portrait"
    mc:Ignorable="d"
    shell:SystemTray.IsVisible="True">

    <!--LayoutRoot is the root grid where all page content is placed-->
    <Grid x:Name="LayoutRoot" Background="Transparent" 
          d:DataContext="{d:DesignData /SampleData/SessionDataItemSampleData.xaml, Instance={x:Null}}">
        <Grid.RowDefinitions>
            <RowDefinition Height="200"/>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>
        <Border Grid.Row="0" Background="Blue" Margin="12,0">
            <Image Name="BackgroundImage" Stretch="UniformToFill" Source="{Binding ImagePath}"/>
        </Border>
        <TextBlock Name="TitleTextBlock" Grid.Row="1" Text="{Binding Title}" 
                   Style="{StaticResource PhoneTextLargeStyle}"/>
        <TextBlock Name="SpeakerTextBlock" Grid.Row="2" Text="{Binding Speaker}"
                   Style="{StaticResource PhoneTextNormalStyle}"/>
        <TextBlock Name="DescriptionTextBlock" TextWrapping="Wrap" Grid.Row="3" Text="{Binding Description}" 
                   Style="{StaticResource PhoneTextSubtleStyle}" Margin="12,12,12,0"/>
    </Grid>
</phone:PhoneApplicationPage>

 

Your application is now ready to receive real data (or almost).

 

image_thumb[3]