I am making a Win Store App, and facing the issue to handle orientation.
There is a grid, I want to modify the design of it when the orientation is changed. The stack Panels in both the sides (in Landscape View) should goto Top and Bottom in Portrait View.
Currently I have created two grids with all the controls and adjust them according to the orientation, and switching the visibility property, using VisualStateManager.
But I want to achieve this with a single grid, any suggestion will be a great help for me.
The below image is the concept design of the app:
Doing this with a single Grid is difficult, but possible.
Firstly, what you probably want to do is worry less about orientation and more about horizontal width. That way, if the user snaps the app in such a way that there isn't much horizontal space (similar to portrait mode), you can display a similar experience.
Now, onto the Grid.
The key here is that you are moving from a Grid
with, in essence, 3 Columns to one with 1 Column. As such, you will need to modify the ColumnSpan
Property of your cells.
What you have drawn so far has 3 Columns
, each with a Width
of GridLength *
. Start there.
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
</Grid>
Next, we want to set up the Rows
. It's better to make the number of Rows the largest that you'll need, which is by my count 6. If you set them all to Auto
, then the empty Rows
(in the Landscape orientation) will disappear.
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Width="Auto"/>
<RowDefinition Width="Auto"/>
<RowDefinition Width="Auto"/>
<RowDefinition Width="Auto"/>
<RowDefinition Width="Auto"/>
<RowDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
</Grid>
Now, to create some basic contents. I'm just going to create a bunch of different StackPanel
s and name them what you have contained in them.
I am setting the default Grid/Row
values to that of Landscape
. I am also using two rows for the 'Second Row' (containing StackPanel
s 1 and 2). Both StackPanel
s will have a RowSpan
of 2 to compensate.
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Width="Auto"/>
<RowDefinition Width="Auto"/>
<RowDefinition Width="Auto"/>
<RowDefinition Width="Auto"/>
<RowDefinition Width="Auto"/>
<RowDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<StackPanel Name="HeaderRow" Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="3"/>
<StackPanel Name="StackPanel1" Grid.Row="1" Grid.Column="0" Grid.RowSpan="2"/>
<StackPanel Name="Content" Grid.Row="1" Grid.Column="1"/>
<StackPanel Name="BlueSection" Grid.Row="2" Grid.Column="1"/>
<StackPanel Name="StackPanel2" Grid.Row="1" Grid.Column="2" Grid.RowSpan="2"/>
<StackPanel Name="FooterRow" Grid.Row="3" Grid.Column="0" Grid.ColumnSpan="3"/>
</Grid>
Now we have a nice definition for your Landscape
orientation. It will automatically size vertically based on the content within it as well, though you are free to do whatever minimum sizing you want.
Now that we've done this, we just need to create some VisualStates
for our different sizes. In one VisualState
we will be defining exactly what we've already done. In the other, we will be creating a new one. I am naming them 'Landscape' and 'Portrait' for now, but I suggest doing something like 'Mid' and 'Wide', and then defining which is used based on the Bounds
given. I have omitted the previous definitions for the sake of space.
Technically, we don't need to include the Landscape
definition, but I'm including it as an additional example.
Note: In the second picture, you have StackPanel2
below Footer
. I have reflected this below. If you need that changed, just change the row definitions of StackPanel2
to 4
and of Footer
to 5
.
<Grid>
...
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="OrientationStates">
<!-- What we've already defined -->
<VisualState x:Name="Landscape">
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(Grid.RowSpan)" Storyboard.TargetName="StackPanel1">
<DiscreteObjectKeyFrame KeyTime="0" Value="2"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(Grid.ColumnSpan)" Storyboard.TargetName="StackPanel1">
<DiscreteObjectKeyFrame KeyTime="0" Value="1"/>
</ObjectAnimationUsingKeyFrames>
<!-- Stack Panel 2 -->
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(Grid.RowSpan)" Storyboard.TargetName="StackPanel2">
<DiscreteObjectKeyFrame KeyTime="0" Value="2"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(Grid.ColumnSpan)" Storyboard.TargetName="StackPanel2">
<DiscreteObjectKeyFrame KeyTime="0" Value="1"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(Grid.Row)" Storyboard.TargetName="StackPanel2">
<DiscreteObjectKeyFrame KeyTime="0" Value="1"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(Grid.Column)" Storyboard.TargetName="StackPanel2">
<DiscreteObjectKeyFrame KeyTime="0" Value="2"/>
</ObjectAnimationUsingKeyFrames>
<!-- Content -->
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(Grid.Row)" Storyboard.TargetName="Content">
<DiscreteObjectKeyFrame KeyTime="0" Value="1"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(Grid.Column)" Storyboard.TargetName="Content">
<DiscreteObjectKeyFrame KeyTime="0" Value="1"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(Grid.ColumnSpan)" Storyboard.TargetName="Content">
<DiscreteObjectKeyFrame KeyTime="0" Value="1"/>
</ObjectAnimationUsingKeyFrames>
<! -- Blue Section -->
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(Grid.Row)" Storyboard.TargetName="BlueSection">
<DiscreteObjectKeyFrame KeyTime="0" Value="2"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(Grid.Column)" Storyboard.TargetName="BlueSection">
<DiscreteObjectKeyFrame KeyTime="0" Value="1"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(Grid.ColumnSpan)" Storyboard.TargetName="BlueSection">
<DiscreteObjectKeyFrame KeyTime="0" Value="1"/>
</ObjectAnimationUsingKeyFrames>
<!-- Footer -->
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(Grid.Row)" Storyboard.TargetName="Footer">
<DiscreteObjectKeyFrame KeyTime="0" Value="3"/>
</ObjectAnimationUsingKeyFrames>
</VisualState>
<!-- The New Section -->
<VisualState x:Name="Portrait">
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(Grid.RowSpan)" Storyboard.TargetName="StackPanel1">
<DiscreteObjectKeyFrame KeyTime="0" Value="1"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(Grid.ColumnSpan)" Storyboard.TargetName="StackPanel1">
<DiscreteObjectKeyFrame KeyTime="0" Value="3"/>
</ObjectAnimationUsingKeyFrames>
<!-- Stack Panel 2 -->
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(Grid.RowSpan)" Storyboard.TargetName="StackPanel2">
<DiscreteObjectKeyFrame KeyTime="0" Value="1"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(Grid.ColumnSpan)" Storyboard.TargetName="StackPanel2">
<DiscreteObjectKeyFrame KeyTime="0" Value="3"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(Grid.Row)" Storyboard.TargetName="StackPanel2">
<DiscreteObjectKeyFrame KeyTime="0" Value="5"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(Grid.Column)" Storyboard.TargetName="StackPanel2">
<DiscreteObjectKeyFrame KeyTime="0" Value="0"/>
</ObjectAnimationUsingKeyFrames>
<!-- Content -->
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(Grid.Row)" Storyboard.TargetName="Content">
<DiscreteObjectKeyFrame KeyTime="0" Value="2"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(Grid.Column)" Storyboard.TargetName="Content">
<DiscreteObjectKeyFrame KeyTime="0" Value="0"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(Grid.ColumnSpan)" Storyboard.TargetName="Content">
<DiscreteObjectKeyFrame KeyTime="0" Value="3"/>
</ObjectAnimationUsingKeyFrames>
<! -- Blue Section -->
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(Grid.Row)" Storyboard.TargetName="BlueSection">
<DiscreteObjectKeyFrame KeyTime="0" Value="3"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(Grid.Column)" Storyboard.TargetName="BlueSection">
<DiscreteObjectKeyFrame KeyTime="0" Value="0"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(Grid.ColumnSpan)" Storyboard.TargetName="BlueSection">
<DiscreteObjectKeyFrame KeyTime="0" Value="3"/>
</ObjectAnimationUsingKeyFrames>
<!-- Footer -->
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(Grid.Row)" Storyboard.TargetName="Footer">
<DiscreteObjectKeyFrame KeyTime="0" Value="4"/>
</ObjectAnimationUsingKeyFrames>
</VisualState>
</VisualStateGroup>
<VisualStateManager.VisualStateGroups>
</Grid>
You're almost done. All you need left is for your Page
to listen for WindowSizeChanged
and respond to it! It needs to get a reference to your Grid
. As such, I suggest adding x:Name="ContentGrid"
or something similar to the base Grid
definition. Such as:
<Grid x:Name="ContentGrid">
My example for WindowSizeChanged
will assume you have done this.
private void WindowSizeChanged(object sender, WindowSizeChangedEventArgs windowSizeChangedEventArgs)
{
if (windowSizeChangedEventArgs.Size.Width < 800)
{
VisualStateManager.GoToState(ContentGrid, "Portrait", true);
}
else
{
VisualStateManager.GoToState(ContentGrid, "Landscape", true);
}
}
Then, in your Page
's OnNavigatedTo
and OnNavigatedFrom
...
protected override void OnNavigatedTo(NavigationEventArgs e)
{
Window.Current.SizeChanged += WindowSizeChanged;
}
protected override void OnNavigatedFrom(NavigationEventArgs e)
{
Window.Current.SizeChanged -= WindowSizeChanged;
}
Make sure you remove the SizeChanged
event
handler on navigating from the page, otherwise your app will retain a copy of your page in memory indefinitely. This is how to create a fun memory leak.
And that's it! On size changed, your main page will notify your Grid that it needs to reconfigure itself and it will do so in the way described. Obviously you won't be able to directly paste this in, but you should be able to use a lot of it. Make sure to change the TargetNames
to your named controls.
Hope this helps and happy coding!
来源:https://stackoverflow.com/questions/22630018/handling-orientation-in-windows-8-1-store-app