I have a Xamarin.Forms app with FreshMvvm. Some of its users experience a problem on their iPhones. A page comes up empty. Here is how the page would look with a simulated exception:
All static xaml stuff is there, including labels. But our customers see this instead:
In the ScrollView, only the BoxViews with height = 1 that make those horizontal lines, are there. And no labels.
Here is my xaml:
<ContentPage
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="LATICRETE_MobileApp.Pages.CoverageCalculator.CoverageCalculatorResultsPage"
ControlTemplate="{StaticResource MainPageTemplate}"
xmlns:controls="clr-namespace:LATICRETE_MobileApp.Controls"
Title="Coverage Results" >
<ContentPage.Content>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="4*" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<ScrollView Grid.Row="0">
<StackLayout Margin="30">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="1" />
<RowDefinition Height="Auto" />
<RowDefinition Height="1" />
<RowDefinition Height="Auto" />
<RowDefinition Height="1" />
<RowDefinition Height="Auto" />
<RowDefinition Height="22" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Label Text="Tile Size:" FontAttributes="Bold" Grid.Row="0" Grid.Column="0" />
<Label Text="Tile Thickness:" FontAttributes="Bold" Grid.Row="2" Grid.Column="0" />
<Label Text="Joint Size:" FontAttributes="Bold" Grid.Row="4" Grid.Column="0" />
<Label Text="Trowel Size:" FontAttributes="Bold" Grid.Row="6" Grid.Column="0" />
<Label Text="{Binding TileSize}" Grid.Row="0" Grid.Column="1" />
<BoxView BackgroundColor="Gray" Grid.Row="1" Grid.ColumnSpan="2" />
<Label Text="{Binding TileThickness}" Grid.Row="2" Grid.Column="1" />
<BoxView BackgroundColor="Gray" Grid.Row="3" Grid.ColumnSpan="2" />
<Label Text="{Binding JointSize}" Grid.Row="4" Grid.Column="1" />
<BoxView BackgroundColor="Gray" Grid.Row="5" Grid.ColumnSpan="2" />
<Label Text="{Binding TrowelSize}" Grid.Row="6" Grid.Column="1" />
<BoxView BackgroundColor="{StaticResource LaticreteColor}" Margin="0,10,0,10" Grid.Row="7" Grid.ColumnSpan="2" />
</Grid>
<Label Text="{Binding Adhesive}" FontAttributes="Bold" />
<Label Text="{Binding AdhesiveCoverage}" />
<BoxView BackgroundColor="Gray" HeightRequest="1" />
<Label Text="{Binding Grout}" FontAttributes="Bold" />
<Label Text="{Binding GroutCoverage}" />
<BoxView BackgroundColor="Gray" HeightRequest="1" />
<Label Style="{StaticResource LabelParagraphStyle}" Text="*Calculated coverage indicated is approximate, based on nominal tile size and is provided for purposes of estimation only." />
<Label Style="{StaticResource LabelParagraphStyle}" Text="Actual grout coverage will vary depending on jobsite conditions, actual tile size and installed grout joint size. Use of sufficient thin-set reduces grout requirement." />
<Label Style="{StaticResource LabelParagraphStyle}" Text="Add 10% to coverage estimates for waste, spillage and clean-up. Add 10%-15% to grout coverage estimate when grouting unglazed quarry tile." />
<Button
Text="{Binding ButtonShareText}"
Command="{Binding ShareCommand}"
Style="{StaticResource ButtonStyle}"
FontSize="Large"
HorizontalOptions="CenterAndExpand" />
</StackLayout>
</ScrollView>
<controls:Toolbar
ButtonCalculatorIcon="{Binding ButtonCalculatorIcon}"
ButtonDistributorLocatorIcon="{Binding ButtonDistributorLocatorIcon}"
ButtonScanQRCodeIcon="{Binding ButtonScanQRCodeIcon}"
ButtonVideoCallIcon="{Binding ButtonVideoCallIcon}"
ButtonMoreIcon="{Binding ButtonMoreIcon}"
CalculatorCommand="{Binding CalculatorCommand}"
DistributorsLocatorCommand="{Binding DistributorsLocatorCommand}"
ScanQRCommand="{Binding ScanQRCommand}"
VideoCallCommand="{Binding VideoCallCommand}"
MoreFeaturesCommand="{Binding MoreFeaturesCommand}"
ShowVideoChat="{Binding ShowVideoChat}"
ControlTemplate="{StaticResource ToolbarTemplate}"
Grid.Row="1" />
</Grid>
</ContentPage.Content>
On App Center Diagnostics, I could see exceptions like this:
I don't know if this causes our issue, but that is the only thing I could find.
Also, on the same iPhone models some users have this issue, and others don't. I personally could not reproduce it on my phones.
ADDED:
The code-behind constructor and the pagemodel constructor contain almost nothing:
public CoverageCalculatorResultsPage ()
{
InitializeComponent ();
}
...
public CoverageCalculatorResultsPageModel()
{
ScreenName = "Coverage Results";
ShareCommand = new Command(async () => await Share());
}
Pagemodel's Init():
public override void Init(object initData)
{
try
{
base.Init(initData);
throw new Exception("Test Exception: remove when done");
_previousModel = (CoverageCalculatorPageModel)initData;
// Standard units
string lengthUnits = "\"";
string coverageUnits = " Ft²";
double tileWidth = Convert.ToDouble(_previousModel.TileWidth, CultureInfo.InvariantCulture);
double tileHeight = Convert.ToDouble(_previousModel.TileHeight, CultureInfo.InvariantCulture);
double jointWidth = Convert.ToDouble(_previousModel.SelectedJointSize.Key, CultureInfo.InvariantCulture);
double tileThickness = Convert.ToDouble(_previousModel.SelectedTileThickness.Key, CultureInfo.InvariantCulture);
if (_previousModel.SelectedUnit.Code == UnitCode.Metric)
{
lengthUnits = "mm";
coverageUnits = "M²";
// Convert to standard units
tileWidth /= 25.4;
tileHeight /= 25.4;
}
// Perform adhesive calculation
// Official formula is: Coverage (Sq Ft)/Unit = (coverage_cuInPerUnit/((Trowel_Height * 0.707108)/2)) * 0.0069444
double cuInPerUnit = _previousModel.SelectedAdhesive.Coverage;
double trowelHeight = Convert.ToDouble(_previousModel.SelectedTrowelSize.Key, CultureInfo.InvariantCulture);
double adhesiveCoverage = (cuInPerUnit / ((trowelHeight * 0.559) / 2)) * 0.0069444;
// Perform grout calculation
// Official formula is: Coverage (Sq Ft)/Unit = coverage_cuInPerUnit/((Tile_Width+Tile_Height+Joint_Width)*Tile_Thickness*Joint_Width*(144/((Tile_Width+Joint_Width)*(Tile_Height+Joint_Width))))
cuInPerUnit = _previousModel.SelectedGrout.Coverage;
double groutCoverage = cuInPerUnit / ((tileWidth + tileHeight + jointWidth) * tileThickness * jointWidth * (144 / ((tileWidth + jointWidth) * (tileHeight + jointWidth))));
if (_previousModel.SelectedUnit.Code == UnitCode.Metric)
{
// Convert coverages to metric units if necessary (sqft to m2)
adhesiveCoverage *= 0.092903;
groutCoverage *= 0.092903;
}
TileSize = $"{_previousModel.TileWidth}{lengthUnits} x {_previousModel.TileHeight}{lengthUnits}";
TileThickness = _previousModel.SelectedTileThickness.Value;
JointSize = _previousModel.SelectedJointSize.Value;
TrowelSize = _previousModel.SelectedTrowelSize.Value;
Adhesive = _previousModel.SelectedAdhesive.Name;
AdhesiveCoverage = $"Adhesive Coverage per Unit*: {adhesiveCoverage: #.##}{coverageUnits}";
Grout = _previousModel.SelectedGrout.Name;
GroutCoverage = $"Grout Coverage per Unit*: {groutCoverage: #.##}{coverageUnits}";
}
catch(Exception ex)
{
HandleException(ex, true);
}
}
All the data is obtained from the user on the previous page or from the calculations shown above.
Related
How do I make the CollectionView render all items with the same height while using a GridItemsLayout on iOS? It already works on Android that all elements in a row gets the same height but on iOS they are squished vertically in the center of the row.
This is how it currently looks on iOS (how it not should be..):
And this is how it currently looks on Android (how it also should be on iOS - all elements are scaled to the same height in the same row):
As of now, this is how my XAML looks like:
<CollectionView Grid.Row="1" x:Name="DataListView" ItemSizingStrategy="MeasureAllItems" ItemsSource="{Binding Data}" IsGrouped="False" ItemTemplate="{StaticResource EntryTemplate}"
Header="{Binding Header}" Footer="{Binding Footer}" BackgroundColor="{DynamicResource DarkBackgroundColor}">
<CollectionView.ItemsLayout>
<!-- Span is set in OnSizeAllocated to make this "dynamic" -->
<GridItemsLayout x:Name="GridItemsLayout" Orientation="Vertical" Span="1" />
</CollectionView.ItemsLayout>
<CollectionView.HeaderTemplate>
<DataTemplate x:DataType="x:String">
<Grid Padding="10,20">
<Label Text="{Binding}" TextColor="{DynamicResource TextColor}" FontAttributes="Bold" HorizontalTextAlignment="Center" LineBreakMode="WordWrap" />
</Grid>
</DataTemplate>
</CollectionView.HeaderTemplate>
<CollectionView.FooterTemplate>
<DataTemplate x:DataType="x:String">
<Grid Padding="{OnPlatform iOS=20 30 20 20, Default=20}">
<Label Text="{Binding}" TextColor="{DynamicResource LightTextColor}" FontSize="Micro" HorizontalTextAlignment="Center" LineBreakMode="WordWrap" />
</Grid>
</DataTemplate>
</CollectionView.FooterTemplate>
</CollectionView>
This is my entry data template XAML:
<DataTemplate x:Key="EntryTemplate" x:DataType="data:Entry">
<Grid BackgroundColor="Red" VerticalOptions="FillAndExpand" Margin="0">
<Frame WidthRequest="{Binding Converter={StaticResource FlowColumnWidthConverter}, ConverterParameter=324}" BackgroundColor="{DynamicResource DarkerBackgroundColor}"
BorderColor="{Binding IsActive, Converter={StaticResource ActiveToColorConverter}}" HorizontalOptions="Center" Margin="8" Padding="10" CornerRadius="5">
<Frame.GestureRecognizers>
<TapGestureRecognizer Tapped="OnChecklistElementTapped" />
</Frame.GestureRecognizers>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="40" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Image Grid.Row="0" Grid.Column="0" Grid.RowSpan="4" Source="{Binding Type, Converter={StaticResource TypeToEmbeddedImageConverter}}" WidthRequest="32" HeightRequest="32" Margin="4" />
<Label Grid.Row="0" Grid.Column="1" Text="{Binding Name}" FontAttributes="Bold" LineBreakMode="TailTruncation" VerticalOptions="Start" />
<Label Grid.Row="1" Grid.Column="1" Text="{Binding Region}" FontSize="12" LineBreakMode="TailTruncation" VerticalOptions="Start" Margin="0,-6,0,0" />
<Label Grid.Row="2" Grid.Column="1" Text="{Binding AdditionalData}" TextType="Html" FontSize="12" LineBreakMode="WordWrap" Margin="0"
IsVisible="{Binding AdditionalData, Converter={StaticResource EmptyToVisibilityConverter}}" InputTransparent="True" />
<Label Grid.Row="3" Grid.Column="1" Text="{Binding Description}" FontSize="12" LineBreakMode="WordWrap"
IsVisible="{Binding Description, Converter={StaticResource EmptyToVisibilityConverter}}" />
<Switch Grid.Row="0" Grid.Column="2" Grid.RowSpan="2" IsToggled="{Binding IsDone}" HorizontalOptions="Center" VerticalOptions="Center" />
<StackLayout Grid.Row="2" Grid.Column="2" Grid.RowSpan="2" Orientation="Horizontal" Spacing="12" HorizontalOptions="Center" VerticalOptions="Start" Margin="0,4,0,4">
<controls:TintedImageButton Source="Details.png" Clicked="OnNavigateToDetailsButtonClicked" HeightRequest="32" WidthRequest="32"
IsVisible="{Binding Details, Converter={StaticResource EmptyToVisibilityConverter}}"
HorizontalOptions="Center" BackgroundColor="Transparent" />
<controls:TintedImageButton Source="Map.png" Clicked="OnNavigateToMapButtonClicked" HeightRequest="32" WidthRequest="32"
HorizontalOptions="Center" BackgroundColor="Transparent" IsVisible="{x:Static resources:Metadata.MAP_ENABLED}" />
</StackLayout>
</Grid>
</Frame>
</Grid>
</DataTemplate>
And this is my code behind (where I set how many columns there should be):
protected override void OnSizeAllocated(double width, double height)
{
base.OnSizeAllocated(width, height);
GridItemsLayout.Span = Convert.ToInt32(Math.Floor(width / 340d));
}
Anyone has an idea, why this is looks different on iOS in the first place and what I should look up to fix this problem?
I have a fairly simple Xamarin forms layout.
The layout works as expected as long as I hold the iPhone, but when placed on a flat surface and I then navigate to a specific page the page, gets an incorrectly layout.
The offending page:
<?xml version="1.0" encoding="UTF-8"?>
<pages:BaseContentPage xmlns:pages="clr-namespace:Mainapp.Pages" xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:customcontrols="clr-namespace:Mainapp.CustomControls" xmlns:forum="clr-namespace:Mainapp.Pages.Forum" x:Class="Mainapp.Pages.Forum.ForumTopicDetailPage">
<pages:BaseContentPage.MainContent>
<AbsoluteLayout HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand" BackgroundColor="Red" >
<StackLayout AbsoluteLayout.LayoutBounds="0,0,1,1" AbsoluteLayout.LayoutFlags="All" BackgroundColor="Blue">
<Grid Padding="65,5,5,5" VerticalOptions="StartAndExpand" HorizontalOptions="FillAndExpand" x:Name="ContentGrid" BackgroundColor="Green">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="60"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="auto"/>
<RowDefinition Height="auto"/>
</Grid.RowDefinitions>
<Label x:Name="LblTopTitle" Style="{StaticResource TitleStyle}" Grid.Row="0" Grid.Column="0" FontAttributes="Bold" FontSize="14" LineBreakMode="WordWrap" HorizontalTextAlignment="Center" MaxLines="2" />
<Label x:Name="LblAuthor" Style="{StaticResource SubTitleStyle}" Grid.Row="1" Grid.Column="0" FontAttributes="None" FontSize="12"/>
<customcontrols:UserInitialsView VerticalOptions="Center" HorizontalOptions="Center" Grid.RowSpan="2" Grid.Column="1" x:Name="UserIntials"></customcontrols:UserInitialsView>
</Grid>
<ListView x:Name="TableListView"
ItemsSource="{Binding Comments}"
HasUnevenRows="true"
VerticalOptions="FillAndExpand"
HorizontalOptions="FillAndExpand"
IsVisible="true"
SeparatorColor="Transparent"
SelectionMode="None"
BackgroundColor="{StaticResource BlueBackground}">
<ListView.ItemTemplate>
<DataTemplate>
<forum:TopicCommentCell CommentContent="{Binding}" UserChangedLikeStateForComment="UserChangedLikedStateForContent" />
</DataTemplate>
</ListView.ItemTemplate>
<ListView.Header>
<Grid Padding="0,0,0,10" BackgroundColor="Purple">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="36" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="10" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Label BackgroundColor="{StaticResource Light_Ivory}" Text=""/>
<Frame HasShadow="False" CornerRadius="36" BackgroundColor="{StaticResource Light_Ivory}" Grid.RowSpan="5" />
<Label Padding="10,0,10,0" x:Name="LblListTitle" MaxLines="2" VerticalOptions="Start" Style="{StaticResource Topic_HeadlineStyle}" Grid.Row="1" Text="TITLE" />
<Label Padding="10,0,10,0" x:Name="LblListText" VerticalOptions="Start" Style="{StaticResource Topic_TextStyle}" LineHeight="1.5" Grid.Row="2" Text="TEXT TEXT TEXT"/>
<customcontrols:LikeView Padding="10,20,0,0" x:Name="LikeView" Grid.Row="3" UserChangedLikedState="UserChangedLikedStateForContent"></customcontrols:LikeView>
<Label Padding="10,0,10,0" x:Name="LblCommentHeader" HorizontalTextAlignment="Start" MaxLines="1" VerticalOptions="Start" Style="{StaticResource TitleStyle}" Grid.Row="5" Text="Kommentarer" />
</Grid>
</ListView.Header>
<ListView.Footer>
<Label BackgroundColor="Transparent" HeightRequest="100"/>
</ListView.Footer>
</ListView>
</StackLayout>
<customcontrols:FloatingTextEditor DidFinishWithDone="FloatingTextEditor_DidFinishWithDone" KeyboardDidShow="FloatingTextEditor_KeyboardDidShow" AbsoluteLayout.LayoutFlags="PositionProportional" AbsoluteLayout.LayoutBounds="0,1"></customcontrols:FloatingTextEditor>
</AbsoluteLayout>
</pages:BaseContentPage.MainContent>
</pages:BaseContentPage>
I'm not using the gyroscope and the app is set to Portrait mode only.
Any idea what I'm doing wrong?
Expected layout, and what happens when i hold the Phone:
Layout when phone is placed on a level plane:
I ended up replacing the absolute layout with a grid layout. It works, but I'm still not sure why the absolute layout didn't work
I have my code below, basically to print out an image.
private async void imageControl_PrintButtonClick(object sender, RoutedEventArgs e)
{
var createBitmapTask = Task.Run(async () =>
{
var stream = await provider.OpenEntryAsRandomAccessStreamAsync(currentImageFile);
var decoder = await BitmapDecoder.CreateAsync(stream);
return await decoder.GetSoftwareBitmapAsync(
BitmapPixelFormat.Bgra8,
BitmapAlphaMode.Premultiplied);
});
var printHelper = new PrintHelper(printPanel);
printHelper.OnPreviewPagesCreated += PrintHelper_OnPreviewPagesCreated;
printPanel.Opacity = 1.0;
var source = new SoftwareBitmapSource();
var bitmap = await createBitmapTask;
await source.SetBitmapAsync(bitmap);
printImage.Source = source;
printFileName.Text = "Hello";
printImage.Height = bitmap.PixelHeight;
printImage.Width = bitmap.PixelWidth;
await printHelper.ShowPrintUIAsync("ZipPicView - " + currentImageFile.ExtractFilename(), true);
printPanel.Opacity = 0;
}
private void PrintHelper_OnPreviewPagesCreated(List<FrameworkElement> obj)
{
ContentDialog dialog = new ContentDialog();
}
However, the print preview shows an empty page. When I print it out, the printer does nothing.
I've tried changing the opacity of the printPanel (which is a Grid object) to non-zero, and the image does display on the screen. Still, the print has nothing on the output page.
I did notice that, on the OnPreviewPagesCreated, its obj parameter has a new object everytime it's called. The first call has one object with both Width and Height as NaN. My guess is because the container has no size, it cannot determine the content size.
Below is the XAML file.
<Page x:Name="page"
x:Class="ZipPicViewUWP.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:ZipPicViewUWP"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:interactivity="using:Microsoft.Xaml.Interactivity"
xmlns:behaviors="using:Microsoft.Toolkit.Uwp.UI.Animations.Behaviors"
mc:Ignorable="d" KeyUp="page_KeyUp" Loaded="page_Loaded" SizeChanged="page_SizeChanged">
<Canvas x:Name="canvas" SizeChanged="canvas_SizeChanged">
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}" Height="1019" Width="1920">
<Grid x:Name="printPanel" Opacity="0">
<StackPanel x:Name="printContent"
Margin="0,0"
Orientation="Vertical">
<TextBlock x:Name="printFileName" />
<Image x:Name="printImage"
Stretch="Fill" />
</StackPanel>
</Grid>
<SplitView x:Name="splitView" DisplayMode="CompactOverlay" PanePlacement="Left" CompactPaneLength="50" OpenPaneLength="300">
<SplitView.Content>
<GridView x:Name="thumbnailGrid" />
</SplitView.Content>
<SplitView.Pane>
<Grid Background="{StaticResource SidebarBackground}">
<Grid.RowDefinitions>
<RowDefinition Height="50" />
<RowDefinition Height="*" />
<RowDefinition Height="50" />
</Grid.RowDefinitions>
<StackPanel Orientation="Horizontal" Grid.Row="0">
<Button x:Name="subFolderButton" Width="50" Height="50" Background="Transparent" Click="subFolderButton_Click">
<SymbolIcon Symbol="List" />
</Button>
<TextBlock VerticalAlignment="Center" Margin="0,15">Folders</TextBlock>
</StackPanel>
<ScrollViewer Grid.Row="1">
<ListView x:Name="subFolderListCtrl" SelectionChanged="subFolderList_SelectionChanged" DataFetchSize="2" Margin="-10,0,0,0" />
</ScrollViewer>
<ProgressRing x:Name="thumbProgress" Height="30" Width="30" HorizontalAlignment="Left" VerticalAlignment="Center" Grid.Row="2" Margin="10,0,0,10" />
</Grid>
</SplitView.Pane>
</SplitView>
<interactivity:Interaction.Behaviors>
<behaviors:Blur x:Name="BlurBehavior" Duration="500" AutomaticallyStart="False" />
</interactivity:Interaction.Behaviors>
</Grid>
<Border x:Name="imageBorder" Visibility="Collapsed">
<Image x:Name="image" ManipulationMode="TranslateX" ManipulationCompleted="image_ManipulationCompleted" Tapped="image_Tapped">
<interactivity:Interaction.Behaviors>
<behaviors:Blur x:Name="ImageTransitionBehavior" Duration="500" AutomaticallyStart="False" />
</interactivity:Interaction.Behaviors>
</Image>
</Border>
<local:ViewerControl x:Name="imageControl" Visibility="Collapsed" CloseButtonClick="imageControl_CloseButtonClick" NextButtonClick="imageControl_NextButtonClick" PrevButtonClick="imageControl_PrevButtonClick" SaveButtonClick="imageControl_SaveButtonClick" PrintButtonClick="imageControl_PrintButtonClick" />
<Border x:Name="loadingBorder" Visibility="Collapsed">
<ProgressRing IsActive="True" Width="100" Height="100" />
</Border>
</Canvas>
<Page.TopAppBar>
<CommandBar VerticalContentAlignment="Center" VerticalAlignment="Center">
<CommandBar.Content>
<TextBlock Margin="10,0,0,0" x:Name="filenameTextBlock" Text="<None>" UseLayoutRounding="True" />
</CommandBar.Content>
<AppBarToggleButton x:Name="fullscreenButton" Icon="FullScreen" Label="FullScreen" Checked="fullscreenButton_Checked" Unchecked="fullscreenButton_Unchecked" />
<AppBarSeparator />
<AppBarButton x:Name="openFileButton" Icon="OpenFile" Label="Open File" Click="openFileButton_Click" />
<AppBarButton x:Name="openFolderButton" Icon="Folder" Label="Open Folder" Click="openFolderButton_Click" />
</CommandBar>
</Page.TopAppBar>
</Page>
And the source code is here : https://github.com/wutipong/ZipPicViewCS/tree/master/ZipPicViewUWP
I'm trying to make a ListView with clickable rows. When you click on a row it sets a child stacklayout to visibility true. This works fine in android, but not in ios. Maybe I'm doing it the wrong way. I am still a beginner, any idea how to fix this or any other better approach?
The problem is that it opens on ios so the visibility changes, but it does not update the height of the cell. The cell updates off-screen for example if you scroll up until you can't see the opened cell anymore and then scroll back down. You'll see it has updated the height.
I tried using a custom renderer, but i have no idea where to start.
This is my xaml:
<?xml version="1.0" encoding="UTF-8"?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="Lisa.Excelsis.Mobile.AssessmentPage" xmlns:local="clr-namespace:Lisa.Excelsis.Mobile;assembly=Lisa.Excelsis.Mobile">
<StackLayout>
<local:SpecialListView x:Name="CategoryList"
ItemsSource = "{Binding Categories}"
HasUnevenRows="true"
RowHeight="-1"
GroupDisplayBinding="{Binding Name}"
IsGroupingEnabled="true">
<local:SpecialListView.ItemTemplate>
<DataTemplate>
<ViewCell x:Name="ObservationCell">
<ViewCell.View>
<StackLayout x:Name="ObservationContainer"
HorizontalOptions="FillAndExpand"
Orientation="Vertical"
VerticalOptions="StartAndExpand"
BackgroundColor="White">
<StackLayout x:Name="Observation"
HorizontalOptions="FillAndExpand"
VerticalOptions="StartAndExpand"
Padding="15, 10, 10, 10"
BackgroundColor="White">
<StackLayout.GestureRecognizers>
<TapGestureRecognizer Tapped="OpenItem"/>
</StackLayout.GestureRecognizers>
<Grid HorizontalOptions="FillAndExpand" >
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="35" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Label x:Name="ObservationOrder"
Text="{Binding Criterion.Order, StringFormat='{0}.'}"
FontSize="18"
VerticalOptions="StartAndExpand"
Grid.Column="0" Grid.Row="0"/>
<Label x:Name="ObservationTitle"
Text="{Binding Criterion.Title}"
FontSize="18"
VerticalOptions="StartAndExpand"
Grid.Column="1" Grid.Row="0"/>
</Grid>
</StackLayout>
<StackLayout x:Name="ObservationButtons"
HorizontalOptions="FillAndExpand"
VerticalOptions="StartAndExpand"
BackgroundColor="White"
IsVisible="false"
Padding="0, 0, 0, 20"
ClassId = "{Binding Id, StringFormat='ObservationButtons_{0}'}">
<Grid HorizontalOptions="Center"
Grid.Column="0" Grid.Row="1" Grid.ColumnSpan="2">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="80" />
<ColumnDefinition Width="80" />
<ColumnDefinition Width="10" />
<ColumnDefinition Width="80" />
<ColumnDefinition Width="80" />
<ColumnDefinition Width="80" />
<ColumnDefinition Width="80" />
</Grid.ColumnDefinitions>
<StackLayout Grid.Column="0" Grid.Row="0" >
<Image Source="yesnobutton0.png"
HeightRequest="60" WidthRequest="60"
HorizontalOptions="Center"
VerticalOptions="Start"
x:Name="yesImage">
<Image.GestureRecognizers>
<TapGestureRecognizer Tapped="SetYesImage"/>
</Image.GestureRecognizers>
</Image>
<Label Text="Ja" VerticalOptions="End" HorizontalOptions="Center"/>
</StackLayout>
<StackLayout Grid.Column="1" Grid.Row="0">
<Image Source="yesnobutton0.png"
HeightRequest="60" WidthRequest="60"
HorizontalOptions="Center"
VerticalOptions="Start"
x:Name="noImage">
<Image.GestureRecognizers>
<TapGestureRecognizer Tapped="SetNoImage"/>
</Image.GestureRecognizers>
</Image>
<Label Text="Nee" VerticalOptions="End" HorizontalOptions="Center"/>
</StackLayout>
<Image Source="maybenot.png"
HeightRequest="60" WidthRequest="60"
HorizontalOptions="Center"
VerticalOptions="Start"
Grid.Column="3" Grid.Row="0">
<Image.GestureRecognizers>
<TapGestureRecognizer Tapped="SetMark"/>
</Image.GestureRecognizers>
</Image>
<Image Source="skip.png"
HeightRequest="60" WidthRequest="60"
HorizontalOptions="Center"
VerticalOptions="Start"
Grid.Column="4" Grid.Row="0">
<Image.GestureRecognizers>
<TapGestureRecognizer Tapped="SetMark"/>
</Image.GestureRecognizers>
</Image>
<Image Source="unclear.png"
HeightRequest="60" WidthRequest="60"
HorizontalOptions="Center"
VerticalOptions="Start"
Grid.Column="5" Grid.Row="0">
<Image.GestureRecognizers>
<TapGestureRecognizer Tapped="SetMark"/>
</Image.GestureRecognizers>
</Image>
<Image Source="change.png"
HeightRequest="60" WidthRequest="60"
HorizontalOptions="Center"
VerticalOptions="Start"
Grid.Column="6" Grid.Row="0">
<Image.GestureRecognizers>
<TapGestureRecognizer Tapped="SetMark"/>
</Image.GestureRecognizers>
</Image>
</Grid>
</StackLayout>
</StackLayout>
</ViewCell.View>
</ViewCell>
</DataTemplate>
</local:SpecialListView.ItemTemplate>
</local:SpecialListView>
</StackLayout>
This is an example of how it works on android and how i want it to work on ios.
I reproduced your Problem in a small Testproject. I prefer doing layout changes via data binding, instead of code behind.
Let's start with the Template:
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="App6.Page1">
<ListView x:Name="CategoryList"
BackgroundColor="Gray"
ItemsSource="{Binding Categories}"
SelectedItem="{Binding SelectedItem}"
HasUnevenRows="true"
RowHeight="-1">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell x:Name="ObservationCell">
<ViewCell.View>
<StackLayout x:Name="Observation"
HorizontalOptions="FillAndExpand"
VerticalOptions="StartAndExpand"
Padding="15, 10, 10, 10"
BackgroundColor="White">
<Label x:Name="ObservationTitle"
Text="{Binding Title}"
FontSize="18"
TextColor="Black"
VerticalOptions="StartAndExpand"/>
<StackLayout Orientation="Horizontal" IsVisible="{Binding IsSelected}">
<Image BackgroundColor="Fuchsia" WidthRequest="40" HeightRequest="40"></Image>
<Image BackgroundColor="Green" WidthRequest="40" HeightRequest="40"></Image>
<Image BackgroundColor="Yellow" WidthRequest="40" HeightRequest="40"></Image>
<Image BackgroundColor="Blue" WidthRequest="40" HeightRequest="40"></Image>
<Image BackgroundColor="Black" WidthRequest="40" HeightRequest="40"></Image>
</StackLayout>
</StackLayout>
</ViewCell.View>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</ContentPage>
The Datatemplate is basicaly the same, but:
the StackLayout has no Click-Listener
the Visbility of the StackLayout is bound to IsSelected (IsVisible="{Binding IsSelected}")
the SelectedItem of the ListView is bound to SelectedItem of our ViewModel
Our Page just sets the ViewModel as DataContext
public partial class Page1 : ContentPage
{
public Page1()
{
InitializeComponent();
BindingContext = new Page1ViewModel();
}
}
The ViewModel
implements INotifyPropertyChanged to notify the view about data changes
adds some dummy items to our Categories Collection
has a SelectedItem property that updates the IsSelected property of the Categories
class Page1ViewModel : INotifyPropertyChanged
{
private Category _selectedItem;
private ObservableCollection<Category> _categories = new ObservableCollection<Category>();
public event PropertyChangedEventHandler PropertyChanged;
public ObservableCollection<Category> Categories
{
get { return _categories; }
set
{
_categories = value;
OnPropertyChanged();
}
}
public Category SelectedItem
{
get { return _selectedItem; }
set
{
if (_selectedItem == value)
return;
if (_selectedItem != null)
{
_selectedItem.IsSelected = false;
}
_selectedItem = value;
if (_selectedItem != null)
{
_selectedItem.IsSelected = true;
}
}
}
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public Page1ViewModel()
{
Categories.Add(new Category());
Categories.Add(new Category());
Categories.Add(new Category());
Categories.Add(new Category());
Categories.Add(new Category());
}
}
Last, but not least, but most important, you need a tiny custom renderer that overwrites the default one. We call ReloadData() if the SelectedItem has changed.
[assembly: ExportRenderer(typeof(ListView), typeof(MyListViewRenderer))]
namespace App6.iOS.CustomRenderer
{
public class MyListViewRenderer : ListViewRenderer
{
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(sender, e);
if (e.PropertyName == ListView.SelectedItemProperty.PropertyName)
{
Device.BeginInvokeOnMainThread(() => Control.ReloadData());
}
}
}
}
Result
I've been trying to get this looking right in Xaml (x.forms) but can't make it work. Any possibly way this can be solved in Xaml without a custom cell renderer?
Here is how it's supposed to look on native iOS:
What I'm getting so far in Xaml:
Here's my Xaml:
<ScrollView>
<StackLayout>
<ActivityIndicator IsRunning="{Binding IsFetching}" />
<ListView ItemsSource="{Binding Events}" Header="2015" Footer="">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell Height="55">
<ViewCell.View>
<StackLayout Orientation="Horizontal" Padding="5" BackgroundColor="White">
<StackLayout>
<BoxView WidthRequest="44"
HeightRequest="5"
BackgroundColor="Purple"
/>
<Label Text="AUG" FontSize="12" HeightRequest="13" HorizontalOptions="Center" VerticalOptions="Start" FontAttributes="Bold"/>
<Label Text="31" FontSize="13" VerticalOptions="StartAndExpand" HorizontalOptions="Center" />
</StackLayout>
<StackLayout Orientation="Vertical"
HorizontalOptions="StartAndExpand">
<Label Text="Relay For Life of" FontSize="14" VerticalOptions="End" TextColor="Gray"/>
<Label Text="Hope City" FontSize="16" FontAttributes="Bold" VerticalOptions="StartAndExpand"/>
</StackLayout>
</StackLayout>
</ViewCell.View>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
</ScrollView>
You can try this with a Grid instead of Stack, also add RowHeight to your ListView
<ListView ... RowHeight="55">
...
<ViewCell Height="55">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="44"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="44"/> <!-- for the checkmark -->
</Grid.ColumnDefinitions>
<Grid Grid.Column="0">
<Grid.RowDefinitions>
<RowDefinition Height="5" />
<RowDefinition Height="22" />
<RowDefinition Height="17" />
</Grid.RowDefinitions>
<BoxView Grid.Row="0" WidthRequest="44" HeightRequest="5" BackgroundColor="Purple"/>
<!-- experiment with the vertical alignment to get it right -->
<Label Grid.Row="1" Text="AUG" .../>
<Label Grid.Row="2" Text="31" .../>
</Grid>
<Grid Grid.Column="0">
<Grid.RowDefinitions>
<RowDefinition Height="22" />
<RowDefinition Height="22" />
</Grid.RowDefinitions>
<!-- if the vertical alignment doesn't work well add two more rows for top and bottom padding -->
<Label Grid.Row="0" Text="Relay for life" VerticalOptions="End" .../>
<Label Grid.Row="1" Text="Hope city" VerticalOptions="Start" .../>
</Grid>
</Grid>
</ViewCell>
Set HasUnevenRows="True" on the ListView