Handling Views in Landscape & Portrait Mode #28823
Replies: 3 comments 2 replies
-
You are right. This, for me, was the reason to convert all detail Pages from XAML to C# markup. This gives the opportunity to replace all views whenever and wherever you want. Remove the view from one collection and add to another (stacklayout fe). Events stay connected and MVVM works perfect. |
Beta Was this translation helpful? Give feedback.
-
trouble is that "portrait" and "landscape" aren't real concepts for a large portion of supported devices, ie any system where you can run windowed or resizable applications. whenever a page needs to have a different layout when it is wider/narrower is just to make a responsive layout and have some code on the OnSizeChanged event to set the relevant bits when necessary. works well enough. |
Beta Was this translation helpful? Give feedback.
-
You could create a component that helps manage some of the scenarios. For instance, a Two-Panel Grid, where the primary panel was in Column=0, Row=0, but the secondary panel would move dependent on whether your were in landscape or portrait mode:
This could be implemented as a wrapper of Grid with some useful properties to help you switch between the two arrangements, e.g. public class TwoPanelGrid : Grid
{
public bool IsLandscape => Width > Height;
public int SecondGridRow => IsLandscape ? 0 : 1;
public int SecondGridColumn => IsLandscape ? 1 : 0;
public RowDefinitionCollection CurrentRowDefinitions => IsLandscape
? new RowDefinitionCollection
{
new RowDefinition { Height = GridLength.Star },
}
: new RowDefinitionCollection
{
new RowDefinition { Height = GridLength.Star },
new RowDefinition { Height = GridLength.Star },
};
public ColumnDefinitionCollection CurrentColumnDefinitions => IsLandscape
? new ColumnDefinitionCollection
{
new ColumnDefinition { Width = GridLength.Star },
new ColumnDefinition { Width = GridLength.Star },
}
: new ColumnDefinitionCollection
{
new ColumnDefinition { Width = GridLength.Star },
};
public TwoPanelGrid()
{
this.SetBinding(RowDefinitionsProperty, static (TwoPanelGrid t) => t.CurrentRowDefinitions, source: this);
this.SetBinding(ColumnDefinitionsProperty, static (TwoPanelGrid t) => t.CurrentColumnDefinitions, source: this);
this.PropertyChanged += (s, e) =>
{
switch (e.PropertyName)
{
case nameof(Width):
case nameof(Height):
OnPropertyChanged(nameof(IsLandscape));
OnPropertyChanged(nameof(SecondGridColumn));
OnPropertyChanged(nameof(SecondGridRow));
OnPropertyChanged(nameof(CurrentRowDefinitions));
OnPropertyChanged(nameof(CurrentColumnDefinitions));
break;
}
};
}
} Here's a demo of the TwoPanelGrid being applied to the .NET MAUI starter app: <local:TwoPanelGrid x:Name="twoPanelGrid">
<VerticalStackLayout Padding="30,0" Spacing="25">
<Image
Aspect="AspectFit"
HeightRequest="185"
SemanticProperties.Description="dot net bot in a hovercraft number nine"
Source="dotnet_bot.png" />
</VerticalStackLayout>
<VerticalStackLayout
Grid.Row="{Binding SecondGridRow, x:DataType=local:TwoPanelGrid, Source={Reference twoPanelGrid}}"
Grid.Column="{Binding SecondGridColumn, x:DataType=local:TwoPanelGrid, Source={Reference twoPanelGrid}}"
Padding="30,0"
Spacing="25">
<Label SemanticProperties.HeadingLevel="Level1"
Style="{StaticResource Headline}"
Text="Hello, World!" />
<Label
SemanticProperties.Description="Welcome to dot net Multi platform App U I"
SemanticProperties.HeadingLevel="Level2"
Style="{StaticResource SubHeadline}"
Text="Welcome to .NET Multi-platform App UI" />
<Button
x:Name="CounterBtn"
Clicked="OnCounterClicked"
HorizontalOptions="Fill"
SemanticProperties.Hint="Counts the number of times you click"
Text="Click me" />
</VerticalStackLayout>
</local:TwoPanelGrid> |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
-
We need to manage different XAML views for portrait and landscape mode while maintaining a single, centralized behavior in our .NET MAUI application. We have explored two potential solutions (described below), but we couldn't find any official documentation or guidelines on how to handle this scenario effectively.
Solution 1: DataTemplate and ResourceDictionary
This approach requires creating two ResourceDictionary files for each page that needs alternative views depending on the device’s orientation. Each dictionary contains a DataTemplate defining the respective UI layout.
Since DataTemplate does not support direct code-behind event handling, we must avoid defining events in XAML. Instead, we retrieve UI controls at runtime using x:Name and attach event handlers programmatically in the constructor or the OnAppearing() method.
The views are managed via an aggregator, utilizing MergedDictionaries within a ResourceDictionary to collect the created DataTemplate instances. This aggregator is then included in the MergedDictionary of App.xaml alongside other ResourceDictionary instances.
To enforce consistency, we create a common abstract class, BaseResponsivePage, which overrides OnAppearing() and OnDisappearing(). Derived classes must define two string properties, PortraitResource and LandscapeResource, used to retrieve the correct DataTemplate. Additionally, they must implement AttachEvents and DetachEvents methods to handle UI event bindings dynamically.
Pros & Cons:
Pro: Centralized logic with minimal redundancy.
Con: No direct binding between XAML and code-behind since multiple XAML files need to link to a single code-behind, leading to potential reference conflicts.
Solution 2: Inheritance-Based Approach
This approach involves creating a MainPage.cs file containing the shared logic and two separate files, MainPageLandscape.xaml and MainPagePortrait.xaml, defining the UI layouts along with their respective code-behind files.
Each code-behind file inherits from MainPage.cs and primarily contains only a constructor. When initializing MainPage.cs, an instance of the appropriate subclass is created based on the device’s orientation, and its content is set as the main page’s Content.
Pros & Cons:
Pro: Allows events to be defined in XAML, as the inheritance structure resolves event references properly. Moreover, each orientation could have its own logic, as each XAML also has its unique code-behind.
Con: Requires more files, although two of them remain mostly empty, serving only as bridges for inheritance. This structure, however, provides flexibility for adding orientation-specific logic when needed.
Additional Notes
The provided implementations include not only MainPage but also SecondPage to test behavior during navigation.
Currently, using navigation via Shell (which prevents multiple instances of the same page in the stack) causes an unexpected crash. We reference the open issue #22563 on GitHub, but for demonstration purposes, we consider this acceptable within our scope.
Github repo --> https://github.com/Beatrice-Zamagna/MauiPortraitModeSample
PortaitModeSample.zip
PortraitModeSampleInheritance.zip
Beta Was this translation helpful? Give feedback.
All reactions