Users can rotate mobile devices, and it’s important to adapt your UI according to the current orientation. The .NET MAUI platform doesn’t have a built-in class to set orientation-specific settings in XAML; however, it provides a visual state mechanism. Alternatively, you can set orientation-based properties in C#. In this recipe, we’ll implement both techniques.
Getting ready
To follow the steps described in this recipe, we just need to create a blank .NET MAUI application. The default template includes sample code in the MainPage.xaml
and MainPage.xaml.cs
files, but you can remove it and leave only a blank ContentPage
in XAML and a constructor with the InitializeComponent
method in CS. When copying code snippets with namespaces, don’t forget to replace them with the namespaces in your project.
The code for this recipe is available at https://github.com/PacktPublishing/.NET-MAUI-Cookbook/tree/main/Chapter01/c1-OrientationSpecificSettings.
How to do it…
Let’s use two different techniques to specify layout settings based on the orientation – set settings in XAML using visual states and set settings in C# by subscribing to the MainDisplayInfoChanged
event, which is raised when the orientation is changed:
- To specify orientation-based settings in XAML, define
VisualStateGroup
with Portrait
and Landscape
visual states. In visual state setters, reference the elements you would like to customize by their names. Note that it’s necessary to explicitly specify the parent property class in each setter:
MainPage.xaml
<VisualStateManager.VisualStateGroups>
<VisualStateGroupList>
<VisualStateGroup>
<VisualState x:Name="Portrait">
<VisualState.StateTriggers>
<OrientationStateTrigger
Orientation="Portrait" />
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter TargetName="rootGrid"
Property="Grid.RowDefinitions"
Value="*,*" />
<Setter TargetName="border2"
Property="Grid.Row"
Value="1"/>
</VisualState.Setters>
</VisualState>
<VisualState x:Name="Landscape">
<VisualState.StateTriggers>
<OrientationStateTrigger
Orientation="Landscape" />
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter TargetName="rootGrid"
Property="Grid.RowDefinitions"
Value="*,*" />
<Setter TargetName="border2"
Property="Grid.Row"
Value="1"/>
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateGroupList>
</VisualStateManager.VisualStateGroups>
<Grid x:Name="rootGrid">
<Border x:Name="border1" BackgroundColor="Coral"/>
<Border x:Name="border2" Background="DarkRed"/>
</Grid>
In the preceding code snippet, we set Grid.RowDefinitions
and Grid.Row
for the grid and border elements, referenced using the TargetName
property. The VisualStateManager.VisualStateGroups
property is defined at the page/view level, which allows you to set all orientation-based settings in one place instead of creating visual states in each element. You will see the following result if you run the project and try to rotate a device:
Figure 1.6 – Landscape layout customization
- To specify orientation-based settings in C#, subscribe to the
DeviceDisplay.MainDisplayInfoChanged
event and modify all required settings in the event handler:
MainPage.xaml.cs
protected override void OnNavigatedTo(NavigatedToEventArgs args) {
base.OnNavigatedTo(args);
SetOrientationSpecificSettings();
DeviceDisplay.MainDisplayInfoChanged +=
OnMainDisplayInfoChanged;
}
protected override void OnNavigatedFrom(NavigatedFromEventArgs args) {
base.OnNavigatedFrom(args);
DeviceDisplay.MainDisplayInfoChanged -=
OnMainDisplayInfoChanged;
}
private void OnMainDisplayInfoChanged(object? sender,
DisplayInfoChangedEventArgs e) {
SetOrientationSpecificSettings();
}
void SetOrientationSpecificSettings() {
if (DeviceDisplay.MainDisplayInfo.Orientation ==
DisplayOrientation.Landscape)
border1.BackgroundColor = Colors.Red;
else
border1.BackgroundColor = Colors.Green;
}
Don’t forget to unsubscribe from the MainDisplayInfoChanged
event; otherwise, DeviceDisplay
will always hold a reference of the view, and it will never be released, causing a memory leak.
How it works…
As you may know, .NET MAUI includes the visual state mechanism. Elements can have different predefined visual statues, which are not limited by Portrait
and Landscape
. For example, there are states such as Normal
, Disabled
, and Focused
.
In C#, you can change the state using the VisualStateManager.GoToState
method:
VisualStateManager.GoToState(someVisualElement, "<stateName>");
You can use any name for your custom state.
In XAML, you can go to a state using a state trigger. The platform provides a built-in OrientationStateTrigger
trigger, so when the orientation is changed, this trigger is invoked, and a corresponding state is activated.
When a state is activated, it executes the setters:
<VisualState x:Name="Portrait">
<VisualState.StateTriggers>
<OrientationStateTrigger Orientation="Portrait" />
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter TargetName="rootGrid" Property="Grid.RowDefinitions"
Value="*,*" />
<Setter TargetName="border2" Property="Grid.Row" Value="1"/>
</VisualState.Setters>
</VisualState>
Each setter can be applied to a specific element on a page using the TargetName
property. Since we define visual states at the ContentPage
level, it’s necessary to explicitly set the name of the parent class in the property setter ("Grid.RowDefinitions"
).
As for the DeviceDisplay
class, it includes basic information about your display, such as orientation, height, width, and density. So, if you need to fine-tune your layout based on these settings, you can use the DeviceDisplay.Current.MainDisplayInfo
property:
double screenWidth = DeviceDisplay.Current.MainDisplayInfo.Width;
double screenHeight = DeviceDisplay.Current.MainDisplayInfo.Height;
double screenDensity = DeviceDisplay.Current.MainDisplayInfo.Density
There’s more…
While the bare .NET MAUI platform doesn’t include XAML extensions (similar to OnIdiom
/OnPlatform
) to set orientation-specific settings, you can use third-party suites, such as DevExpress, which include additional extensions that enable you to perform this task:
<dx:DXButton Content="Click" Padding="{dx:OnOrientation
Portrait='8,4', Landscape='12,8'}"/>
You can learn more about this functionality in DevExpress’s documentation: https://docs.devexpress.com/MAUI/404287/common-concepts/specify-device-specific-settings#xaml-use-the-onorientation-extension.