How to use platform Idiom in Xaml

CaptainXamtasticCaptainXamtastic ✭✭✭GBUniversity ✭✭✭

Although I can do the following in code, I want to do it in Xaml:

            if (Xamarin.Forms.Device.Idiom == TargetIdiom.Phone)
            {
                MainPage = new NavigationPage(new MyPage());
            }
            else if(Xamarin.Forms.Device.Idiom == TargetIdiom.Tablet)
            {
        // etc
            }
            else if(Xamarin.Forms.Device.Idiom == TargetIdiom.Desktop)
            {
        // etc
            }
            else if (Xamarin.Forms.Device.Idiom == TargetIdiom.Unsupported)
            {
                // etc
            }
            else
            {
                // etc
            }

Another post suggests the following, however it throws an error "OnIdiom" not found in xmlns: http://xamarin.com/schemas/2014/forms:

<StackLayout>
    <StackLayout.Orientation>
    <OnIdiom x:TypeArguments="StackOrientation">
        <OnIdiom.Phone>Vertical</OnIdiom.Phone>
        <OnIdiom.Tablet>Horizontal</OnIdiom.Tablet>
    </OnIdiom>
    </StackLayout.Orientation>
    <Label Text="child0"/>
    <Label Text="child1"/>          
</StackLayout>

Any ideas as to how to do this in Xaml?

Posts

  • NMackayNMackay mod GBInsider, University mod
    edited November 2015

    @AnthonyHarrison.3194

    I tested this in Forms 1.5.1 and it works fine. Is this Forms 2.0.0?

    <ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
                 xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
                 xmlns:local="clr-namespace:OrderFinder.Helpers;assembly=CrewFinder"
                 xmlns:mvvm="clr-namespace:OrderFinderassembly=CrewFinder"
                 xmlns:xfc="clr-namespace:Xamarin.Forms;assembly=Xamarin.Forms.Core"
                 Title="Home"
                 BindingContext="{Binding Source={x:Static mvvm:App.Locator}, Path=Start}"
                 x:Class="OrderFinder.View.StartPage">
    
      <StackLayout>
        <StackLayout.Orientation>
          <OnIdiom x:TypeArguments="StackOrientation">
            <OnIdiom.Phone>Vertical</OnIdiom.Phone>
            <OnIdiom.Tablet>Horizontal</OnIdiom.Tablet>
          </OnIdiom>
        </StackLayout.Orientation>
        <Label Text="child0"/>
        <Label Text="child1"/>
      </StackLayout>
    

    It seems to work fine with XamlC enabled.

    namespace OrderFinder.View
    {
        [XamlCompilation(XamlCompilationOptions.Compile)]
        public partial class StartPage
    
  • CaptainXamtasticCaptainXamtastic ✭✭✭ GBUniversity ✭✭✭
    edited November 2015

    Forms 2 - and thanks for testing it!

    However this is the actual Xaml that was throwing (the Pages are ContentPages):

      <TabbedPage.Children>
        <OnIdiom>
          <OnIdiom.Phone>
            <pages:PhoneInbox Title="Inbox" />
            <pages:PhoneChatPage Title ="Chat" />        
          </OnIdiom.Phone>
          <OnIdiom.Tablet>
            <pages:TabletInbox Title="Inbox" />
            <pages:TabletChatPage Title ="Chat" />
          </OnIdiom.Tablet>
        </OnIdiom>
      </TabbedPage.Children>
    

    I'm doing it in code at the moment but would prefer this way, if someone has a solution.

  • CaptainXamtasticCaptainXamtastic ✭✭✭ GBUniversity ✭✭✭

    Yes, that would work well, it's better inclined to creating each sub-view as a custom control instead, I'll try it and report back!

  • NMackayNMackay mod GBInsider, University mod

    I've been playing about with this some more.

    One thing to remember if you have a TabbedPage with say 4 sub pages all with their own ViewModel then it's worth noting that only the parent page gets the popped event when it's popped of the nav stack so you have to manually take care of cleaning up your child pages.

    Sure most people are aware of this but thought it worth mentioning for memory management.

    public void CleanupPage()
            {
                // Cleanup any child pages as they won't get popped
                foreach (var page in Children.Select(page => page as IPageLifetime).Where(cleanup => !(Equals(cleanup,null))))
                {
                    page.CleanupPage();
                    ((Page)page).BindingContext = null;
                }
    
                // Cleanup parent page
                Messenger.Default.Unregister<SetKeybrdVisibilityMsg>(this);
                // Cancel data fetchs etc
                ((SearchViewModel)BindingContext).Cleanup();
                SimpleIoc.Default.Unregister<SearchViewModel>();
            }
    
  • flchauxflchaux ✭✭ FRMember ✭✭

    I've this error too : Type OnIdiom not found in xmlns http://schemas.microsoft.com/winfx/2009/xaml
    Did someone find a solution ? @CaptainXamtastic

  • flchauxflchaux ✭✭ FRMember ✭✭

    To solve my issue I had just to add x:TypeArguments to OnIdiom.
    Now I've an other problem :

    <StackLayout Spacing="10" AbsoluteLayout.LayoutFlags="All" BackgroundColor="#0E517B" Padding="0,30,0,0">
        <StackLayout.AbsoluteLayout.LayoutBounds>
            <OnIdiom x:TypeArguments="Rectangle" Phone="0.5,1,1,0.80" Tablet="1,0,0.5,1" />
        </StackLayout.AbsoluteLayout.LayoutBounds>
    ...
    </StackLayout>
    

    My StackLayout doesn't appear on the screen. I think the layout bounds is not used.

  • ChristophKahlChristophKahl ✭✭ DEMember ✭✭

    @TomSoderling said:
    Thanks so much for this thread!

    I've been trying to figure out to do Idiom-specific styling in XAML, and this was the final bit of info I needed.

    I'm using XAML defined Styles in XF and wanted a way to choose between the Tablet and Phone versions of the Style. I hope this is helpful to someone else as well - I couldn't find any documentation with this specific example. This works in XAML and XAMLC, Forms 2.0

    <StackLayout x:Name="statusGridStackLayout">
        <StackLayout.Style>
            <OnIdiom x:TypeArguments="Style" 
                   Tablet="{StaticResource StatusGridStackLayout}" 
                   Phone="{StaticResource StatusGridStackLayout_Phone}" />
        </StackLayout.Style>
    
    

    Which type do you use to declare your staticResource?

    <ResourceDictionary>
          <Color x:Key="textColor">Blue</Color>
          <String x:Key="StatusGridStackLayout">StatusGridStackLayout.xaml</String>
          <String x:Key="StatusGridStackLayout">StatusGridStackLayout_Phone.xaml</String>
    </ResourceDictionary>
    
  • TomSoderlingTomSoderling ✭✭✭ USUniversity ✭✭✭
    edited August 2017

    The type is Style

    Here's how I defined it for the normal StackLayout (for tablet), and the phone-specific one that it's based off it:

    <Application.Resources>
        <ResourceDictionary >
            <Style x:Key="StatusGridStackLayout" TargetType="StackLayout">
                <Setter Property="Padding" Value="40, 40, 40, 0" />
                <Setter Property="BackgroundColor" Value="{StaticResource SpanielLightestGrey}" />
            </Style>
            <Style x:Key="StatusGridStackLayout_Phone" TargetType="StackLayout" BasedOn="{StaticResource StatusGridStackLayout}">
                <Setter Property="Padding" Value="5, 5, 5, 0" />
            </Style>
        </ResourceDictionary>
    </Application.Resources>
    
  • TomSoderlingTomSoderling ✭✭✭ USUniversity ✭✭✭

    @JoeManke - Love it! Even better!

  • MikeScott8MikeScott8 USUniversity

    @JoeManke @TomSoderling Yes the one property using OnIdiom is nice if you are using it for just one control on one view.

    I would go the setting style using OnIdiom route just for simple fact I'd probably be using the style on multiple controls on multiple views (buttons on multiple views). Also probably would be modifying more than one property between the Idiom's (fontfamily - some fonts might look better at different sizes, fontsize, padding, margin, etc)

    But yes there are multiple ways to do it. Both work, and both have their uses. I guess it all depends on scope of application and views.

  • TomSoderlingTomSoderling ✭✭✭ USUniversity ✭✭✭
    edited November 2016

    @MikeScott8 Just to clarify, since I defined these Styles in the ResourceDictionary of my App.xaml file, they are available to every StackLayout on any View in the entire app.

    For my money, I'd rather create just a single Style, tell every StackLayout "your style is X", and sort out the idiom-specific stuff in that Style definition itself.

  • MikeScott8MikeScott8 USUniversity
    edited November 2016

    Right. sorry I read the thread too quickly and missed the finer points, like it was in the app.xaml, and the OnIdiom was on the style there. I agree that is cleaner. sorry for the confusion on my part.

  • RD8RD8 USMember
    edited August 2017

    Thank you.
    Thought I'd share mine in case it helps someone else (or someone tells me I should be doing it a better way).

    I'm also curious what happens for any idiom's you've not specified, "Desktop"/"Unsupported"?

    <Grid x:Name="gridLayout">
        <Grid.ColumnDefinitions>
          <ColumnDefinition x:Name="columnList">
            <ColumnDefinition.Width>
              <OnIdiom x:TypeArguments="GridLength" Phone="*" Tablet="420" />
            </ColumnDefinition.Width>
          </ColumnDefinition>
          <ColumnDefinition x:Name="columnDetails">
            <ColumnDefinition.Width>
              <OnIdiom x:TypeArguments="GridLength" Phone="0" Tablet="*" />
            </ColumnDefinition.Width>
          </ColumnDefinition>
        </Grid.ColumnDefinitions>
    </Grid>
    
  • abraabra ✭✭✭ ADMember ✭✭✭

    Would be possible to combine the device idiom information with the device orientation information (portrait/landscape) to set the specific styling in XAML ?

  • NMackayNMackay mod GBInsider, University mod

    There is no syntax in XAML to define a style based on the device orientation.

    In cases where I have to cope with this I usually have two styles and change data template etc when the device rotate is detected.

     private void SetLayout(PageOrientation orientation)
            {
    
                // Only do this for handheld devices
                if (_deviceService.Idiom != TargetIdiom.Phone) return;
    
                try
                {
                    ListviewShell.LayoutDefinition = orientation == PageOrientation.Landscape
                       ? (ListViewLayoutBase)Master.Resources["LandscapeListviewLayout"]
                       : (ListViewLayoutBase)Master.Resources["PortraitListviewLayout"];
    
    
  • abraabra ✭✭✭ ADMember ✭✭✭

    Thank you for the answer. Do you eventually have a link to the complete sample, or to a similar one ? (what is for instance ListViewLayoutBase ?)

  • NMackayNMackay mod GBInsider, University mod

    Sorry, that's a telerik class, I just used the code to demonstrate the concept (it's just a data template but their's has a different class name).

    I don't have time to knock up a full sample at the moment, pretty busy on a project that has a lot to do in a short timeframe.

    I posted some sample code here on getting a page to detect the orientation change.

    https://forums.xamarin.com/discussion/88646/detecting-page-orientation-change-for-contentpages

    There's a guide here if you want to know the device orientation when the page loads

    https://developer.xamarin.com/guides/xamarin-forms/application-fundamentals/dependency-service/device-orientation/

  • UnreachableCodeUnreachableCode ✭✭✭ USMember ✭✭✭
            <OnIdiom x:TypeArguments="Page">
                <OnIdiom.Phone><local:PhonePage /></OnIdiom.Phone>
                <OnIdiom.Tablet><local:TabletPage /></OnIdiom.Tablet>
            </OnIdiom>
    

    I'm trying to do this but I get an invalid cast exception. Is it possible to use pages this way?

  • ClayBrinleeClayBrinlee ✭✭ USMember ✭✭
    edited January 18

    Is this nice? Yes it is!

                <Grid>
                    <Grid.IsVisible>
                        <OnIdiom x:TypeArguments="x:Boolean">
                            <OnIdiom.Phone>True</OnIdiom.Phone>
                        </OnIdiom>
                    </Grid.IsVisible>
                    <Label Text="Phone"></Label>
                </Grid>
    
                <Grid>
                    <Grid.IsVisible>
                        <OnIdiom x:TypeArguments="x:Boolean">
                            <OnIdiom.Desktop>True</OnIdiom.Desktop>
                        </OnIdiom>
                    </Grid.IsVisible>
                    <Label Text="Desktop"></Label>
                </Grid>
    
  • CaptainXamtasticCaptainXamtastic ✭✭✭ GBUniversity ✭✭✭
    edited January 18

    @ClayBrinlee said:
    Is this nice? Yes it it!

                <Grid>
                    <Grid.IsVisible>
                        <OnIdiom x:TypeArguments="x:Boolean">
                            <OnIdiom.Phone>True</OnIdiom.Phone>
                        </OnIdiom>
                    </Grid.IsVisible>
                    <Label Text="Phone"></Label>
                </Grid>
    
                <Grid>
                    <Grid.IsVisible>
                        <OnIdiom x:TypeArguments="x:Boolean">
                            <OnIdiom.Desktop>True</OnIdiom.Desktop>
                        </OnIdiom>
                    </Grid.IsVisible>
                    <Label Text="Desktop"></Label>
                </Grid>`
    

    Agreed! Very nice indeed!

    It's 3 years since I did the original post, nowadays (as I'm typically covering everything from iOS and Android phone through MacOS and UWP and WPF) I tend to build my pages dynamically using ListViews and DataTemplateSelectors, that way all the templates are in App.xaml the DataTemplateSelector is where I do the Idiom selection. But I'll be glad when WPF comes out of preview because it's a bit unreliable on the Templating at the moment!)

Sign In or Register to comment.