Responsive Design in Universal Windows Apps

2015-10-18 12:25:20来源:作者:人点击

I’ve been working a little bit in Universal Windows apps recently as part of my onboarding of mobile apps at Microsoft Azure. It’s been a fun time. One of the things I found hard was to handle responsive design. How does one design for both a tablet and a phone, plus the various orientations of the devices? After all, the layout doesn’t just change. In HTML land, you have media queries– you can use CSS3 to control the visibility of the various pieces.

It turns out that there are two pieces you need to understand. The first is a XAML piece – defining a visual state managerthat does the same thing as the content of the media queries. It alters the style of the components of the page. The second is a piece of code that sets the appropriate visual state. This can be considered the definition of the media query.

To start with, I created a Universal Windows App. I opened the MainPage.xaml file and put in the following XAML:

<Pagex:Class="QuickStart.UWP.MainPage"xmlns=""xmlns:x=""xmlns:local="using:QuickStart.UWP"xmlns:d=""xmlns:mc=""mc:Ignorable="d"><Grid x:Name="LayoutRoot" Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"><Grid.RowDefinitions><RowDefinition Height="Auto"/><RowDefinition Height="Auto"/><RowDefinition Height="*"/><RowDefinition Height="26"/></Grid.RowDefinitions><StackPanel x:Name="TitlePanel" Grid.Row="0" Background="#00abec"><TextBlock x:Name="TopBrandName" Margin="10,4,10,0" FontSize="28" Foreground="LightGray">Azure App Service Mobile Apps</TextBlock><TextBlock x:Name="TopProductName" Margin="10,0,10,0" FontSize="45" Foreground="White">Task List</TextBlock></StackPanel><Grid x:Name="NewTaskPanel" Grid.Row="1" Background="#00abec"><Grid.ColumnDefinitions><ColumnDefinition Width="*"/><ColumnDefinition Width="Auto"/></Grid.ColumnDefinitions><TextBox x:Name="NewTaskContent" Grid.Column="0" Margin="10,4,10,4" PlaceholderText="Enter a new task..."/><Button x:Name="SaveTaskButton" Grid.Column="1" Margin="10,4,10,4">Save</Button></Grid><ScrollViewer Grid.Row="2"></ScrollViewer><AppBar Grid.Row="3"></AppBar></Grid></Page>

This looks really good – I’ve got a brand title and a product title, my “enter a task” box with a save button and a couple of other areas that I haven’t filled in yet. The problem is in running this. If you go to a narrow screen, the title gets cut off, which is not a good experience. In addition, if I turn the device into a landscape mode, the title take a good chunk of real estate. I’ve decided I want to turn off the brand title when I’m on a smaller screen.

The first step is to define two visual states. I’ll have a Normal state and a Narrow state, defined like this:

<VisualStateManager.VisualStateGroups><VisualStateGroup x:Name="VisualStateGroup"><VisualState x:Name="Narrow"><Storyboard><!-- List my changes to the normal here --></Storyboard></VisualState><VisualState x:Name="Normal"/></VisualStateGroup></VisualStateManager.VisualStateGroups>

My existing layout is my “normal” layout. I’m going to make some changes inside the storyboard section for the narrow layout. In my case, I’m going to make the visibility of the title block “collapsed” which will turn it off:

<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Visibility)" Storyboard.TargetName="TopBrandName"> <DiscreteObjectKeyFrame KeyTime="0"> <DiscreteObjectKeyFrame.Value> <Visibility>Collapsed</Visibility> </DiscreteObjectKeyFrame.Value> </DiscreteObjectKeyFrame></ObjectAnimationUsingKeyFrames>

This may seem like ludicrous amounts of code to turn something off – it allows you to do animations when a device goes from one state to another, which may result in a more pleasing effect. For my purposes, this isn’t a tutorial on animations, so I can just turn the visibility to collapsed. I can turn different properties to different things.

I don’t have to have a story board for the normal layout – I’ve already defined it.

Now for the code. In the MainPage.xaml.cscode behind file, I need to add an event handler for when the device size changes:

public MainPage() { this.InitializeComponent(); SizeChanged += MainPage_SizeChanged; }

This, of course, means I have to define a Size Changed function as well.

private void MainPage_SizeChanged(object sender, SizeChangedEventArgs e) { var state = "Normal"; if (e.NewSize.Width < 600 || e.NewSize.Height < 600) { state = "Narrow"; } VisualStateManager.GoToState(this, state, true);}

I’ve named the visual states in the XAML code (check out the x:Name properties in the VisualState tag). I have to tell the VisualStateManager to change to that state. It will then apply the changes to the layout for me. My default state is “Normal”. I set the state explicitly to “Narrow” if I want the narrow styling to be applied – in this case, if the width or height of the new window is too small.

Run this project and resize the window – as it gets smaller, you will note the title at the top removed, but it is put back again when the display is widened.

You can have as many visual states as you want and adjust everything about the layout that you need. Visual Studio is also distributed with another tool called Blend that makes visual state editing easy to do. If you are more of a visual editor, check it out. There are plenty of tutorials out of the web now you know what the feature is called.