<file_sep>﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Xamarin.Forms;
using Xamarin.Forms.DualScreen;

namespace Nevarro
{
    //Learn more about making custom code visible in the Xamarin.Forms previewer
    // by visiting https://aka.ms/xamarinforms-previewer
    [DesignTimeVisible(false)]
    public partial class MainPage : ContentPage
    {
        public MainPage()
        {
            InitializeComponent();
            twoPaneView.TallModeConfiguration = Xamarin.Forms.DualScreen.TwoPaneViewTallModeConfiguration.TopBottom;
        }

        void OnNavigateToSample(object sender, EventArgs e)
        {
            Button button = (Button)sender;

            switch (button.Text)
            {
                case "Two Page":
                    Navigation.PushAsync(new TwoPage());
                    break;
                case "Companion Pane":
                    Navigation.PushAsync(new CompanionPane());
                    break;
            }
        }
    }
}
<file_sep>﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Threading.Tasks;
using Nevarro.Services;
using Xamarin.Forms;
using Xamarin.Forms.DualScreen;
using Xamarin.Forms.Xaml;

namespace Nevarro
{
	[XamlCompilation(XamlCompilationOptions.Compile)]
	public partial class TwoPage : ContentPage, INotifyPropertyChanged
	{
		IItemsLayout horizontalLayout = null;
		IItemsLayout verticalItemsLayout = null;
		bool disableUpdates = false;
		private double contentWidth;
		private double contentHeight;
		private ObservableCollection<Uri> imagesList;

		public DualScreenInfo DualScreenLayoutInfo { get; }
		bool IsSpanned => DualScreenLayoutInfo.SpanningBounds.Length > 0;

		public TwoPage()
		{
			InitializeComponent();
			DualScreenLayoutInfo = new DualScreenInfo(layout);

			cv.ItemsSource =
				Enumerable.Range(0, 1000)
					.Select(i => $"Page {i}")
					.ToList();
		}

		protected override async void OnAppearing()
		{
			DualScreenLayoutInfo.PropertyChanged += OnFormsWindowPropertyChanged;
			DualScreenInfo.Current.PropertyChanged += OnFormsWindowPropertyChanged;
			var images = await FetchMediaService.CallImagesEndpoint();
			ImagesList = images;
			cv.ItemsSource = ImagesList;
			SetupCollectionViewLayoutAsync();
		}

		protected override void OnDisappearing()
		{
			base.OnDisappearing();
			DualScreenLayoutInfo.PropertyChanged -= OnFormsWindowPropertyChanged;
			DualScreenInfo.Current.PropertyChanged -= OnFormsWindowPropertyChanged;
		}

		void OnFormsWindowPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
		{
			if (Content == null || disableUpdates)
				return;

			SetupCollectionViewLayoutAsync();
			if (e.PropertyName == nameof(DualScreenInfo.Current.HingeBounds))
			{
				OnPropertyChanged(nameof(HingeWidth));
			}
		}

		public double ContentHeight
		{
			get => contentHeight;
			set
			{
				if (contentHeight == value)
					return;

				contentHeight = value;
				OnPropertyChanged(nameof(ContentHeight));
			}
		}

		public double ContentWidth
		{
			get => contentWidth;
			set
			{
				if (contentWidth == value)
					return;

				contentWidth = value;
				OnPropertyChanged(nameof(ContentWidth));
			}
		}

		public ObservableCollection<Uri> ImagesList
        {
			get => imagesList;
            set
            {
				if (imagesList == value)
					return;

				imagesList = value;
				OnPropertyChanged(nameof(ImagesList));
            }
        }
       
		public double Pane1Height => IsSpanned ? (DualScreenLayoutInfo.SpanningBounds[0].Height) : layout.Height;

		public double Pane2Height => IsSpanned ? (DualScreenLayoutInfo.SpanningBounds[1].Height) : 0d;

		public double HingeWidth => DualScreenLayoutInfo?.HingeBounds.Width ?? DualScreenInfo.Current?.HingeBounds.Width ?? 0d;


		void SetupCollectionViewLayoutAsync()
		{
			ContentWidth = IsSpanned ? (DualScreenLayoutInfo.SpanningBounds[0].Width) : layout.Width;
			ContentHeight = (!DualScreenLayoutInfo.IsLandscape) ? Pane1Height : Pane1Height + Pane2Height;
			disableUpdates = true;

			if (verticalItemsLayout == null)
			{
				horizontalLayout = cv.ItemsLayout;
				verticalItemsLayout = new LinearItemsLayout(ItemsLayoutOrientation.Vertical)
				{
					SnapPointsAlignment = SnapPointsAlignment.Start,
					SnapPointsType = SnapPointsType.None
				};
			}

			if (!DualScreenLayoutInfo.IsLandscape)
			{
				if (cv.ItemsLayout != horizontalLayout)
				{
					cv.ItemsLayout = horizontalLayout;
				}
			}
			else
			{

				if (cv.ItemsLayout != verticalItemsLayout)
				{
					cv.ItemsLayout = verticalItemsLayout;
				}
			}

			disableUpdates = false;
		}
	}
}
<file_sep># Nevarro


Sample Xamarin.Forms App for the Surface Duo (Currently, only supported on Android). 

Nevarro is a mobile app that gives you information on The Mandalorian universe and it's characters. 

<file_sep>﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using Nevarro.Services;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;

namespace Nevarro
{
    [XamlCompilation(XamlCompilationOptions.Compile)]
    public partial class CompanionPane : ContentPage, INotifyPropertyChanged
    {
        List<string> _dataSource;
        private ObservableCollection<Uri> imagesList;
        public CompanionPane()
        {
            InitializeComponent();

            _dataSource =
                Enumerable.Range(1, 1000)
                    .Select(i => $"{i}")
                    .ToList();

            twoPaneView.TallModeConfiguration = Xamarin.Forms.DualScreen.TwoPaneViewTallModeConfiguration.TopBottom;
            cv.ItemsSource = _dataSource;

            indicators.SelectedItem = _dataSource[0];

            cv.PositionChanged += OnCarouselViewPositionChanged;
            indicators.SelectionChanged += OnIndicatorsSelectionChanged;
        }

        protected override async void OnAppearing()
        {
            var images = await FetchMediaService.CallImagesEndpoint();
            ImagesList = images;
            cv.ItemsSource = ImagesList;

        }

        public ObservableCollection<Uri> ImagesList
        {
            get => imagesList;
            set
            {
                if (imagesList == value)
                    return;

                imagesList = value;
                OnPropertyChanged(nameof(ImagesList));
            }
        }

        void OnIndicatorsSelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            if (indicators.SelectedItem == null)
                return;

            if (ImagesList.Contains(indicators.SelectedItem))
            {
                int index = ImagesList.IndexOf((Uri)indicators.SelectedItem);
                cv.Position = _dataSource.IndexOf((index + 1).ToString());
            }
            else
            {
                cv.Position = _dataSource.IndexOf((string)indicators.SelectedItem);
            }
        }

        void OnCarouselViewPositionChanged(object sender, PositionChangedEventArgs e)
        {
            indicators.SelectedItem = _dataSource[e.CurrentPosition];
            indicators.ScrollTo(e.CurrentPosition);
        }
    }
}
<file_sep>﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Threading.Tasks;
using Nevarro.Interfaces;
using Refit;

namespace Nevarro.Services
{
    public static class FetchMediaService
    {
        public static INevarroApi apiService;
        static string baseUrl = "http://10.0.2.2:5000";

        public static async Task<ObservableCollection<Uri>> CallImagesEndpoint()
        {
            apiService = RestService.For<INevarroApi>(baseUrl);
            var images = await apiService.GetImages();
            return images;
        }
   
    }
}
<file_sep>﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Threading.Tasks;
using Refit;

namespace Nevarro.Interfaces
{
    public interface INevarroApi
    {
        [Get("/images")]
        Task<ObservableCollection<Uri>> GetImages();
    }
}
