зеркало из https://github.com/DeGsoft/maui-linux.git
TPV Additional Samples and event notification changes (#9487)
* TPV fixes and added CG demos * - simplify two page sample * - fixes * - uwp screen size fixes * - performance updates * - android alive check * - code cleanup and more samples * - additional gallery updates * - add one shot hinge angle to android head * - add text * - dispose of hinge changes * - unit test compile fix * - split hinge angle into separate file
This commit is contained in:
Родитель
dd3dba3557
Коммит
fda127d525
|
@ -56,6 +56,7 @@ using Android.Support.V4.Content;
|
|||
[assembly: ExportRenderer(typeof(ShellGestures.TouchTestView), typeof(ShellGesturesTouchTestViewRenderer))]
|
||||
[assembly: ExportRenderer(typeof(Issue7249Switch), typeof(Issue7249SwitchRenderer))]
|
||||
[assembly: ExportRenderer(typeof(Issue9360.Issue9360NavigationPage), typeof(Issue9360NavigationPageRenderer))]
|
||||
[assembly: ExportRenderer(typeof(Xamarin.Forms.Controls.GalleryPages.TwoPaneViewGalleries.HingeAngleLabel), typeof(HingeAngleLabelRenderer))]
|
||||
|
||||
#if PRE_APPLICATION_CLASS
|
||||
#elif FORMS_APPLICATION_ACTIVITY
|
||||
|
@ -64,6 +65,56 @@ using Android.Support.V4.Content;
|
|||
#endif
|
||||
namespace Xamarin.Forms.ControlGallery.Android
|
||||
{
|
||||
public class HingeAngleLabelRenderer : Xamarin.Forms.Platform.Android.FastRenderers.LabelRenderer
|
||||
{
|
||||
System.Timers.Timer _hingeTimer;
|
||||
public HingeAngleLabelRenderer(Context context) : base(context)
|
||||
{
|
||||
}
|
||||
|
||||
async void OnTimerElapsed(object sender, System.Timers.ElapsedEventArgs e)
|
||||
{
|
||||
if (_hingeTimer == null)
|
||||
return;
|
||||
|
||||
_hingeTimer.Stop();
|
||||
var hingeAngle = await DualScreen.DualScreenInfo.Current.GetHingeAngleAsync();
|
||||
|
||||
Device.BeginInvokeOnMainThread(() =>
|
||||
{
|
||||
if (_hingeTimer != null)
|
||||
Element.Text = hingeAngle.ToString();
|
||||
});
|
||||
|
||||
if(_hingeTimer != null)
|
||||
_hingeTimer.Start();
|
||||
}
|
||||
|
||||
protected override void OnElementChanged(ElementChangedEventArgs<Label> e)
|
||||
{
|
||||
base.OnElementChanged(e);
|
||||
|
||||
if(_hingeTimer == null)
|
||||
{
|
||||
_hingeTimer = new System.Timers.Timer(100);
|
||||
_hingeTimer.Elapsed += OnTimerElapsed;
|
||||
_hingeTimer.Start();
|
||||
}
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (_hingeTimer != null)
|
||||
{
|
||||
_hingeTimer.Elapsed -= OnTimerElapsed;
|
||||
_hingeTimer.Stop();
|
||||
_hingeTimer = null;
|
||||
}
|
||||
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
}
|
||||
|
||||
public class Issue9360NavigationPageRenderer : Xamarin.Forms.Platform.Android.AppCompat.NavigationPageRenderer
|
||||
{
|
||||
public Issue9360NavigationPageRenderer(Context context) : base(context)
|
||||
|
|
|
@ -276,4 +276,4 @@
|
|||
<Target Name="AfterBuild">
|
||||
</Target>
|
||||
-->
|
||||
</Project>
|
||||
</Project>
|
|
@ -0,0 +1,75 @@
|
|||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<ContentPage
|
||||
xmlns:local="clr-namespace:Xamarin.Forms.Controls.GalleryPages.TwoPaneViewGalleries" xmlns="http://xamarin.com/schemas/2014/forms"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
|
||||
xmlns:d="http://xamarin.com/schemas/2014/forms/design"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:dualscreen="clr-namespace:Xamarin.Forms.DualScreen;assembly=Xamarin.Forms.DualScreen"
|
||||
mc:Ignorable="d"
|
||||
x:Name="mainPage"
|
||||
x:Class="Xamarin.Forms.Controls.GalleryPages.TwoPaneViewGalleries.CompanionPane"
|
||||
BackgroundColor="LightGray"
|
||||
Visual="Material">
|
||||
<dualscreen:TwoPaneView x:Name="twoPaneView" MinWideModeWidth="4000" MinTallModeHeight="4000">
|
||||
<dualscreen:TwoPaneView.Pane1>
|
||||
<CarouselView x:Name="cv" BackgroundColor="LightGray" IsScrollAnimated="False" >
|
||||
<CarouselView.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<Frame BackgroundColor="LightGray" Padding="0" Margin="0"
|
||||
WidthRequest="{Binding Source={x:Reference twoPaneView}, Path=Pane1.Width}"
|
||||
HeightRequest="{Binding Source={x:Reference twoPaneView}, Path=Pane1.Height}">
|
||||
<Frame Margin="20" BackgroundColor="White">
|
||||
<Label FontSize="Large" Text="{Binding ., StringFormat='Slide Content {0}'}" VerticalTextAlignment="Center" HorizontalTextAlignment="Center" HorizontalOptions="Center" VerticalOptions="Center"></Label>
|
||||
</Frame>
|
||||
</Frame>
|
||||
</DataTemplate>
|
||||
</CarouselView.ItemTemplate>
|
||||
</CarouselView>
|
||||
</dualscreen:TwoPaneView.Pane1>
|
||||
<dualscreen:TwoPaneView.Pane2>
|
||||
<CollectionView x:Name="indicators"
|
||||
SelectionMode="Single"
|
||||
Margin="20, 20, 20, 20"
|
||||
BackgroundColor="LightGray"
|
||||
WidthRequest="{Binding Source={x:Reference twoPaneView}, Path=Pane2.Width}"
|
||||
ItemsSource="{Binding Source={x:Reference cv}, Path=ItemsSource}">
|
||||
<CollectionView.Resources>
|
||||
<ResourceDictionary>
|
||||
<Style TargetType="Frame">
|
||||
<Setter Property="VisualStateManager.VisualStateGroups">
|
||||
<VisualStateGroupList>
|
||||
<VisualStateGroup x:Name="CommonStates">
|
||||
<VisualState x:Name="Normal">
|
||||
<VisualState.Setters>
|
||||
<Setter Property="Padding" Value="0"></Setter>
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
<VisualState x:Name="Selected">
|
||||
<VisualState.Setters>
|
||||
<Setter Property="BorderColor" Value="Green" />
|
||||
<Setter Property="Padding" Value="1"></Setter>
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
</VisualStateGroup>
|
||||
</VisualStateGroupList>
|
||||
</Setter>
|
||||
</Style>
|
||||
</ResourceDictionary>
|
||||
</CollectionView.Resources>
|
||||
<CollectionView.ItemsLayout>
|
||||
<LinearItemsLayout Orientation="Vertical" ItemSpacing="10"></LinearItemsLayout>
|
||||
</CollectionView.ItemsLayout>
|
||||
<CollectionView.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<Frame WidthRequest="{Binding Source={x:Reference twoPaneView}, Path=Pane2.Width}" CornerRadius="10" HeightRequest="60" BackgroundColor="White" Margin="0">
|
||||
<StackLayout HorizontalOptions="Fill" VerticalOptions="Fill" Orientation="Horizontal">
|
||||
<Label FontSize="Micro" Padding="20,0,20,0" VerticalTextAlignment="Center" WidthRequest="140" Text="{Binding ., StringFormat='Slide Content {0}'}"></Label>
|
||||
<Label FontSize="Small" Padding="20,0,20,0" VerticalTextAlignment="Center" HorizontalOptions="FillAndExpand" BackgroundColor="DarkGray" Grid.Column="1" Text="{Binding ., StringFormat='Slide {0}'}"></Label>
|
||||
</StackLayout>
|
||||
</Frame>
|
||||
</DataTemplate>
|
||||
</CollectionView.ItemTemplate>
|
||||
</CollectionView>
|
||||
</dualscreen:TwoPaneView.Pane2>
|
||||
</dualscreen:TwoPaneView>
|
||||
</ContentPage>
|
|
@ -0,0 +1,50 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using Xamarin.Forms;
|
||||
using Xamarin.Forms.Xaml;
|
||||
|
||||
namespace Xamarin.Forms.Controls.GalleryPages.TwoPaneViewGalleries
|
||||
{
|
||||
[XamlCompilation(XamlCompilationOptions.Compile)]
|
||||
public partial class CompanionPane : ContentPage
|
||||
{
|
||||
List<string> _dataSource;
|
||||
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;
|
||||
}
|
||||
|
||||
void OnIndicatorsSelectionChanged(object sender, SelectionChangedEventArgs e)
|
||||
{
|
||||
if (indicators.SelectedItem == null)
|
||||
return;
|
||||
|
||||
cv.Position = _dataSource.IndexOf((string)indicators.SelectedItem);
|
||||
}
|
||||
|
||||
void OnCarouselViewPositionChanged(object sender, PositionChangedEventArgs e)
|
||||
{
|
||||
indicators.SelectedItem = _dataSource[e.CurrentPosition];
|
||||
indicators.ScrollTo(e.CurrentPosition);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<ContentPage
|
||||
xmlns:local="clr-namespace:Xamarin.Forms.Controls.GalleryPages.TwoPaneViewGalleries" xmlns="http://xamarin.com/schemas/2014/forms"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
|
||||
xmlns:d="http://xamarin.com/schemas/2014/forms/design"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:dualScreen="clr-namespace:Xamarin.Forms.DualScreen;assembly=Xamarin.Forms.DualScreen"
|
||||
mc:Ignorable="d"
|
||||
x:Class="Xamarin.Forms.Controls.GalleryPages.TwoPaneViewGalleries.DualViewListPage">
|
||||
<dualScreen:TwoPaneView>
|
||||
<dualScreen:TwoPaneView.Pane1>
|
||||
<CollectionView SelectionMode="Single" x:Name="mapList">
|
||||
<CollectionView.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<Grid Padding="10,5,10,5">
|
||||
<Frame Visual="Material" BorderColor="LightGray">
|
||||
<StackLayout Padding="5">
|
||||
<Label FontSize="Title" Text="{Binding Title}"></Label>
|
||||
</StackLayout>
|
||||
</Frame>
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</CollectionView.ItemTemplate>
|
||||
</CollectionView>
|
||||
</dualScreen:TwoPaneView.Pane1>
|
||||
<dualScreen:TwoPaneView.Pane2>
|
||||
<local:DualViewMap x:Name="mapPage"></local:DualViewMap>
|
||||
</dualScreen:TwoPaneView.Pane2>
|
||||
</dualScreen:TwoPaneView>
|
||||
</ContentPage>
|
|
@ -0,0 +1,99 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using Xamarin.Forms;
|
||||
using Xamarin.Forms.DualScreen;
|
||||
using Xamarin.Forms.Xaml;
|
||||
|
||||
namespace Xamarin.Forms.Controls.GalleryPages.TwoPaneViewGalleries
|
||||
{
|
||||
[XamlCompilation(XamlCompilationOptions.Compile)]
|
||||
public partial class DualViewListPage
|
||||
{
|
||||
DualViewMapPage mapPagePushed;
|
||||
bool IsSpanned => DualScreenInfo.Current.SpanMode != TwoPaneViewMode.SinglePane;
|
||||
public DualViewListPage()
|
||||
{
|
||||
InitializeComponent();
|
||||
mapList.SelectionChanged += OnTitleSelected;
|
||||
mapPagePushed = new DualViewMapPage();
|
||||
|
||||
mapList.ItemsSource = new List<MapItem>
|
||||
{
|
||||
new MapItem("New York", 40.7128f, -74.0060f),
|
||||
new MapItem("Seattle", 47.6062f, -122.3425f),
|
||||
new MapItem("Palo Alto", 37.444184f, -122.161059f),
|
||||
new MapItem("San Francisco", 37.7542f, -122.4471f)
|
||||
};
|
||||
}
|
||||
|
||||
async void OnTitleSelected(object sender, SelectionChangedEventArgs e)
|
||||
{
|
||||
if (e.CurrentSelection == null || e.CurrentSelection.Count == 0)
|
||||
return;
|
||||
|
||||
UpdateMapItem();
|
||||
await SetupViews();
|
||||
}
|
||||
|
||||
public MapItem SelectedItem { get; set; }
|
||||
|
||||
|
||||
void UpdateMapItem()
|
||||
{
|
||||
var item = mapList.SelectedItem as MapItem ?? (mapList.ItemsSource as IList<MapItem>)[0];
|
||||
|
||||
SelectedItem = item;
|
||||
|
||||
if (SelectedItem != null)
|
||||
{
|
||||
mapPage.UpdateMap(item);
|
||||
mapPagePushed.UpdateMap(item);
|
||||
}
|
||||
}
|
||||
|
||||
async Task SetupViews()
|
||||
{
|
||||
if (IsSpanned && !DualScreenInfo.Current.IsLandscape)
|
||||
UpdateMapItem();
|
||||
|
||||
if (SelectedItem == null)
|
||||
return;
|
||||
|
||||
if (!IsSpanned || DualScreenInfo.Current.IsLandscape)
|
||||
{
|
||||
if (!Navigation.NavigationStack.Contains(mapPagePushed))
|
||||
{
|
||||
await Navigation.PushAsync(mapPagePushed);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
protected override void OnAppearing()
|
||||
{
|
||||
if (!IsSpanned)
|
||||
mapList.SelectedItem = null;
|
||||
|
||||
|
||||
DualScreenInfo.Current.PropertyChanged += OnFormsWindowPropertyChanged;
|
||||
}
|
||||
|
||||
protected override void OnDisappearing()
|
||||
{
|
||||
base.OnDisappearing();
|
||||
DualScreenInfo.Current.PropertyChanged -= OnFormsWindowPropertyChanged;
|
||||
}
|
||||
|
||||
async void OnFormsWindowPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
|
||||
{
|
||||
if (e.PropertyName == nameof(DualScreenInfo.Current.SpanMode) || e.PropertyName == nameof(DualScreenInfo.Current.IsLandscape))
|
||||
{
|
||||
await SetupViews();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ContentView xmlns="http://xamarin.com/schemas/2014/forms"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
|
||||
xmlns:d="http://xamarin.com/schemas/2014/forms/design"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
mc:Ignorable="d"
|
||||
x:Class="Xamarin.Forms.Controls.GalleryPages.TwoPaneViewGalleries.DualViewMap">
|
||||
<ContentView.Content>
|
||||
<WebView x:Name="webView" HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand" />
|
||||
</ContentView.Content>
|
||||
</ContentView>
|
|
@ -0,0 +1,25 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using Xamarin.Forms;
|
||||
using Xamarin.Forms.Xaml;
|
||||
|
||||
namespace Xamarin.Forms.Controls.GalleryPages.TwoPaneViewGalleries
|
||||
{
|
||||
[XamlCompilation(XamlCompilationOptions.Compile)]
|
||||
public partial class DualViewMap : ContentView
|
||||
{
|
||||
public DualViewMap()
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
webView.Source = $"file:///android_asset/googlemap.html";
|
||||
}
|
||||
|
||||
public void UpdateMap(MapItem item)
|
||||
=> webView.Source = $"file:///android_asset/googlemap.html?lat={item.Lat}&lng={item.Lng}";
|
||||
}
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<ContentPage
|
||||
xmlns:local="clr-namespace:Xamarin.Forms.Controls.GalleryPages.TwoPaneViewGalleries" xmlns="http://xamarin.com/schemas/2014/forms"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
|
||||
xmlns:d="http://xamarin.com/schemas/2014/forms/design"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
mc:Ignorable="d"
|
||||
x:Class="Xamarin.Forms.Controls.GalleryPages.TwoPaneViewGalleries.DualViewMapPage">
|
||||
<local:DualViewMap x:Name="map"></local:DualViewMap>
|
||||
</ContentPage>
|
|
@ -0,0 +1,23 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using Xamarin.Forms;
|
||||
using Xamarin.Forms.Xaml;
|
||||
|
||||
namespace Xamarin.Forms.Controls.GalleryPages.TwoPaneViewGalleries
|
||||
{
|
||||
[XamlCompilation(XamlCompilationOptions.Compile)]
|
||||
public partial class DualViewMapPage : ContentPage
|
||||
{
|
||||
public DualViewMapPage()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
public void UpdateMap(MapItem item)
|
||||
=> map.UpdateMap(item);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Xamarin.Forms.Controls.GalleryPages.TwoPaneViewGalleries
|
||||
{
|
||||
public class MapItem
|
||||
{
|
||||
public MapItem(string title, double lat, double lng)
|
||||
{
|
||||
Title = title;
|
||||
Lat = lat;
|
||||
Lng = lng;
|
||||
}
|
||||
|
||||
public string Title { get; set; }
|
||||
public double Lat { get; set; }
|
||||
public double Lng { get; set; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<ContentPage
|
||||
xmlns:local="clr-namespace:Xamarin.Forms.Controls.GalleryPages.TwoPaneViewGalleries" xmlns="http://xamarin.com/schemas/2014/forms"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
|
||||
xmlns:d="http://xamarin.com/schemas/2014/forms/design"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
mc:Ignorable="d"
|
||||
x:Class="Xamarin.Forms.Controls.GalleryPages.TwoPaneViewGalleries.ExtendCanvas">
|
||||
<ContentPage.Content>
|
||||
<Grid>
|
||||
<WebView x:Name="webView" HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand" />
|
||||
<SearchBar x:Name="searchBar" Placeholder="Find a place..." BackgroundColor="DarkGray" Opacity="0.8" HorizontalOptions="FillAndExpand" VerticalOptions="Start" />
|
||||
</Grid>
|
||||
</ContentPage.Content>
|
||||
</ContentPage>
|
|
@ -0,0 +1,38 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using Xamarin.Forms;
|
||||
using Xamarin.Forms.Xaml;
|
||||
|
||||
namespace Xamarin.Forms.Controls.GalleryPages.TwoPaneViewGalleries
|
||||
{
|
||||
[XamlCompilation(XamlCompilationOptions.Compile)]
|
||||
public partial class ExtendCanvas
|
||||
{
|
||||
public ExtendCanvas()
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
searchBar.SearchButtonPressed += SearchBar_SearchButtonPressed;
|
||||
|
||||
webView.Source = "file:///android_asset/googlemapsearch.html";
|
||||
|
||||
StartSearch();
|
||||
}
|
||||
|
||||
private void SearchBar_SearchButtonPressed(object sender, EventArgs e)
|
||||
{
|
||||
StartSearch();
|
||||
}
|
||||
|
||||
void StartSearch()
|
||||
{
|
||||
var place = searchBar?.Text ?? string.Empty;
|
||||
|
||||
webView.Source = "file:///android_asset/googlemapsearch.html?place=" + System.Web.HttpUtility.UrlEncode(place);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -23,16 +23,17 @@ namespace Xamarin.Forms.Controls.GalleryPages.TwoPaneViewGalleries
|
|||
protected override void OnAppearing()
|
||||
{
|
||||
base.OnAppearing();
|
||||
DualScreenInfo.PropertyChanged += DualScreenInfo_PropertyChanged;
|
||||
DualScreenInfo.PropertyChanged += OnInfoPropertyChanged;
|
||||
UpdateColumns();
|
||||
}
|
||||
|
||||
protected override void OnDisappearing()
|
||||
{
|
||||
base.OnDisappearing();
|
||||
DualScreenInfo.PropertyChanged -= DualScreenInfo_PropertyChanged;
|
||||
DualScreenInfo.PropertyChanged -= OnInfoPropertyChanged;
|
||||
}
|
||||
|
||||
void DualScreenInfo_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
|
||||
void UpdateColumns()
|
||||
{
|
||||
if (DualScreenInfo.SpanningBounds.Length > 0)
|
||||
{
|
||||
|
@ -50,6 +51,12 @@ namespace Xamarin.Forms.Controls.GalleryPages.TwoPaneViewGalleries
|
|||
OnPropertyChanged(nameof(Column1Width));
|
||||
OnPropertyChanged(nameof(Column2Width));
|
||||
OnPropertyChanged(nameof(Column3Width));
|
||||
|
||||
}
|
||||
|
||||
void OnInfoPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
|
||||
{
|
||||
UpdateColumns();
|
||||
}
|
||||
|
||||
public double Column1Width { get; set; }
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Grid xmlns="http://xamarin.com/schemas/2014/forms"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
|
||||
xmlns:d="http://xamarin.com/schemas/2014/forms/design"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
mc:Ignorable="d"
|
||||
x:Class="Xamarin.Forms.Controls.GalleryPages.TwoPaneViewGalleries.Details">
|
||||
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto"></ColumnDefinition>
|
||||
<ColumnDefinition Width="*"></ColumnDefinition>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"></RowDefinition>
|
||||
<RowDefinition Height="*"></RowDefinition>
|
||||
</Grid.RowDefinitions>
|
||||
<Image Margin="16, 16, 0, 0" WidthRequest="100" HeightRequest="100" Source="ic_movie_black_24dp"></Image>
|
||||
|
||||
<Grid Grid.Column="1" VerticalOptions="Center">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"></RowDefinition>
|
||||
<RowDefinition Height="Auto"></RowDefinition>
|
||||
<RowDefinition Height="*"></RowDefinition>
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto"></ColumnDefinition>
|
||||
<ColumnDefinition Width="*"></ColumnDefinition>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Label FontSize="Title" FontAttributes="Bold" Text="Title:"></Label>
|
||||
<Label FontSize="Title" Grid.Row="1" FontAttributes="Bold" Text="Details:"></Label>
|
||||
|
||||
<Label Grid.Column="1" Text="{Binding Title}" VerticalTextAlignment="Center"></Label>
|
||||
<Label Grid.Column="1" Grid.Row="1" Text="{Binding Details}" VerticalTextAlignment="Center"></Label>
|
||||
</Grid>
|
||||
</Grid>
|
|
@ -0,0 +1,20 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using Xamarin.Forms;
|
||||
using Xamarin.Forms.Xaml;
|
||||
|
||||
namespace Xamarin.Forms.Controls.GalleryPages.TwoPaneViewGalleries
|
||||
{
|
||||
[XamlCompilation(XamlCompilationOptions.Compile)]
|
||||
public partial class Details : Grid
|
||||
{
|
||||
public Details()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
|
||||
xmlns:d="http://xamarin.com/schemas/2014/forms/design"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:local="clr-namespace:Xamarin.Forms.Controls.GalleryPages.TwoPaneViewGalleries"
|
||||
mc:Ignorable="d"
|
||||
x:Class="Xamarin.Forms.Controls.GalleryPages.TwoPaneViewGalleries.DetailsPage"
|
||||
Title="Master Details">
|
||||
<local:Details></local:Details>
|
||||
</ContentPage>
|
|
@ -0,0 +1,43 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using Xamarin.Forms;
|
||||
using Xamarin.Forms.DualScreen;
|
||||
using Xamarin.Forms.Xaml;
|
||||
|
||||
namespace Xamarin.Forms.Controls.GalleryPages.TwoPaneViewGalleries
|
||||
{
|
||||
[XamlCompilation(XamlCompilationOptions.Compile)]
|
||||
public partial class DetailsPage
|
||||
{
|
||||
bool IsSpanned => DualScreenInfo.Current.SpanMode != TwoPaneViewMode.SinglePane;
|
||||
public DetailsPage()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
protected override void OnAppearing()
|
||||
{
|
||||
base.OnAppearing();
|
||||
DualScreenInfo.Current.PropertyChanged += OnFormsWindowPropertyChanged;
|
||||
}
|
||||
|
||||
protected override void OnDisappearing()
|
||||
{
|
||||
base.OnDisappearing();
|
||||
DualScreenInfo.Current.PropertyChanged -= OnFormsWindowPropertyChanged;
|
||||
}
|
||||
|
||||
async void OnFormsWindowPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
|
||||
{
|
||||
if(e.PropertyName == nameof(DualScreenInfo.Current.SpanMode) || e.PropertyName == nameof(DualScreenInfo.Current.IsLandscape))
|
||||
{
|
||||
if (IsSpanned)
|
||||
await Navigation.PopAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<CollectionView xmlns="http://xamarin.com/schemas/2014/forms"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
|
||||
xmlns:d="http://xamarin.com/schemas/2014/forms/design"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
mc:Ignorable="d"
|
||||
x:Class="Xamarin.Forms.Controls.GalleryPages.TwoPaneViewGalleries.Master"
|
||||
SelectionMode="Single">
|
||||
|
||||
<CollectionView.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<Grid Padding="10,5,10,5">
|
||||
<Frame Visual="Material" BorderColor="LightGray">
|
||||
<StackLayout Padding="5">
|
||||
<Label FontSize="Title" Text="{Binding Title}"></Label>
|
||||
</StackLayout>
|
||||
</Frame>
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</CollectionView.ItemTemplate>
|
||||
</CollectionView>
|
|
@ -0,0 +1,23 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using Xamarin.Forms;
|
||||
using Xamarin.Forms.Xaml;
|
||||
|
||||
namespace Xamarin.Forms.Controls.GalleryPages.TwoPaneViewGalleries
|
||||
{
|
||||
[XamlCompilation(XamlCompilationOptions.Compile)]
|
||||
public partial class Master : CollectionView
|
||||
{
|
||||
public Master()
|
||||
{
|
||||
InitializeComponent();
|
||||
ItemsSource = Enumerable.Range(1, 100)
|
||||
.Select(x => new MasterDetailsItem(x))
|
||||
.ToList();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<ContentPage
|
||||
xmlns="http://xamarin.com/schemas/2014/forms"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
|
||||
xmlns:d="http://xamarin.com/schemas/2014/forms/design"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:dualScreen="clr-namespace:Xamarin.Forms.DualScreen;assembly=Xamarin.Forms.DualScreen"
|
||||
xmlns:local="clr-namespace:Xamarin.Forms.Controls.GalleryPages.TwoPaneViewGalleries"
|
||||
mc:Ignorable="d"
|
||||
x:Class="Xamarin.Forms.Controls.GalleryPages.TwoPaneViewGalleries.MasterDetail">
|
||||
<dualScreen:TwoPaneView MinWideModeWidth="4000" MinTallModeHeight="4000">
|
||||
<dualScreen:TwoPaneView.Pane1>
|
||||
<local:Master x:Name="masterPage"></local:Master>
|
||||
</dualScreen:TwoPaneView.Pane1>
|
||||
<dualScreen:TwoPaneView.Pane2>
|
||||
<local:Details x:Name="detailsPage"></local:Details>
|
||||
</dualScreen:TwoPaneView.Pane2>
|
||||
</dualScreen:TwoPaneView>
|
||||
</ContentPage>
|
|
@ -0,0 +1,82 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using Xamarin.Forms;
|
||||
using Xamarin.Forms.DualScreen;
|
||||
using Xamarin.Forms.Xaml;
|
||||
|
||||
namespace Xamarin.Forms.Controls.GalleryPages.TwoPaneViewGalleries
|
||||
{
|
||||
[XamlCompilation(XamlCompilationOptions.Compile)]
|
||||
public partial class MasterDetail
|
||||
{
|
||||
bool IsSpanned => DualScreenInfo.Current.SpanMode != TwoPaneViewMode.SinglePane;
|
||||
DetailsPage detailsPagePushed;
|
||||
|
||||
public MasterDetail()
|
||||
{
|
||||
InitializeComponent();
|
||||
masterPage.SelectionChanged += OnTitleSelected;
|
||||
detailsPagePushed = new DetailsPage();
|
||||
}
|
||||
|
||||
private void OnTitleSelected(object sender, SelectionChangedEventArgs e)
|
||||
{
|
||||
if (e.CurrentSelection == null || e.CurrentSelection.Count == 0)
|
||||
return;
|
||||
|
||||
SetBindingContext();
|
||||
SetupViews();
|
||||
}
|
||||
|
||||
|
||||
void SetBindingContext()
|
||||
{
|
||||
var bindingContext = masterPage.SelectedItem ?? (masterPage.ItemsSource as IList<MasterDetailsItem>)[0];
|
||||
detailsPagePushed.BindingContext = bindingContext;
|
||||
detailsPage.BindingContext = bindingContext;
|
||||
}
|
||||
|
||||
async void SetupViews()
|
||||
{
|
||||
if (IsSpanned && !DualScreenInfo.Current.IsLandscape)
|
||||
SetBindingContext();
|
||||
|
||||
if (detailsPage.BindingContext == null)
|
||||
return;
|
||||
|
||||
if (!IsSpanned)
|
||||
{
|
||||
if (!Navigation.NavigationStack.Contains(detailsPagePushed))
|
||||
{
|
||||
await Navigation.PushAsync(detailsPagePushed);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
protected override void OnAppearing()
|
||||
{
|
||||
if (!IsSpanned)
|
||||
masterPage.SelectedItem = null;
|
||||
DualScreenInfo.Current.PropertyChanged += OnFormsWindowPropertyChanged;
|
||||
}
|
||||
|
||||
protected override void OnDisappearing()
|
||||
{
|
||||
base.OnDisappearing();
|
||||
DualScreenInfo.Current.PropertyChanged -= OnFormsWindowPropertyChanged;
|
||||
}
|
||||
|
||||
void OnFormsWindowPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
|
||||
{
|
||||
if (e.PropertyName == nameof(DualScreenInfo.Current.SpanMode) || e.PropertyName == nameof(DualScreenInfo.Current.IsLandscape))
|
||||
{
|
||||
SetupViews();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Xamarin.Forms.Controls.GalleryPages.TwoPaneViewGalleries
|
||||
{
|
||||
public class MasterDetailsItem
|
||||
{
|
||||
|
||||
public MasterDetailsItem(int x)
|
||||
{
|
||||
Title = $"Item {x}";
|
||||
Details = $"This is item {x}";
|
||||
}
|
||||
|
||||
public string Title { get; set; }
|
||||
public string Details { get; set; }
|
||||
}
|
||||
}
|
|
@ -6,11 +6,11 @@
|
|||
mc:Ignorable="d"
|
||||
x:Class="Xamarin.Forms.Controls.GalleryPages.TwoPaneViewGalleries.OpenCompactWindow">
|
||||
<ContentPage.Content>
|
||||
<StackLayout>
|
||||
<Button Text="Open Compact Window"
|
||||
<StackLayout x:Name="layout">
|
||||
<Button x:Name="button" Text="Open Compact Window"
|
||||
Clicked="Button_Clicked"
|
||||
VerticalOptions="CenterAndExpand"
|
||||
HorizontalOptions="CenterAndExpand" />
|
||||
VerticalOptions="Start"
|
||||
HorizontalOptions="Start" />
|
||||
</StackLayout>
|
||||
</ContentPage.Content>
|
||||
</ContentPage>
|
|
@ -12,9 +12,25 @@ namespace Xamarin.Forms.Controls.GalleryPages.TwoPaneViewGalleries
|
|||
[XamlCompilation(XamlCompilationOptions.Compile)]
|
||||
public partial class OpenCompactWindow : ContentPage
|
||||
{
|
||||
DualScreen.DualScreenInfo info;
|
||||
public OpenCompactWindow()
|
||||
{
|
||||
InitializeComponent();
|
||||
info = new DualScreen.DualScreenInfo(layout);
|
||||
info.PropertyChanged += Info_PropertyChanged;
|
||||
}
|
||||
|
||||
private void Info_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
|
||||
{
|
||||
if (info.SpanningBounds.Length > 0)
|
||||
{
|
||||
var bounds = info.SpanningBounds[0];
|
||||
layout.Padding = new Thickness(bounds.Width / 2 - button.Width / 2, bounds.Height / 2 - button.Height / 2, 0, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
layout.Padding = new Thickness(0);
|
||||
}
|
||||
}
|
||||
|
||||
async void Button_Clicked(object sender, EventArgs e)
|
||||
|
@ -29,7 +45,7 @@ namespace Xamarin.Forms.Controls.GalleryPages.TwoPaneViewGalleries
|
|||
{
|
||||
Children =
|
||||
{
|
||||
new Label(){ Text = "rabbit"},new Label(){ Text = "rabbit"},new Label(){ Text = "rabbit"},new Label(){ Text = "rabbit"},new Label(){ Text = "rabbit"},new Label(){ Text = "rabbit"},new Label(){ Text = "rabbit"},new Label(){ Text = "rabbit"},new Label(){ Text = "rabbit"},new Label(){ Text = "rabbit"},new Label(){ Text = "rabbit"},new Label(){ Text = "rabbit"},new Label(){ Text = "rabbit"},new Label(){ Text = "rabbit"},new Label(){ Text = "rabbit"},
|
||||
new Label(){ Text = "Welcome to your Compact Mode Window" },
|
||||
button
|
||||
},
|
||||
BackgroundColor = Color.Yellow,
|
||||
|
@ -39,13 +55,13 @@ namespace Xamarin.Forms.Controls.GalleryPages.TwoPaneViewGalleries
|
|||
|
||||
|
||||
layout.BatchCommitted += Layout_BatchCommitted;
|
||||
page.Content = layout;
|
||||
page.Content = new ScrollView() { Content = layout };
|
||||
|
||||
var args = await DualScreen.DualScreenHelper.OpenCompactMode(page);
|
||||
|
||||
button.Command = new Command(async () =>
|
||||
{
|
||||
await args.Close();
|
||||
await args.CloseAsync();
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
|
||||
xmlns:d="http://xamarin.com/schemas/2014/forms/design"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
mc:Ignorable="d"
|
||||
x:Class="Xamarin.Forms.Controls.GalleryPages.TwoPaneViewGalleries.TwoPage"
|
||||
xmlns:local="clr-namespace:Xamarin.Forms.Controls.GalleryPages.TwoPaneViewGalleries"
|
||||
Visual="Material"
|
||||
x:Name="mainPage">
|
||||
|
||||
<Grid x:Name="layout">
|
||||
<CollectionView x:Name="cv" BackgroundColor="LightGray">
|
||||
<CollectionView.ItemsLayout>
|
||||
<LinearItemsLayout
|
||||
SnapPointsAlignment="Start"
|
||||
SnapPointsType="MandatorySingle"
|
||||
Orientation="Horizontal"
|
||||
ItemSpacing="{Binding Source={x:Reference mainPage}, Path=HingeWidth}" />
|
||||
</CollectionView.ItemsLayout>
|
||||
<CollectionView.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<Frame BackgroundColor="LightGray" Padding="0" Margin="0"
|
||||
WidthRequest="{Binding Source={x:Reference mainPage}, Path=ContentWidth}"
|
||||
HeightRequest="{Binding Source={x:Reference mainPage}, Path=ContentHeight}">
|
||||
<Frame Margin="20" BackgroundColor="White">
|
||||
<Label FontSize="Large" Text="{Binding .}" VerticalTextAlignment="Center" HorizontalTextAlignment="Center" HorizontalOptions="Center" VerticalOptions="Center"></Label>
|
||||
</Frame>
|
||||
</Frame>
|
||||
</DataTemplate>
|
||||
</CollectionView.ItemTemplate>
|
||||
</CollectionView>
|
||||
</Grid>
|
||||
</ContentPage>
|
|
@ -0,0 +1,130 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using Xamarin.Forms;
|
||||
using Xamarin.Forms.DualScreen;
|
||||
using Xamarin.Forms.Xaml;
|
||||
|
||||
namespace Xamarin.Forms.Controls.GalleryPages.TwoPaneViewGalleries
|
||||
{
|
||||
[XamlCompilation(XamlCompilationOptions.Compile)]
|
||||
public partial class TwoPage : ContentPage
|
||||
{
|
||||
IItemsLayout horizontalLayout = null;
|
||||
IItemsLayout verticalItemsLayout = null;
|
||||
bool disableUpdates = false;
|
||||
private double contentWidth;
|
||||
private double contentHeight;
|
||||
|
||||
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 void OnAppearing()
|
||||
{
|
||||
DualScreenLayoutInfo.PropertyChanged += OnFormsWindowPropertyChanged;
|
||||
DualScreenInfo.Current.PropertyChanged += OnFormsWindowPropertyChanged;
|
||||
SetupColletionViewLayout();
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
SetupColletionViewLayout();
|
||||
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 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 SetupColletionViewLayout()
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
|
||||
xmlns:d="http://xamarin.com/schemas/2014/forms/design"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:local="clr-namespace:Xamarin.Forms.DualScreen;assembly=Xamarin.Forms.DualScreen"
|
||||
xmlns:twoPaneGallery="clr-namespace:Xamarin.Forms.Controls.GalleryPages.TwoPaneViewGalleries"
|
||||
mc:Ignorable="d"
|
||||
x:Class="Xamarin.Forms.Controls.GalleryPages.TwoPaneViewGalleries.TwoPanePropertiesGallery">
|
||||
<ContentPage.Content>
|
||||
<local:TwoPaneView x:Name="twoPaneView"
|
||||
MinTallModeHeight="{Binding Source={x:Reference MinTallModeHeight}, Path=Value}"
|
||||
MinWideModeWidth="{Binding Source={x:Reference MinWideModeWidth}, Path=Value}"
|
||||
PanePriority="{Binding Source={x:Reference PanePriority}, Path=SelectedItem, Mode=TwoWay}"
|
||||
TallModeConfiguration="{Binding Source={x:Reference TallModeConfiguration}, Path=SelectedItem}"
|
||||
WideModeConfiguration="{Binding Source={x:Reference WideModeConfiguration}, Path=SelectedItem}">
|
||||
<local:TwoPaneView.Pane1>
|
||||
<StackLayout Padding="20">
|
||||
<Label Text="MinTallModeHeight"></Label>
|
||||
<Slider x:Name="MinTallModeHeight" ></Slider>
|
||||
<Label Text="MinWideModeWidth"></Label>
|
||||
<Slider x:Name="MinWideModeWidth"></Slider>
|
||||
<Label Text="Pane1Length"></Label>
|
||||
<Slider x:Name="Pane1Length" Maximum="1" Value="1"></Slider>
|
||||
<Label Text="Pane2Length"></Label>
|
||||
<Slider x:Name="Pane2Length" Maximum="1" Value="1"></Slider>
|
||||
<Picker Title="PanePriority" x:Name="PanePriority" SelectedIndex="0">
|
||||
</Picker>
|
||||
<Picker Title="TallModeConfiguration" x:Name="TallModeConfiguration" SelectedIndex="1">
|
||||
</Picker>
|
||||
<Picker Title="WideModeConfiguration" x:Name="WideModeConfiguration" SelectedIndex="1">
|
||||
</Picker>
|
||||
<Label Text="Hinge Angle (only works on Android):"></Label>
|
||||
<twoPaneGallery:HingeAngleLabel />
|
||||
</StackLayout>
|
||||
</local:TwoPaneView.Pane1>
|
||||
<local:TwoPaneView.Pane2>
|
||||
<StackLayout BackgroundColor="Yellow">
|
||||
<Label Text="Secondary Content"></Label>
|
||||
<Button Text="Reset" Clicked="OnReset"></Button>
|
||||
</StackLayout>
|
||||
</local:TwoPaneView.Pane2>
|
||||
</local:TwoPaneView>
|
||||
</ContentPage.Content>
|
||||
</ContentPage>
|
|
@ -0,0 +1,69 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using Xamarin.Forms;
|
||||
using Xamarin.Forms.Xaml;
|
||||
|
||||
namespace Xamarin.Forms.Controls.GalleryPages.TwoPaneViewGalleries
|
||||
{
|
||||
[XamlCompilation(XamlCompilationOptions.Compile)]
|
||||
public partial class TwoPanePropertiesGallery : ContentPage
|
||||
{
|
||||
public TwoPanePropertiesGallery()
|
||||
{
|
||||
InitializeComponent();
|
||||
Pane1Length.ValueChanged += PaneLengthChanged;
|
||||
Pane2Length.ValueChanged += PaneLengthChanged;
|
||||
PanePriority.ItemsSource = Enum.GetValues(typeof(DualScreen.TwoPaneViewPriority));
|
||||
TallModeConfiguration.ItemsSource = Enum.GetValues(typeof(DualScreen.TwoPaneViewTallModeConfiguration));
|
||||
WideModeConfiguration.ItemsSource = Enum.GetValues(typeof(DualScreen.TwoPaneViewWideModeConfiguration));
|
||||
twoPaneView.PanePriority = DualScreen.TwoPaneViewPriority.Pane1;
|
||||
Pane1Length.Value = 0.5;
|
||||
Pane2Length.Value = 0.5;
|
||||
}
|
||||
|
||||
void PaneLengthChanged(object sender, ValueChangedEventArgs e)
|
||||
{
|
||||
twoPaneView.Pane1Length = new GridLength(Pane1Length.Value, GridUnitType.Star);
|
||||
twoPaneView.Pane2Length = new GridLength(Pane2Length.Value, GridUnitType.Star);
|
||||
}
|
||||
|
||||
protected override void OnAppearing()
|
||||
{
|
||||
base.OnAppearing();
|
||||
Setup(Width, Height);
|
||||
|
||||
PanePriority.SelectedIndex = 0;
|
||||
TallModeConfiguration.SelectedIndex = 1;
|
||||
WideModeConfiguration.SelectedIndex = 1;
|
||||
}
|
||||
|
||||
void Setup(double width, double height)
|
||||
{
|
||||
if (width <= 0 || height <= 0)
|
||||
return;
|
||||
|
||||
|
||||
MinTallModeHeight.Maximum = height;
|
||||
MinWideModeWidth.Maximum = width;
|
||||
}
|
||||
|
||||
protected override void OnSizeAllocated(double width, double height)
|
||||
{
|
||||
base.OnSizeAllocated(width, height);
|
||||
Setup(width, height);
|
||||
}
|
||||
|
||||
void OnReset(object sender, EventArgs e)
|
||||
{
|
||||
twoPaneView.PanePriority = DualScreen.TwoPaneViewPriority.Pane1;
|
||||
Pane1Length.Value = 0.5;
|
||||
Pane2Length.Value = 0.5;
|
||||
}
|
||||
}
|
||||
|
||||
public class HingeAngleLabel : Label { }
|
||||
}
|
|
@ -14,15 +14,40 @@ namespace Xamarin.Forms.Controls.GalleryPages.TwoPaneViewGalleries
|
|||
mdp.IsPresented = false;
|
||||
}
|
||||
|
||||
Content = new StackLayout
|
||||
var twoPaneView = new DualScreen.TwoPaneView()
|
||||
{
|
||||
MinTallModeHeight = 0,
|
||||
MinWideModeWidth = 0
|
||||
};
|
||||
|
||||
var pane1 = new StackLayout
|
||||
{
|
||||
Children =
|
||||
{
|
||||
|
||||
GalleryBuilder.NavButton("TwoPanePropertiesGallery", () => new TwoPanePropertiesGallery(), Navigation),
|
||||
GalleryBuilder.NavButton("Master Details Sample", () => new MasterDetail(), Navigation),
|
||||
GalleryBuilder.NavButton("Companion Pane", () => new CompanionPane(), Navigation),
|
||||
GalleryBuilder.NavButton("ExtendCanvas Sample", () => new ExtendCanvas(), Navigation),
|
||||
GalleryBuilder.NavButton("DualViewMapPage Sample", () => new DualViewMapPage(), Navigation),
|
||||
}
|
||||
};
|
||||
|
||||
var pane2 = new StackLayout
|
||||
{
|
||||
Children =
|
||||
{
|
||||
GalleryBuilder.NavButton("Nested TwoPaneView Split Across Hinge", () => new NestedTwoPaneViewSplitAcrossHinge(), Navigation),
|
||||
GalleryBuilder.NavButton("Open Picture in Picture Window", () => new OpenCompactWindow(), Navigation),
|
||||
GalleryBuilder.NavButton("DualScreenInfo with non TwoPaneView", () => new GridUsingDualScreenInfo(), Navigation),
|
||||
GalleryBuilder.NavButton("eReader Samples", () => new TwoPage(), Navigation),
|
||||
}
|
||||
};
|
||||
|
||||
twoPaneView.Pane1 = pane1;
|
||||
twoPaneView.Pane2 = pane2;
|
||||
|
||||
Content = twoPaneView;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -72,6 +72,12 @@
|
|||
<EmbeddedResource Update="GalleryPages\TwoPaneViewGalleries\OpenCompactWindow.xaml">
|
||||
<Generator>MSBuild:UpdateDesignTimeXaml</Generator>
|
||||
</EmbeddedResource>
|
||||
<EmbeddedResource Update="GalleryPages\TwoPaneViewGalleries\TwoPage.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</EmbeddedResource>
|
||||
<EmbeddedResource Update="GalleryPages\TwoPaneViewGalleries\TwoPanePropertiesGallery.xaml">
|
||||
<Generator>MSBuild:UpdateDesignTimeXaml</Generator>
|
||||
</EmbeddedResource>
|
||||
</ItemGroup>
|
||||
|
||||
<Target Name="CreateControllGalleryConfig" BeforeTargets="Build">
|
||||
|
|
|
@ -12,9 +12,10 @@ namespace Xamarin.Forms.DualScreen
|
|||
bool IsSpanned { get; }
|
||||
bool IsLandscape { get; }
|
||||
Rectangle GetHinge();
|
||||
Size ScaledScreenSize { get; }
|
||||
Point? GetLocationOnScreen(VisualElement visualElement);
|
||||
DeviceInfo DeviceInfo { get; }
|
||||
void WatchForChangesOnLayout(VisualElement visualElement);
|
||||
void StopWatchingForChangesOnLayout(VisualElement visualElement);
|
||||
object WatchForChangesOnLayout(VisualElement visualElement, Action action);
|
||||
void StopWatchingForChangesOnLayout(VisualElement visualElement, object handle);
|
||||
Task<int> GetHingeAngleAsync();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Xamarin.Forms.Internals;
|
||||
|
||||
namespace Xamarin.Forms.DualScreen.UnitTests
|
||||
|
@ -19,6 +20,8 @@ namespace Xamarin.Forms.DualScreen.UnitTests
|
|||
|
||||
public DeviceInfo DeviceInfo { get; set; }
|
||||
|
||||
public Size ScaledScreenSize => DeviceInfo.ScaledScreenSize;
|
||||
|
||||
public event EventHandler OnScreenChanged;
|
||||
|
||||
public void Dispose()
|
||||
|
@ -41,20 +44,20 @@ namespace Xamarin.Forms.DualScreen.UnitTests
|
|||
|
||||
public Point? SetLocationOnScreen(Point point) => _location = point;
|
||||
|
||||
public void WatchForChangesOnLayout(VisualElement visualElement)
|
||||
public object WatchForChangesOnLayout(VisualElement visualElement, Action action)
|
||||
{
|
||||
visualElement.BatchCommitted += OnLayoutChangesCommited;
|
||||
EventHandler<EventArg<VisualElement>> handler = (_, __) => action();
|
||||
visualElement.BatchCommitted += handler;
|
||||
return handler;
|
||||
}
|
||||
|
||||
public void StopWatchingForChangesOnLayout(VisualElement visualElement)
|
||||
public void StopWatchingForChangesOnLayout(VisualElement visualElement, object handle)
|
||||
{
|
||||
visualElement.BatchCommitted -= OnLayoutChangesCommited;
|
||||
if(handle is EventHandler<EventArg<VisualElement>> eh)
|
||||
visualElement.BatchCommitted -= eh;
|
||||
}
|
||||
|
||||
void OnLayoutChangesCommited(object sender, EventArg<VisualElement> e)
|
||||
{
|
||||
OnScreenChanged?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
public Task<int> GetHingeAngleAsync() => Task.FromResult(0);
|
||||
}
|
||||
|
||||
internal class TestDualScreenServiceLandscape : TestDualScreenService
|
||||
|
|
|
@ -176,5 +176,57 @@ namespace Xamarin.Forms.DualScreen.UnitTests
|
|||
Assert.AreEqual(0, twoPaneViewNested.Pane1.X);
|
||||
Assert.AreEqual(0, twoPaneViewNested.Pane2.X);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void SpanningBoundsLandscape()
|
||||
{
|
||||
var testDualScreenService = new TestDualScreenServiceLandscape();
|
||||
testDualScreenService.IsSpanned = true;
|
||||
testDualScreenService.SetLocationOnScreen(new Point(0, 400));
|
||||
var result = new StackLayout() { IsPlatformEnabled = true };
|
||||
|
||||
result.Layout(new Rectangle(0, 400, testDualScreenService.ScaledScreenSize.Width, 200));
|
||||
DualScreenInfo info = new DualScreenInfo(result, testDualScreenService);
|
||||
|
||||
|
||||
var top = info.SpanningBounds[0];
|
||||
|
||||
Assert.AreEqual(0, top.X);
|
||||
Assert.AreEqual(0, top.Y);
|
||||
Assert.AreEqual(testDualScreenService.ScaledScreenSize.Width, top.Width);
|
||||
Assert.AreEqual(90, top.Height);
|
||||
|
||||
var bottom = info.SpanningBounds[1];
|
||||
Assert.AreEqual(0, bottom.X);
|
||||
Assert.AreEqual(110, bottom.Y);
|
||||
Assert.AreEqual(testDualScreenService.ScaledScreenSize.Width, bottom.Width);
|
||||
Assert.AreEqual(90, bottom.Height);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void SpanningBoundsPortrait()
|
||||
{
|
||||
var testDualScreenService = new TestDualScreenServicePortrait();
|
||||
testDualScreenService.IsSpanned = true;
|
||||
testDualScreenService.SetLocationOnScreen(new Point(400, 0));
|
||||
var result = new StackLayout() { IsPlatformEnabled = true };
|
||||
|
||||
result.Layout(new Rectangle(400, 0, 200, testDualScreenService.ScaledScreenSize.Height));
|
||||
DualScreenInfo info = new DualScreenInfo(result, testDualScreenService);
|
||||
|
||||
|
||||
var left = info.SpanningBounds[0];
|
||||
|
||||
Assert.AreEqual(0, left.X);
|
||||
Assert.AreEqual(0, left.Y);
|
||||
Assert.AreEqual(90, left.Width);
|
||||
Assert.AreEqual(testDualScreenService.ScaledScreenSize.Height, left.Height);
|
||||
|
||||
var right = info.SpanningBounds[1];
|
||||
Assert.AreEqual(110, right.X);
|
||||
Assert.AreEqual(0, right.Y);
|
||||
Assert.AreEqual(90, right.Width);
|
||||
Assert.AreEqual(testDualScreenService.ScaledScreenSize.Height, right.Height);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -7,6 +7,7 @@ namespace Xamarin.Forms.DualScreen
|
|||
{
|
||||
public class CompactModeArgs : EventArgs
|
||||
{
|
||||
Func<Task> _close;
|
||||
public CompactModeArgs(Func<Task> close, bool success)
|
||||
{
|
||||
if(close == null)
|
||||
|
@ -14,11 +15,11 @@ namespace Xamarin.Forms.DualScreen
|
|||
close = () => Task.Delay(0);
|
||||
}
|
||||
|
||||
Close = close;
|
||||
_close = close;
|
||||
Success = success;
|
||||
}
|
||||
|
||||
public Func<Task> Close { get; }
|
||||
public Task CloseAsync() => _close();
|
||||
public bool Success { get; }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Xamarin.Forms.DualScreen
|
||||
{
|
||||
public partial class DualScreenInfo : INotifyPropertyChanged
|
||||
{
|
||||
public Task<int> GetHingeAngleAsync() => DualScreenService.GetHingeAngleAsync();
|
||||
}
|
||||
}
|
|
@ -7,16 +7,19 @@ using System.Threading.Tasks;
|
|||
|
||||
namespace Xamarin.Forms.DualScreen
|
||||
{
|
||||
public class DualScreenInfo : INotifyPropertyChanged
|
||||
{
|
||||
static Lazy<DualScreenInfo> _dualScreenInfo { get; } = new Lazy<DualScreenInfo>(OnCreate);
|
||||
public event PropertyChangedEventHandler PropertyChanged;
|
||||
Rectangle[] _spanningBounds;
|
||||
Rectangle _hingeBounds;
|
||||
bool _isLandscape;
|
||||
TwoPaneViewMode _spanMode;
|
||||
public partial class DualScreenInfo : INotifyPropertyChanged
|
||||
{
|
||||
static Lazy<DualScreenInfo> _dualScreenInfo { get; } = new Lazy<DualScreenInfo>(OnCreate);
|
||||
public event PropertyChangedEventHandler PropertyChanged;
|
||||
Rectangle[] _spanningBounds;
|
||||
Rectangle _hingeBounds;
|
||||
bool _isLandscape;
|
||||
TwoPaneViewMode _spanMode;
|
||||
TwoPaneViewLayoutGuide _twoPaneViewLayoutGuide;
|
||||
IDualScreenService _dualScreenService;
|
||||
public static DualScreenInfo Current => _dualScreenInfo.Value;
|
||||
IDualScreenService DualScreenService =>
|
||||
_dualScreenService ?? DependencyService.Get<IDualScreenService>() ?? NoDualScreenServiceImpl.Instance;
|
||||
|
||||
public DualScreenInfo(Layout layout) : this(layout, null)
|
||||
{
|
||||
|
@ -24,6 +27,7 @@ namespace Xamarin.Forms.DualScreen
|
|||
|
||||
internal DualScreenInfo(Layout layout, IDualScreenService dualScreenService)
|
||||
{
|
||||
_dualScreenService = dualScreenService;
|
||||
if (layout == null)
|
||||
{
|
||||
_twoPaneViewLayoutGuide = TwoPaneViewLayoutGuide.Instance;
|
||||
|
@ -36,94 +40,95 @@ namespace Xamarin.Forms.DualScreen
|
|||
}
|
||||
|
||||
public Rectangle[] SpanningBounds
|
||||
{
|
||||
get => GetSpanningBounds();
|
||||
set
|
||||
{
|
||||
SetProperty(ref _spanningBounds, value);
|
||||
}
|
||||
}
|
||||
{
|
||||
get => GetSpanningBounds();
|
||||
set
|
||||
{
|
||||
SetProperty(ref _spanningBounds, value);
|
||||
}
|
||||
}
|
||||
|
||||
public Rectangle HingeBounds
|
||||
{
|
||||
get => GetHingeBounds();
|
||||
set
|
||||
{
|
||||
SetProperty(ref _hingeBounds, value);
|
||||
}
|
||||
}
|
||||
public Rectangle HingeBounds
|
||||
{
|
||||
get => GetHingeBounds();
|
||||
set
|
||||
{
|
||||
SetProperty(ref _hingeBounds, value);
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsLandscape
|
||||
{
|
||||
get => GetIsLandscape();
|
||||
set
|
||||
{
|
||||
SetProperty(ref _isLandscape, value);
|
||||
}
|
||||
}
|
||||
public bool IsLandscape
|
||||
{
|
||||
get => GetIsLandscape();
|
||||
set
|
||||
{
|
||||
SetProperty(ref _isLandscape, value);
|
||||
}
|
||||
}
|
||||
|
||||
public TwoPaneViewMode SpanMode
|
||||
{
|
||||
get => GetSpanMode();
|
||||
set
|
||||
{
|
||||
SetProperty(ref _spanMode, value);
|
||||
}
|
||||
}
|
||||
public TwoPaneViewMode SpanMode
|
||||
{
|
||||
get => GetSpanMode();
|
||||
set
|
||||
{
|
||||
SetProperty(ref _spanMode, value);
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle[] GetSpanningBounds()
|
||||
{
|
||||
var guide = _twoPaneViewLayoutGuide;
|
||||
var hinge = guide.Hinge;
|
||||
|
||||
Rectangle[] GetSpanningBounds()
|
||||
{
|
||||
var guide = _twoPaneViewLayoutGuide;
|
||||
var hinge = guide.Hinge;
|
||||
guide.UpdateLayouts();
|
||||
if (hinge == Rectangle.Zero)
|
||||
return new Rectangle[0];
|
||||
|
||||
if (hinge == Rectangle.Zero)
|
||||
return new Rectangle[0];
|
||||
if (guide.Pane2 == Rectangle.Zero)
|
||||
return new Rectangle[0];
|
||||
|
||||
if (guide.Pane2 == Rectangle.Zero)
|
||||
return new Rectangle[0];
|
||||
if(IsLandscape)
|
||||
return new[] { guide.Pane1, new Rectangle(0, hinge.Height + guide.Pane1.Height, guide.Pane2.Width, guide.Pane2.Height) };
|
||||
else
|
||||
return new[] { guide.Pane1, new Rectangle(hinge.Width + guide.Pane1.Width, 0, guide.Pane2.Width, guide.Pane2.Height) };
|
||||
}
|
||||
|
||||
return new[] { guide.Pane1, guide.Pane2 };
|
||||
}
|
||||
Rectangle GetHingeBounds()
|
||||
{
|
||||
var guide = _twoPaneViewLayoutGuide;
|
||||
return guide.Hinge;
|
||||
}
|
||||
|
||||
Rectangle GetHingeBounds()
|
||||
{
|
||||
var guide = _twoPaneViewLayoutGuide;
|
||||
guide.UpdateLayouts();
|
||||
return guide.Hinge;
|
||||
}
|
||||
bool GetIsLandscape() => _twoPaneViewLayoutGuide.IsLandscape;
|
||||
|
||||
bool GetIsLandscape() => _twoPaneViewLayoutGuide.IsLandscape;
|
||||
TwoPaneViewMode GetSpanMode() => _twoPaneViewLayoutGuide.Mode;
|
||||
|
||||
TwoPaneViewMode GetSpanMode() => _twoPaneViewLayoutGuide.Mode;
|
||||
|
||||
static DualScreenInfo OnCreate()
|
||||
{
|
||||
DualScreenInfo dualScreenInfo = new DualScreenInfo(null);
|
||||
static DualScreenInfo OnCreate()
|
||||
{
|
||||
DualScreenInfo dualScreenInfo = new DualScreenInfo(null);
|
||||
dualScreenInfo._twoPaneViewLayoutGuide.WatchForChanges();
|
||||
dualScreenInfo._twoPaneViewLayoutGuide.PropertyChanged += dualScreenInfo.OnTwoPaneViewLayoutGuideChanged;
|
||||
return dualScreenInfo;
|
||||
}
|
||||
return dualScreenInfo;
|
||||
}
|
||||
|
||||
void OnTwoPaneViewLayoutGuideChanged(object sender, PropertyChangedEventArgs e)
|
||||
{
|
||||
SpanningBounds = GetSpanningBounds();
|
||||
IsLandscape = GetIsLandscape();
|
||||
HingeBounds = GetHingeBounds();
|
||||
SpanMode = GetSpanMode();
|
||||
}
|
||||
void OnTwoPaneViewLayoutGuideChanged(object sender, PropertyChangedEventArgs e)
|
||||
{
|
||||
SpanningBounds = GetSpanningBounds();
|
||||
IsLandscape = GetIsLandscape();
|
||||
HingeBounds = GetHingeBounds();
|
||||
SpanMode = GetSpanMode();
|
||||
}
|
||||
|
||||
bool SetProperty<T>(ref T backingStore, T value,
|
||||
[CallerMemberName]string propertyName = "",
|
||||
Action onChanged = null)
|
||||
{
|
||||
if (EqualityComparer<T>.Default.Equals(backingStore, value))
|
||||
return false;
|
||||
bool SetProperty<T>(ref T backingStore, T value,
|
||||
[CallerMemberName]string propertyName = "",
|
||||
Action onChanged = null)
|
||||
{
|
||||
if (EqualityComparer<T>.Default.Equals(backingStore, value))
|
||||
return false;
|
||||
|
||||
backingStore = value;
|
||||
onChanged?.Invoke();
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
backingStore = value;
|
||||
onChanged?.Invoke();
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ using System.ComponentModel;
|
|||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Android.App;
|
||||
using Android.Util;
|
||||
using Android.Views;
|
||||
using Microsoft.Device.Display;
|
||||
using Xamarin.Forms;
|
||||
|
@ -25,16 +26,17 @@ namespace Xamarin.Forms.DualScreen
|
|||
internal class DualScreenServiceImpl : IDualScreenService, IDisposable
|
||||
{
|
||||
public event EventHandler OnScreenChanged;
|
||||
GenericGlobalLayoutListener _genericGlobalLayoutListener;
|
||||
ScreenHelper _helper;
|
||||
bool _isDuo = false;
|
||||
HingeSensor _hingeSensor;
|
||||
static Activity _mainActivity;
|
||||
static DualScreenServiceImpl _HingeService;
|
||||
|
||||
int _hingeAngle;
|
||||
Rectangle _hingeLocation;
|
||||
bool _isDisposed;
|
||||
bool _isLandscape;
|
||||
Size _pixelScreenSize;
|
||||
object _hingeAngleLock = new object();
|
||||
TaskCompletionSource<int> _gettingHingeAngle;
|
||||
|
||||
Activity MainActivity
|
||||
{
|
||||
|
@ -45,7 +47,6 @@ namespace Xamarin.Forms.DualScreen
|
|||
public DualScreenServiceImpl()
|
||||
{
|
||||
_HingeService = this;
|
||||
_genericGlobalLayoutListener = new GenericGlobalLayoutListener(() => OnScreenChanged?.Invoke(this, EventArgs.Empty));
|
||||
if (_mainActivity != null)
|
||||
Init(_mainActivity);
|
||||
}
|
||||
|
@ -78,56 +79,116 @@ namespace Xamarin.Forms.DualScreen
|
|||
_HingeService._helper = new ScreenHelper();
|
||||
}
|
||||
|
||||
if (_HingeService._hingeSensor != null)
|
||||
{
|
||||
_HingeService._hingeSensor.OnSensorChanged -= _HingeService.OnSensorChanged;
|
||||
_HingeService._hingeSensor.StopListening();
|
||||
}
|
||||
|
||||
_HingeService._isDuo = _HingeService._helper.Initialize(_HingeService.MainActivity);
|
||||
|
||||
if (_HingeService._isDuo)
|
||||
{
|
||||
_HingeService._hingeSensor = new HingeSensor(_HingeService.MainActivity);
|
||||
_HingeService._hingeSensor.OnSensorChanged += _HingeService.OnSensorChanged;
|
||||
_HingeService._hingeSensor.StartListening();
|
||||
}
|
||||
}
|
||||
|
||||
void ConfigurationChanged(object sender, EventArgs e)
|
||||
{
|
||||
bool screenChanged = false;
|
||||
if (_isLandscape != IsLandscape)
|
||||
OnScreenChanged?.Invoke(this, e);
|
||||
|
||||
_isLandscape = IsLandscape;
|
||||
}
|
||||
|
||||
void OnSensorChanged(object sender, HingeSensor.HingeSensorChangedEventArgs e)
|
||||
{
|
||||
if (_hingeLocation != GetHinge())
|
||||
{
|
||||
_hingeLocation = GetHinge();
|
||||
_isLandscape = IsLandscape;
|
||||
screenChanged = true;
|
||||
}
|
||||
|
||||
if (_hingeAngle != e.HingeAngle)
|
||||
OnScreenChanged?.Invoke(this, EventArgs.Empty);
|
||||
if (_mainActivity != null)
|
||||
{
|
||||
using (DisplayMetrics display = _mainActivity.Resources.DisplayMetrics)
|
||||
{
|
||||
var scalingFactor = display.Density;
|
||||
_pixelScreenSize = new Size(display.WidthPixels, display.HeightPixels);
|
||||
var newSize = new Size(_pixelScreenSize.Width / scalingFactor, _pixelScreenSize.Height / scalingFactor);
|
||||
|
||||
_hingeAngle = e.HingeAngle;
|
||||
if (newSize != ScaledScreenSize)
|
||||
{
|
||||
ScaledScreenSize = newSize;
|
||||
screenChanged = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(screenChanged)
|
||||
OnScreenChanged?.Invoke(this, e);
|
||||
}
|
||||
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_hingeSensor != null)
|
||||
{
|
||||
_hingeSensor.OnSensorChanged -= OnSensorChanged;
|
||||
_hingeSensor.StopListening();
|
||||
}
|
||||
if (_isDisposed)
|
||||
return;
|
||||
|
||||
_isDisposed = true;
|
||||
|
||||
// make sure the one shot task is cleared out if it's running
|
||||
SetHingeAngle(0);
|
||||
StopListeningForHingeChanges();
|
||||
}
|
||||
|
||||
public Size ScaledScreenSize
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
public bool IsSpanned
|
||||
=> _isDuo && (_helper?.IsDualMode ?? false);
|
||||
|
||||
public DeviceInfo DeviceInfo => Device.info;
|
||||
void StartListeningForHingeChanges()
|
||||
{
|
||||
if (_hingeSensor == null)
|
||||
return;
|
||||
|
||||
_hingeSensor.OnSensorChanged += OnSensorChanged;
|
||||
_hingeSensor.StartListening();
|
||||
|
||||
}
|
||||
|
||||
void StopListeningForHingeChanges()
|
||||
{
|
||||
if (_hingeSensor == null)
|
||||
return;
|
||||
|
||||
_hingeSensor.OnSensorChanged -= OnSensorChanged;
|
||||
_hingeSensor.StopListening();
|
||||
}
|
||||
|
||||
void OnSensorChanged(object sender, HingeSensor.HingeSensorChangedEventArgs e)
|
||||
{
|
||||
SetHingeAngle(e.HingeAngle);
|
||||
}
|
||||
|
||||
void SetHingeAngle(int hingeAngle)
|
||||
{
|
||||
TaskCompletionSource<int> toSet = null;
|
||||
lock (_hingeAngleLock)
|
||||
{
|
||||
StopListeningForHingeChanges();
|
||||
toSet = _gettingHingeAngle;
|
||||
_gettingHingeAngle = null;
|
||||
}
|
||||
|
||||
if (toSet != null)
|
||||
toSet.SetResult(hingeAngle);
|
||||
}
|
||||
|
||||
public Task<int> GetHingeAngleAsync()
|
||||
{
|
||||
lock (_hingeAngleLock)
|
||||
{
|
||||
if (_gettingHingeAngle == null)
|
||||
{
|
||||
_gettingHingeAngle = new TaskCompletionSource<int>();
|
||||
StartListeningForHingeChanges();
|
||||
}
|
||||
}
|
||||
|
||||
return _gettingHingeAngle.Task;
|
||||
}
|
||||
|
||||
public Rectangle GetHinge()
|
||||
{
|
||||
|
@ -137,7 +198,7 @@ namespace Xamarin.Forms.DualScreen
|
|||
var rotation = ScreenHelper.GetRotation(_helper.Activity);
|
||||
var hinge = _helper.DisplayMask.GetBoundingRectsForRotation(rotation).FirstOrDefault();
|
||||
var hingeDp = new Rectangle(PixelsToDp(hinge.Left), PixelsToDp(hinge.Top), PixelsToDp(hinge.Width()), PixelsToDp(hinge.Height()));
|
||||
|
||||
|
||||
return hingeDp;
|
||||
}
|
||||
|
||||
|
@ -170,23 +231,65 @@ namespace Xamarin.Forms.DualScreen
|
|||
return new Point(view.View.Context.FromPixels(location[0]), view.View.Context.FromPixels(location[1]));
|
||||
}
|
||||
|
||||
public void WatchForChangesOnLayout(VisualElement visualElement)
|
||||
public object WatchForChangesOnLayout(VisualElement visualElement, Action action)
|
||||
{
|
||||
if (action == null)
|
||||
return null;
|
||||
|
||||
var view = Platform.Android.Platform.GetRenderer(visualElement);
|
||||
var androidView = view?.View;
|
||||
|
||||
if (view?.View == null)
|
||||
return;
|
||||
if (androidView == null || !androidView.IsAlive())
|
||||
return null;
|
||||
|
||||
view.View.ViewTreeObserver.AddOnGlobalLayoutListener(_genericGlobalLayoutListener);
|
||||
ViewTreeObserver.IOnGlobalLayoutListener listener = null;
|
||||
listener = new GenericGlobalLayoutListener(() =>
|
||||
{
|
||||
if (!androidView.IsAlive())
|
||||
{
|
||||
action = null;
|
||||
androidView = null;
|
||||
try
|
||||
{
|
||||
_mainActivity?.Window?.DecorView?.RootView?.ViewTreeObserver?.RemoveOnGlobalLayoutListener(listener);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// just in case something along the call path here is disposed of
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
action?.Invoke();
|
||||
});
|
||||
|
||||
androidView.ViewTreeObserver.AddOnGlobalLayoutListener(listener);
|
||||
return listener;
|
||||
}
|
||||
|
||||
public void StopWatchingForChangesOnLayout(VisualElement visualElement)
|
||||
public void StopWatchingForChangesOnLayout(VisualElement visualElement, object handle)
|
||||
{
|
||||
var view = Platform.Android.Platform.GetRenderer(visualElement);
|
||||
if (view?.View == null)
|
||||
if (handle == null)
|
||||
return;
|
||||
|
||||
view.View.ViewTreeObserver.RemoveOnGlobalLayoutListener(_genericGlobalLayoutListener);
|
||||
var view = Platform.Android.Platform.GetRenderer(visualElement);
|
||||
var androidView = view?.View;
|
||||
|
||||
if (androidView == null || !androidView.IsAlive())
|
||||
return;
|
||||
|
||||
if (handle is ViewTreeObserver.IOnGlobalLayoutListener vto)
|
||||
{
|
||||
try
|
||||
{
|
||||
view.View.ViewTreeObserver.RemoveOnGlobalLayoutListener(vto);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// just in case something along the call path here is disposed of
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,13 +24,22 @@ namespace Xamarin.Forms.DualScreen
|
|||
|
||||
public DualScreenService()
|
||||
{
|
||||
}
|
||||
if(Window.Current != null)
|
||||
Window.Current.SizeChanged += OnCurrentSizeChanged;
|
||||
}
|
||||
|
||||
public bool IsSpanned
|
||||
public Task<int> GetHingeAngleAsync() => Task.FromResult(0);
|
||||
|
||||
void OnCurrentSizeChanged(object sender, Windows.UI.Core.WindowSizeChangedEventArgs e)
|
||||
{
|
||||
OnScreenChanged?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
public bool IsSpanned
|
||||
{
|
||||
get
|
||||
{
|
||||
var visibleBounds = ApplicationView.GetForCurrentView().VisibleBounds;
|
||||
var visibleBounds = Window.Current.Bounds;
|
||||
|
||||
if (visibleBounds.Height > 1200 || visibleBounds.Width > 1200)
|
||||
return true;
|
||||
|
@ -51,7 +60,16 @@ namespace Xamarin.Forms.DualScreen
|
|||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
public Size ScaledScreenSize
|
||||
{
|
||||
get
|
||||
{
|
||||
Windows.Foundation.Rect windowSize = Window.Current.Bounds;
|
||||
return new Size(windowSize.Width, windowSize.Height);
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -86,29 +104,34 @@ namespace Xamarin.Forms.DualScreen
|
|||
return new Point(screenCoords.X, screenCoords.Y);
|
||||
}
|
||||
|
||||
public void WatchForChangesOnLayout(VisualElement visualElement)
|
||||
public object WatchForChangesOnLayout(VisualElement visualElement, Action action)
|
||||
{
|
||||
var view = Platform.UWP.Platform.GetRenderer(visualElement);
|
||||
|
||||
if (view?.ContainerElement == null)
|
||||
return null;
|
||||
|
||||
EventHandler<object> layoutUpdated = (_, __) =>
|
||||
{
|
||||
action();
|
||||
};
|
||||
|
||||
view.ContainerElement.LayoutUpdated += layoutUpdated;
|
||||
return layoutUpdated;
|
||||
}
|
||||
|
||||
public void StopWatchingForChangesOnLayout(VisualElement visualElement, object handle)
|
||||
{
|
||||
if (handle == null)
|
||||
return;
|
||||
|
||||
var view = Platform.UWP.Platform.GetRenderer(visualElement);
|
||||
|
||||
if (view?.ContainerElement == null)
|
||||
return;
|
||||
|
||||
view.ContainerElement.LayoutUpdated += OnContainerElementLayoutUpdated;
|
||||
}
|
||||
|
||||
public void StopWatchingForChangesOnLayout(VisualElement visualElement)
|
||||
{
|
||||
var view = Platform.UWP.Platform.GetRenderer(visualElement);
|
||||
|
||||
if (view?.ContainerElement == null)
|
||||
return;
|
||||
|
||||
view.ContainerElement.LayoutUpdated -= OnContainerElementLayoutUpdated;
|
||||
}
|
||||
|
||||
void OnContainerElementLayoutUpdated(object sender, object e)
|
||||
{
|
||||
OnScreenChanged?.Invoke(this, EventArgs.Empty);
|
||||
if(handle is EventHandler<object> handler)
|
||||
view.ContainerElement.LayoutUpdated -= handler;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -15,19 +15,24 @@ namespace Xamarin.Forms.DualScreen
|
|||
{
|
||||
}
|
||||
|
||||
public bool IsSpanned => false;
|
||||
public Task<int> GetHingeAngleAsync() => Task.FromResult(0);
|
||||
|
||||
public bool IsSpanned => false;
|
||||
|
||||
public bool IsLandscape => Device.info.CurrentOrientation.IsLandscape();
|
||||
|
||||
public DeviceInfo DeviceInfo => Device.info;
|
||||
|
||||
#pragma warning disable CS0067
|
||||
public event EventHandler OnScreenChanged;
|
||||
#pragma warning restore CS0067
|
||||
|
||||
public void Dispose()
|
||||
public void Dispose()
|
||||
{
|
||||
}
|
||||
|
||||
public Rectangle GetHinge()
|
||||
public Size ScaledScreenSize => Device.info.ScaledScreenSize;
|
||||
public Rectangle GetHinge()
|
||||
{
|
||||
return Rectangle.Zero;
|
||||
}
|
||||
|
@ -37,19 +42,27 @@ namespace Xamarin.Forms.DualScreen
|
|||
return null;
|
||||
}
|
||||
|
||||
public void WatchForChangesOnLayout(VisualElement visualElement)
|
||||
public object WatchForChangesOnLayout(VisualElement visualElement, Action action)
|
||||
{
|
||||
visualElement.BatchCommitted += OnLayoutChangesCommited;
|
||||
if (action == null)
|
||||
return null;
|
||||
|
||||
EventHandler<EventArg<VisualElement>> layoutUpdated = (_, __) =>
|
||||
{
|
||||
action();
|
||||
};
|
||||
|
||||
visualElement.BatchCommitted += layoutUpdated;
|
||||
return layoutUpdated;
|
||||
}
|
||||
|
||||
public void StopWatchingForChangesOnLayout(VisualElement visualElement)
|
||||
public void StopWatchingForChangesOnLayout(VisualElement visualElement, object handle)
|
||||
{
|
||||
visualElement.BatchCommitted -= OnLayoutChangesCommited;
|
||||
}
|
||||
if (handle == null)
|
||||
return;
|
||||
|
||||
void OnLayoutChangesCommited(object sender, EventArg<VisualElement> e)
|
||||
{
|
||||
OnScreenChanged?.Invoke(this, EventArgs.Empty);
|
||||
if (handle is EventHandler<EventArg<VisualElement>> handler)
|
||||
visualElement.BatchCommitted -= handler;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -58,14 +58,20 @@ namespace Xamarin.Forms.DualScreen
|
|||
ViewMode _currentMode;
|
||||
bool _hasMeasured = false;
|
||||
bool _updatingMode = false;
|
||||
bool _performingLayout = false;
|
||||
bool _processPendingChange = false;
|
||||
Rectangle _layoutGuidePane1;
|
||||
Rectangle _layoutGuidePane2;
|
||||
TwoPaneViewMode _layoutGuideMode;
|
||||
Rectangle _layoutGuideHinge;
|
||||
bool _layoutGuideIsLandscape;
|
||||
double _previousWidth = -1;
|
||||
double _previousHeight = -1;
|
||||
|
||||
public static readonly BindableProperty TallModeConfigurationProperty
|
||||
= BindableProperty.Create("TallModeConfiguration", typeof(TwoPaneViewTallModeConfiguration), typeof(TwoPaneView), defaultValue: TwoPaneViewTallModeConfiguration.TopBottom, propertyChanged: OnJustInvalidateLayout);
|
||||
= BindableProperty.Create("TallModeConfiguration", typeof(TwoPaneViewTallModeConfiguration), typeof(TwoPaneView), defaultValue: TwoPaneViewTallModeConfiguration.TopBottom, propertyChanged: TwoPaneViewLayoutPropertyChanged);
|
||||
|
||||
public static readonly BindableProperty WideModeConfigurationProperty
|
||||
= BindableProperty.Create("WideModeConfiguration", typeof(TwoPaneViewWideModeConfiguration), typeof(TwoPaneView), defaultValue: TwoPaneViewWideModeConfiguration.LeftRight, propertyChanged: OnJustInvalidateLayout);
|
||||
= BindableProperty.Create("WideModeConfiguration", typeof(TwoPaneViewWideModeConfiguration), typeof(TwoPaneView), defaultValue: TwoPaneViewWideModeConfiguration.LeftRight, propertyChanged: TwoPaneViewLayoutPropertyChanged);
|
||||
|
||||
public static readonly BindableProperty Pane1Property
|
||||
= BindableProperty.Create("Pane1", typeof(View), typeof(TwoPaneView), propertyChanged: (b, o, n) => OnPanePropertyChanged(b, o, n, 0));
|
||||
|
@ -79,19 +85,19 @@ namespace Xamarin.Forms.DualScreen
|
|||
public static readonly BindableProperty ModeProperty = ModePropertyKey.BindableProperty;
|
||||
|
||||
public static readonly BindableProperty PanePriorityProperty
|
||||
= BindableProperty.Create("PanePriority", typeof(TwoPaneViewPriority), typeof(TwoPaneView), defaultValue: TwoPaneViewPriority.Pane1, propertyChanged: OnJustInvalidateLayout);
|
||||
= BindableProperty.Create("PanePriority", typeof(TwoPaneViewPriority), typeof(TwoPaneView), defaultValue: TwoPaneViewPriority.Pane1, propertyChanged: TwoPaneViewLayoutPropertyChanged);
|
||||
|
||||
public static readonly BindableProperty MinTallModeHeightProperty
|
||||
= BindableProperty.Create("MinTallModeHeight", typeof(double), typeof(TwoPaneView), defaultValueCreator: OnMinModePropertyCreate, propertyChanged: OnJustInvalidateLayout);
|
||||
= BindableProperty.Create("MinTallModeHeight", typeof(double), typeof(TwoPaneView), defaultValueCreator: OnMinModePropertyCreate, propertyChanged: TwoPaneViewLayoutPropertyChanged);
|
||||
|
||||
public static readonly BindableProperty MinWideModeWidthProperty
|
||||
= BindableProperty.Create("MinWideModeWidth", typeof(double), typeof(TwoPaneView), defaultValueCreator: OnMinModePropertyCreate, propertyChanged: OnJustInvalidateLayout);
|
||||
= BindableProperty.Create("MinWideModeWidth", typeof(double), typeof(TwoPaneView), defaultValueCreator: OnMinModePropertyCreate, propertyChanged: TwoPaneViewLayoutPropertyChanged);
|
||||
|
||||
public static readonly BindableProperty Pane1LengthProperty
|
||||
= BindableProperty.Create("Pane1Length", typeof(GridLength), typeof(TwoPaneView), defaultValue: GridLength.Star, propertyChanged: OnJustInvalidateLayout);
|
||||
= BindableProperty.Create("Pane1Length", typeof(GridLength), typeof(TwoPaneView), defaultValue: GridLength.Star, propertyChanged: TwoPaneViewLayoutPropertyChanged);
|
||||
|
||||
public static readonly BindableProperty Pane2LengthProperty
|
||||
= BindableProperty.Create("Pane2Length", typeof(GridLength), typeof(TwoPaneView), defaultValue: GridLength.Star, propertyChanged: OnJustInvalidateLayout);
|
||||
= BindableProperty.Create("Pane2Length", typeof(GridLength), typeof(TwoPaneView), defaultValue: GridLength.Star, propertyChanged: TwoPaneViewLayoutPropertyChanged);
|
||||
|
||||
public event EventHandler ModeChanged;
|
||||
|
||||
|
@ -111,17 +117,11 @@ namespace Xamarin.Forms.DualScreen
|
|||
((TwoPaneView)bindable).ModeChanged?.Invoke(bindable, EventArgs.Empty);
|
||||
}
|
||||
|
||||
static void OnJustInvalidateLayout(BindableObject bindable, object oldValue, object newValue)
|
||||
|
||||
static void TwoPaneViewLayoutPropertyChanged(BindableObject bindable, object oldValue, object newValue)
|
||||
{
|
||||
var b = (TwoPaneView)bindable;
|
||||
if (!b._performingLayout && !b._updatingMode)
|
||||
{
|
||||
b.UpdateMode();
|
||||
}
|
||||
else
|
||||
{
|
||||
b._processPendingChange = true;
|
||||
}
|
||||
b.UpdateMode();
|
||||
}
|
||||
|
||||
static void OnPanePropertyChanged(BindableObject bindable, object oldValue, object newValue, int paneIndex)
|
||||
|
@ -134,7 +134,7 @@ namespace Xamarin.Forms.DualScreen
|
|||
else
|
||||
twoPaneView._content2.Content = newView;
|
||||
|
||||
OnJustInvalidateLayout(bindable, null, null);
|
||||
twoPaneView.UpdateMode();
|
||||
}
|
||||
|
||||
public double MinTallModeHeight
|
||||
|
@ -233,22 +233,52 @@ namespace Xamarin.Forms.DualScreen
|
|||
base.OnIsPlatformEnabledChanged();
|
||||
if (IsPlatformEnabled)
|
||||
{
|
||||
TwoPaneViewLayoutGuide.WatchForChanges();
|
||||
TwoPaneViewLayoutGuide.PropertyChanged += OnTwoPaneViewLayoutGuide;
|
||||
_twoPaneViewLayoutGuide.WatchForChanges();
|
||||
_twoPaneViewLayoutGuide.PropertyChanged += OnTwoPaneViewLayoutGuide;
|
||||
}
|
||||
else
|
||||
{
|
||||
TwoPaneViewLayoutGuide.StopWatchingForChanges();
|
||||
TwoPaneViewLayoutGuide.PropertyChanged -= OnTwoPaneViewLayoutGuide;
|
||||
_twoPaneViewLayoutGuide.StopWatchingForChanges();
|
||||
_twoPaneViewLayoutGuide.PropertyChanged -= OnTwoPaneViewLayoutGuide;
|
||||
}
|
||||
}
|
||||
|
||||
TwoPaneViewLayoutGuide TwoPaneViewLayoutGuide => _twoPaneViewLayoutGuide;
|
||||
|
||||
|
||||
void OnTwoPaneViewLayoutGuide(object sender, System.ComponentModel.PropertyChangedEventArgs e)
|
||||
{
|
||||
OnJustInvalidateLayout(this, null, null);
|
||||
if (_twoPaneViewLayoutGuide.Pane1 == _layoutGuidePane1 &&
|
||||
_twoPaneViewLayoutGuide.Pane2 == _layoutGuidePane2 &&
|
||||
_twoPaneViewLayoutGuide.Mode == _layoutGuideMode &&
|
||||
_twoPaneViewLayoutGuide.Hinge == _layoutGuideHinge &&
|
||||
_twoPaneViewLayoutGuide.IsLandscape == _layoutGuideIsLandscape)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_layoutGuidePane1 = _twoPaneViewLayoutGuide.Pane1;
|
||||
_layoutGuidePane2 = _twoPaneViewLayoutGuide.Pane2;
|
||||
_layoutGuideMode = _twoPaneViewLayoutGuide.Mode;
|
||||
_layoutGuideHinge = _twoPaneViewLayoutGuide.Hinge;
|
||||
_layoutGuideIsLandscape = _twoPaneViewLayoutGuide.IsLandscape;
|
||||
|
||||
UpdateMode();
|
||||
}
|
||||
|
||||
|
||||
protected override void OnSizeAllocated(double width, double height)
|
||||
{
|
||||
if (!_updatingMode &&
|
||||
width > 0 &&
|
||||
height > 0 &&
|
||||
width != _previousWidth &&
|
||||
height != _previousHeight)
|
||||
{
|
||||
_previousWidth = width;
|
||||
_previousHeight = height;
|
||||
UpdateMode(false);
|
||||
}
|
||||
|
||||
base.OnSizeAllocated(width, height);
|
||||
}
|
||||
|
||||
protected override void LayoutChildren(double x, double y, double width, double height)
|
||||
|
@ -262,8 +292,12 @@ namespace Xamarin.Forms.DualScreen
|
|||
UpdateMode();
|
||||
}
|
||||
|
||||
void UpdateMode()
|
||||
void UpdateMode(bool invalidateLayout = true)
|
||||
{
|
||||
// controls hasn't fully been created yet
|
||||
if (RowDefinitions.Count != 3 || ColumnDefinitions.Count != 3)
|
||||
return;
|
||||
|
||||
if (_updatingMode)
|
||||
{
|
||||
_processPendingChange = true;
|
||||
|
@ -280,11 +314,11 @@ namespace Xamarin.Forms.DualScreen
|
|||
|
||||
_hasMeasured = true;
|
||||
|
||||
TwoPaneViewLayoutGuide.UpdateLayouts();
|
||||
_twoPaneViewLayoutGuide.UpdateLayouts();
|
||||
|
||||
if (TwoPaneViewLayoutGuide.Mode != TwoPaneViewMode.SinglePane)
|
||||
if (_twoPaneViewLayoutGuide.Mode != TwoPaneViewMode.SinglePane)
|
||||
{
|
||||
if (TwoPaneViewLayoutGuide.Mode == TwoPaneViewMode.Wide)
|
||||
if (_twoPaneViewLayoutGuide.Mode == TwoPaneViewMode.Wide)
|
||||
{
|
||||
// Regions are laid out horizontally
|
||||
if (WideModeConfiguration != TwoPaneViewWideModeConfiguration.SinglePane)
|
||||
|
@ -292,7 +326,7 @@ namespace Xamarin.Forms.DualScreen
|
|||
newMode = (WideModeConfiguration == TwoPaneViewWideModeConfiguration.LeftRight) ? ViewMode.LeftRight : ViewMode.RightLeft;
|
||||
}
|
||||
}
|
||||
else if (TwoPaneViewLayoutGuide.Mode == TwoPaneViewMode.Tall)
|
||||
else if (_twoPaneViewLayoutGuide.Mode == TwoPaneViewMode.Tall)
|
||||
{
|
||||
// Regions are laid out vertically
|
||||
if (TallModeConfiguration != TwoPaneViewTallModeConfiguration.SinglePane)
|
||||
|
@ -368,12 +402,8 @@ namespace Xamarin.Forms.DualScreen
|
|||
}
|
||||
else
|
||||
{
|
||||
InvalidateLayout();
|
||||
Device.BeginInvokeOnMainThread(() =>
|
||||
{
|
||||
TwoPaneViewLayoutGuide.UpdateLayouts();
|
||||
if (invalidateLayout)
|
||||
InvalidateLayout();
|
||||
});
|
||||
}
|
||||
}
|
||||
finally
|
||||
|
@ -396,46 +426,56 @@ namespace Xamarin.Forms.DualScreen
|
|||
Rectangle pane2 = _twoPaneViewLayoutGuide.Pane2;
|
||||
bool isLayoutSpanned = _twoPaneViewLayoutGuide.Mode != TwoPaneViewMode.SinglePane;
|
||||
|
||||
columnMiddle.Width = new GridLength(0, GridUnitType.Absolute);
|
||||
rowMiddle.Height = new GridLength(0, GridUnitType.Absolute);
|
||||
|
||||
if (newMode == ViewMode.LeftRight || newMode == ViewMode.RightLeft)
|
||||
{
|
||||
columnLeft.Width = ((newMode == ViewMode.LeftRight) ? Pane1Length : Pane2Length);
|
||||
columnRight.Width = ((newMode == ViewMode.LeftRight) ? Pane2Length : Pane1Length);
|
||||
}
|
||||
else
|
||||
{
|
||||
columnLeft.Width = new GridLength(1, GridUnitType.Star);
|
||||
columnRight.Width = new GridLength(0, GridUnitType.Absolute);
|
||||
}
|
||||
|
||||
if (newMode == ViewMode.TopBottom || newMode == ViewMode.BottomTop)
|
||||
{
|
||||
rowTop.Height = ((newMode == ViewMode.TopBottom) ? Pane1Length : Pane2Length);
|
||||
rowBottom.Height = ((newMode == ViewMode.TopBottom) ? Pane2Length : Pane1Length);
|
||||
}
|
||||
else
|
||||
{
|
||||
rowTop.Height = new GridLength(1, GridUnitType.Star);
|
||||
rowBottom.Height = new GridLength(0, GridUnitType.Absolute);
|
||||
}
|
||||
|
||||
if (TwoPaneViewLayoutGuide.Mode != TwoPaneViewMode.SinglePane && newMode != ViewMode.Pane1Only && newMode != ViewMode.Pane2Only)
|
||||
if (_twoPaneViewLayoutGuide.Mode != TwoPaneViewMode.SinglePane && newMode != ViewMode.Pane1Only && newMode != ViewMode.Pane2Only)
|
||||
{
|
||||
Rectangle hinge = _twoPaneViewLayoutGuide.Hinge;
|
||||
|
||||
if (TwoPaneViewLayoutGuide.Mode == TwoPaneViewMode.Wide)
|
||||
if (_twoPaneViewLayoutGuide.Mode == TwoPaneViewMode.Wide)
|
||||
{
|
||||
columnMiddle.Width = new GridLength(hinge.Width, GridUnitType.Absolute);
|
||||
columnLeft.Width = new GridLength(pane1.Width, GridUnitType.Absolute);
|
||||
columnRight.Width = new GridLength(pane2.Width, GridUnitType.Absolute);
|
||||
|
||||
rowMiddle.Height = new GridLength(0, GridUnitType.Absolute);
|
||||
rowTop.Height = GridLength.Star;
|
||||
rowBottom.Height = new GridLength(0, GridUnitType.Absolute);
|
||||
}
|
||||
else
|
||||
{
|
||||
rowMiddle.Height = new GridLength(hinge.Height, GridUnitType.Absolute);
|
||||
rowTop.Height = new GridLength(pane1.Height, GridUnitType.Absolute);
|
||||
rowBottom.Height = new GridLength(pane2.Height, GridUnitType.Absolute);
|
||||
|
||||
columnMiddle.Width = new GridLength(0, GridUnitType.Absolute);
|
||||
columnLeft.Width = GridLength.Star;
|
||||
columnRight.Width = new GridLength(0, GridUnitType.Absolute);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
columnMiddle.Width = new GridLength(0, GridUnitType.Absolute);
|
||||
rowMiddle.Height = new GridLength(0, GridUnitType.Absolute);
|
||||
|
||||
if (newMode == ViewMode.LeftRight || newMode == ViewMode.RightLeft)
|
||||
{
|
||||
columnLeft.Width = ((newMode == ViewMode.LeftRight) ? Pane1Length : Pane2Length);
|
||||
columnRight.Width = ((newMode == ViewMode.LeftRight) ? Pane2Length : Pane1Length);
|
||||
}
|
||||
else
|
||||
{
|
||||
columnLeft.Width = new GridLength(1, GridUnitType.Star);
|
||||
columnRight.Width = new GridLength(0, GridUnitType.Absolute);
|
||||
}
|
||||
|
||||
if (newMode == ViewMode.TopBottom || newMode == ViewMode.BottomTop)
|
||||
{
|
||||
rowTop.Height = ((newMode == ViewMode.TopBottom) ? Pane1Length : Pane2Length);
|
||||
rowBottom.Height = ((newMode == ViewMode.TopBottom) ? Pane2Length : Pane1Length);
|
||||
}
|
||||
else
|
||||
{
|
||||
rowTop.Height = new GridLength(1, GridUnitType.Star);
|
||||
rowBottom.Height = new GridLength(0, GridUnitType.Absolute);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -26,7 +26,7 @@ namespace Xamarin.Forms.DualScreen
|
|||
public event PropertyChangedEventHandler PropertyChanged;
|
||||
List<string> _pendingPropertyChanges = new List<string>();
|
||||
Rectangle _absoluteLayoutPosition;
|
||||
bool _isWatchingForChanges = false;
|
||||
object _watchHandle = null;
|
||||
|
||||
TwoPaneViewLayoutGuide()
|
||||
{
|
||||
|
@ -44,49 +44,37 @@ namespace Xamarin.Forms.DualScreen
|
|||
_dualScreenService = dualScreenService;
|
||||
|
||||
if(_layout != null)
|
||||
{
|
||||
UpdateLayouts();
|
||||
_layout.PropertyChanged += OnLayoutPropertyChanged;
|
||||
_layout.PropertyChanging += OnLayoutPropertyChanging;
|
||||
}
|
||||
}
|
||||
|
||||
void CheckIsPlatformEnabled()
|
||||
void OnLayoutPropertyChanging(object sender, PropertyChangingEventArgs e)
|
||||
{
|
||||
if (_layout == null)
|
||||
return;
|
||||
|
||||
if (_layout.IsPlatformEnabled)
|
||||
{
|
||||
if (!_isWatchingForChanges)
|
||||
{
|
||||
WatchForChanges();
|
||||
_isWatchingForChanges = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_isWatchingForChanges)
|
||||
{
|
||||
StopWatchingForChanges();
|
||||
_isWatchingForChanges = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (e.PropertyName == "Renderer")
|
||||
StopWatchingForChanges();
|
||||
}
|
||||
|
||||
void OnLayoutPropertyChanged(object sender, PropertyChangedEventArgs e) => CheckIsPlatformEnabled();
|
||||
void OnLayoutPropertyChanged(object sender, PropertyChangedEventArgs e)
|
||||
{
|
||||
if (e.PropertyName == "Renderer")
|
||||
WatchForChanges();
|
||||
}
|
||||
|
||||
public void WatchForChanges()
|
||||
{
|
||||
StopWatchingForChanges();
|
||||
DualScreenService.OnScreenChanged += OnScreenChanged;
|
||||
|
||||
if (_layout != null)
|
||||
{
|
||||
DualScreenService.WatchForChangesOnLayout(_layout);
|
||||
}
|
||||
|
||||
if (DualScreenService.DeviceInfo is INotifyPropertyChanged npc)
|
||||
{
|
||||
npc.PropertyChanged += OnDeviceInfoChanged;
|
||||
_watchHandle = DualScreenService.WatchForChangesOnLayout(_layout, () => OnScreenChanged(DualScreenService, EventArgs.Empty));
|
||||
if (_watchHandle == null)
|
||||
return;
|
||||
}
|
||||
|
||||
DualScreenService.OnScreenChanged += OnScreenChanged;
|
||||
}
|
||||
|
||||
public void StopWatchingForChanges()
|
||||
|
@ -95,12 +83,8 @@ namespace Xamarin.Forms.DualScreen
|
|||
|
||||
if (_layout != null)
|
||||
{
|
||||
DualScreenService.StopWatchingForChangesOnLayout(_layout);
|
||||
}
|
||||
|
||||
if (DualScreenService.DeviceInfo is INotifyPropertyChanged npc)
|
||||
{
|
||||
npc.PropertyChanged -= OnDeviceInfoChanged;
|
||||
DualScreenService.StopWatchingForChangesOnLayout(_layout, _watchHandle);
|
||||
_watchHandle = null;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -112,8 +96,6 @@ namespace Xamarin.Forms.DualScreen
|
|||
return;
|
||||
}
|
||||
|
||||
CheckIsPlatformEnabled();
|
||||
|
||||
var screenPosition = DualScreenService.GetLocationOnScreen(_layout);
|
||||
if (screenPosition == null)
|
||||
return;
|
||||
|
@ -127,15 +109,6 @@ namespace Xamarin.Forms.DualScreen
|
|||
}
|
||||
}
|
||||
|
||||
void OnDeviceInfoChanged(object sender, PropertyChangedEventArgs args)
|
||||
{
|
||||
CheckIsPlatformEnabled();
|
||||
if (args.PropertyName == nameof(DualScreenService.DeviceInfo.CurrentOrientation))
|
||||
{
|
||||
UpdateLayouts();
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsLandscape
|
||||
{
|
||||
get => DualScreenService.IsLandscape;
|
||||
|
@ -195,7 +168,7 @@ namespace Xamarin.Forms.DualScreen
|
|||
Rectangle containerArea;
|
||||
if (_layout == null)
|
||||
{
|
||||
containerArea = new Rectangle(Point.Zero, DualScreenService.DeviceInfo.ScaledScreenSize);
|
||||
containerArea = new Rectangle(Point.Zero, DualScreenService.ScaledScreenSize);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -210,7 +183,7 @@ namespace Xamarin.Forms.DualScreen
|
|||
Rectangle containerArea;
|
||||
if (_layout == null)
|
||||
{
|
||||
containerArea = new Rectangle(Point.Zero, DualScreenService.DeviceInfo.ScaledScreenSize);
|
||||
containerArea = new Rectangle(Point.Zero, DualScreenService.ScaledScreenSize);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -227,8 +200,6 @@ namespace Xamarin.Forms.DualScreen
|
|||
|
||||
internal void UpdateLayouts()
|
||||
{
|
||||
CheckIsPlatformEnabled();
|
||||
|
||||
Rectangle containerArea = GetContainerArea();
|
||||
if (containerArea.Width <= 0)
|
||||
{
|
||||
|
|
|
@ -23,7 +23,14 @@ namespace Xamarin.Forms.Platform.Android
|
|||
|
||||
void ElementMeasureInvalidated(object sender, System.EventArgs e)
|
||||
{
|
||||
RequestLayout();
|
||||
if (this.IsAlive())
|
||||
{
|
||||
RequestLayout();
|
||||
}
|
||||
else if(sender is VisualElement ve)
|
||||
{
|
||||
ve.MeasureInvalidated -= ElementMeasureInvalidated;
|
||||
}
|
||||
}
|
||||
|
||||
internal IVisualElementRenderer VisualElementRenderer => Content;
|
||||
|
|
Загрузка…
Ссылка в новой задаче