Xamarin.CommunityToolkit.MauiCompat CameraView produces Android No view found for fragment CameraFragment error - xamarin.android

Thanks in advance!! I'm quite new to XAML so be kind :)
My requirement is to add a CameraView to a .net Maui app which runs on Android and iOS.
I understand that there is no CameraView in the Maui.Community.Toolkit but I was under the impression, which may be wrong, that I could run the Xamarin.Community.Tookit.MauiCompat packages which I'm trying to do in the code below.
using Microsoft.AspNetCore.Components.WebView.Maui;
using Xamarin.CommunityToolkit.MauiCompat;
using Microsoft.Maui.Controls.Compatibility.Hosting;
etc...
namespace Mobile;
public static class MauiProgram
{
public static MauiApp CreateMauiApp()
{
var builder = MauiApp.CreateBuilder();
builder
.UseMauiApp<App>()
.ConfigureFonts(fonts =>
{
fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
});
builder.Services.AddScoped(sp => new HttpClient
{
Timeout = TimeSpan.FromMinutes(10)
});
builder.Services.AddSingleton<MainPage>();
builder.Services.AddBlazorWebViewDeveloperTools();
builder.Services.AddMauiBlazorWebView();
builder.Services.AddScoped<IAuthenticationService, AuthenticationService>();
builder.Services.AddScoped<AuthenticationStateProvider, AuthStateProvider>();
builder.Services.AddAuthorizationCore();
builder.UseMauiCompatibility();
builder.ConfigureMauiHandlers(handlers =>
{
// Register ALL handlers in the Xamarin Community Toolkit assembly
handlers.AddCompatibilityRenderers(typeof(Xamarin.CommunityToolkit.UI.Views.CameraViewRenderer).Assembly);
// Register just one handler for the control you need
handlers.AddCompatibilityRenderer(typeof(Xamarin.CommunityToolkit.UI.Views.CameraView), typeof(Xamarin.CommunityToolkit.UI.Views.CameraViewRenderer));
});
return builder.Build();
}
}
MainPage.xaml (Which I simply pasted from https://github.com/xamarin/XamarinCommunityToolkit/blob/main/samples/XCT.Sample/Pages/Views/CameraViewPage.xaml.cs)
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:b="clr-namespace:Microsoft.AspNetCore.Components.WebView.Maui;assembly=Microsoft.AspNetCore.Components.WebView.Maui"
xmlns:local="clr-namespace:Mobile"
xmlns:data="clr-namespace:Mobile.Data"
xmlns:zxing="clr-namespace:ZXing.Net.Maui.Controls;assembly=ZXing.Net.MAUI"
xmlns:xct="http://xamarin.com/schemas/2020/toolkit"
x:Class="Mobile.MainPage"
BackgroundColor="{DynamicResource PageBackgroundColor}">
<ContentPage.Resources>
<data:ShowQrReaderConverter x:Key="ShowQrReaderConverter"/>
<data:ShowFrCapturingConverter x:Key="ShowFrCapturingConverter"/>
</ContentPage.Resources>
<AbsoluteLayout>
<b:BlazorWebView HostPage="wwwroot/index.html"
AbsoluteLayout.LayoutBounds="0, 0, 1, 1"
AbsoluteLayout.LayoutFlags="All">
<b:BlazorWebView.RootComponents>
<b:RootComponent Selector="#app" ComponentType="{x:Type local:Main}" />
</b:BlazorWebView.RootComponents>
</b:BlazorWebView>
<!-- QR Reader -->
Removed for brevity...
<!-- FR Camera View -->
<AbsoluteLayout
x:Name="absLayout2"
Margin="0,334"
BackgroundColor="Beige"
HeightRequest="{Binding availableQrReaderHeight}"
WidthRequest="{Binding availableQrReaderWidth}"
>
<AbsoluteLayout.TranslationX>
<MultiBinding Converter="{StaticResource ShowFrCapturingConverter}">
<Binding Path="CardToggle" />
<Binding Path="cameraViewerCapturing" />
<Binding Path="AppState.ShootMode" />
</MultiBinding>
</AbsoluteLayout.TranslationX>
<Grid RowDefinitions="300, Auto, *">
<Grid ColumnDefinitions="*, *" Grid.Row="0">
<xct:CameraView
Grid.Column="0"
x:Name="cameraView"
CaptureMode="Photo"
FlashMode="Off"
HorizontalOptions="FillAndExpand"
MediaCaptured="CameraView_MediaCaptured"
OnAvailable="CameraView_OnAvailable"
VerticalOptions="FillAndExpand" />
<Label
Grid.Column="0"
Text="Camera"
HorizontalTextAlignment="Center"
HorizontalOptions="FillAndExpand"
VerticalOptions="End" />
<Image
Grid.Column="1"
x:Name="previewPicture"
Aspect="AspectFit"
BackgroundColor="LightGray" />
<xct:MediaElement
Grid.Column="1"
x:Name="previewVideo"
Aspect="AspectFit"
BackgroundColor="LightGray"
IsVisible="false"/>
<Label
Grid.Column="1"
Text="Result"
HorizontalTextAlignment="Center"
HorizontalOptions="FillAndExpand"
VerticalOptions="End" />
</Grid>
<StackLayout Grid.Row="1" Orientation="Horizontal">
<Label x:Name="zoomLabel" />
<Slider
x:Name="zoomSlider"
Margin="5,0"
IsEnabled="False"
Maximum="10"
Minimum="1"
HorizontalOptions="FillAndExpand"
ValueChanged="ZoomSlider_ValueChanged"
Value="1" />
</StackLayout>
<StackLayout Grid.Row="2">
<Grid ColumnDefinitions="*, *" RowDefinitions="*,*">
<StackLayout
Grid.Row="0"
Grid.Column="0"
Margin="5"
Orientation="Horizontal">
<Switch
Margin="0,0,5,0"
IsToggled="False"
Toggled="VideoSwitch_Toggled" />
<Label Text="Video" />
</StackLayout>
<StackLayout
Grid.Row="1"
Grid.Column="0"
Margin="5"
Orientation="Horizontal">
<Switch
Margin="0,0,5,0"
IsToggled="False"
Toggled="FrontCameraSwitch_Toggled" />
<Label Text="Front camera" />
</StackLayout>
<StackLayout
Grid.Row="0"
Grid.Column="1"
Margin="5"
Orientation="Horizontal">
<Switch
Margin="0,0,5,0"
IsToggled="False"
Toggled="FlashSwitch_Toggled" />
<Label Text="Flash" />
</StackLayout>
</Grid>
<Button
x:Name="doCameraThings"
Clicked="DoCameraThings_Clicked"
IsEnabled="False"
Text="Snap picture" />
</StackLayout>
</Grid>
</AbsoluteLayout>
</AbsoluteLayout>
MainPage.xaml.cs
...
using Xamarin.CommunityToolkit.UI.Views;
namespace Mobile;
public partial class MainPage : ContentPage
{
LocalDb _db;
AppStateService _AppStateService;
CommonMethods _CommonMethods;
public MainPage(AppStateService AppStateService, LocalDb db, CommonMethods CommonMethods)
{
InitializeComponent();
this.BindingContext = AppStateService;
_AppStateService = AppStateService;
_db = db;
_CommonMethods = CommonMethods;
zoomLabel.Text = string.Format("Zoom: {0}", zoomSlider.Value);
}
//Begin CameraView methods
void ZoomSlider_ValueChanged(object? sender, ValueChangedEventArgs e)
{
cameraView.Zoom = (float)zoomSlider.Value;
zoomLabel.Text = string.Format("Zoom: {0}", Math.Round(zoomSlider.Value));
}
void VideoSwitch_Toggled(object? sender, ToggledEventArgs e)
{
var captureVideo = e.Value;
if (captureVideo)
cameraView.CaptureMode = CameraCaptureMode.Video;
else
cameraView.CaptureMode = CameraCaptureMode.Photo;
previewPicture.IsVisible = !captureVideo;
doCameraThings.Text = e.Value ? "Start Recording"
: "Snap Picture";
}
// You can also set it to Default and External
void FrontCameraSwitch_Toggled(object? sender, ToggledEventArgs e)
=> cameraView.CameraOptions = e.Value ? CameraOptions.Front : CameraOptions.Back;
// You can also set it to Torch (always on) and Auto
void FlashSwitch_Toggled(object? sender, ToggledEventArgs e)
=> cameraView.FlashMode = e.Value ? CameraFlashMode.On : CameraFlashMode.Off;
void DoCameraThings_Clicked(object? sender, EventArgs e)
{
cameraView.Shutter();
doCameraThings.Text = cameraView.CaptureMode == CameraCaptureMode.Video
? "Stop Recording"
: "Snap Picture";
}
void CameraView_OnAvailable(object? sender, bool e)
{
if (e)
{
zoomSlider.Value = cameraView.Zoom;
var max = cameraView.MaxZoom;
if (max > zoomSlider.Minimum && max > zoomSlider.Value)
zoomSlider.Maximum = max;
else
zoomSlider.Maximum = zoomSlider.Minimum + 1; // if max == min throws exception
}
doCameraThings.IsEnabled = e;
zoomSlider.IsEnabled = e;
}
void CameraView_MediaCaptured(object? sender, MediaCapturedEventArgs e)
{
switch (cameraView.CaptureMode)
{
default:
case CameraCaptureMode.Default:
case CameraCaptureMode.Photo:
previewVideo.IsVisible = false;
previewPicture.IsVisible = true;
previewPicture.Rotation = e.Rotation;
previewPicture.Source = e.Image;
doCameraThings.Text = "Snap Picture";
break;
case CameraCaptureMode.Video:
previewPicture.IsVisible = false;
previewVideo.IsVisible = true;
previewVideo.Source = e.Video;
doCameraThings.Text = "Start Recording";
break;
}
}
}
This produces the error:
No view found for id 0x1 (unknown) for fragment CameraFragment{f98267d} (57bc3319-7a90-475b-a4d0-458320859acb id=0x1 tag=camera)

Related

Command not firing on button click on iPhone devices, but is firing on iOS emulator and Android devices

When I test my Xamarin Form app on Android devices and iOS emulators, all the commands on button clicks work perfectly fine.
However, when I test the app on iPhone 12 pro (debugging on iPhone and downloading app from TestFlight), commands are not firing on button clicks.
I have done hours of searching but still can't find out what's wrong with my code. Thanks in advance!
HomeAnonymousPage.Xml:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="MyMobile.Views.HomeAnonymousPage"
xmlns:vm="clr-namespace:MyMobile.ViewModels"
Title="{Binding Title}"
Shell.NavBarIsVisible="False">
<ContentPage.BindingContext>
<vm:HomeAnonymousViewModel />
</ContentPage.BindingContext>
<ContentPage.Resources>
<ResourceDictionary>
<Color x:Key="Accent">#96d1ff</Color>
<Color x:Key="VivaOrange">#F06036</Color>
</ResourceDictionary>
</ContentPage.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<StackLayout BackgroundColor="{StaticResource Accent}" VerticalOptions="FillAndExpand" HorizontalOptions="Fill">
<StackLayout Orientation="Horizontal" HorizontalOptions="Center" VerticalOptions="Center">
<ContentView Padding="0,40,0,40" VerticalOptions="FillAndExpand">
<Image Source="my_logo.png" VerticalOptions="Center" HeightRequest="64" />
</ContentView>
</StackLayout>
</StackLayout>
<ScrollView Grid.Row="1">
<StackLayout Orientation="Vertical" Padding="30,24,30,24" Spacing="10">
<Button x:Name="btnOpenViva" Margin="0,10,0,0" Text="Learn more"
Command="{Binding OpenMyWebCommand}"
BackgroundColor="{StaticResource Primary}"
TextColor="White" />
<Button x:Name="btnLogin" Margin="0,10,0,0" Text="Login"
Command="{Binding OpenLoginPageCommand}"
BackgroundColor="{StaticResource VivaOrange}"
TextColor="White" />
</StackLayout>
</ScrollView>
</Grid>
</ContentPage>
HomeAnonymousViewModel:
public class HomeAnonymousViewModel : BaseViewModel
{
public HomeAnonymousViewModel()
{
Title = "my Mobile";
OpenMyWebCommand = new Command(async () => await Browser.OpenAsync("google.com"));
OpenLoginPageCommand = new Command(async () => await Shell.Current.GoToAsync($"{ nameof(LoginPage)}?TargetURL={AppSettingsManager.Settings["HomeUriPath"]}"));
}
public ICommand OpenMyWebCommand { get; set;}
public ICommand OpenLoginPageCommand { get; set;}
}
HomeAnonymousPage.cs:
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class HomeAnonymousPage : ContentPage
{
HomeAnonymousViewModel vm;
public HomeAnonymousPage()
{
InitializeComponent();
vm = (HomeAnonymousViewModel)this.BindingContext;
}
protected override async void OnAppearing()
{
base.OnAppearing();
}
}

adding listview hides form background image Xamarin ios

I am working on Xamarin cross platform app.
I have have form where I am using form's BackgroundImage = "timelinebackground.png"; on constructor to set background image.
Its working on fine Android but when I check same in iOS it's not showing background image.
When I remove listview from xaml then its background image is showing in iOS as well.
here is 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="BMTHomesApp.Views.Timeline">
<ContentPage.ToolbarItems>
<ToolbarItem Text="Logout" Order="Primary" Priority="0" Clicked="MenuItem_OnClicked"></ToolbarItem>
</ContentPage.ToolbarItems>
<StackLayout>
<ListView
x:Name="timelineListView"
ItemTapped="timelineListView_ItemTapped"
ItemsSource="{Binding .}"
RowHeight="75"
SeparatorVisibility="None">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Grid ColumnSpacing="0" RowSpacing="0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100" />
<ColumnDefinition Width="25" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Label HorizontalOptions="Center" Text="{Binding ActualStartDate}" TextColor="#0073ae" />
<Label Grid.Column="2" Margin="20,0" FontSize="Medium" Text="{Binding StageName}" TextColor="#ffffff"/>
<!--<Label
Grid.Row="1"
Grid.Column="2"
Margin="20,0"
FontSize="Small"
Text="Please add some desc text in api" TextColor="#ffffff"/>-->
<BoxView
Grid.RowSpan="2"
Grid.Column="1"
BackgroundColor="#ffffff"
HorizontalOptions="Center"
VerticalOptions="Fill"
WidthRequest="1" />
<Image Grid.Column="1" Source="Bullet.png" HeightRequest="25" WidthRequest="25" />
</Grid>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<DatePicker x:Name="DatePickerTimeline" IsVisible="False"></DatePicker>
</StackLayout>
</ContentPage>
and code behind:
public partial class Timeline : ContentPage
{
public Timeline ()
{
InitializeComponent ();
NavigationPage.SetBackButtonTitle(this, "");
Title = "TIMELINE";
BackgroundImage = "timelinebackground.png";
var constructionStages = GetConstructionStages();
if (!string.IsNullOrEmpty(constructionStages.Result.Error))
{
DisplayAlert("Error", constructionStages.Result.Error, "Ok");
}
else
{
timelineListView.ItemsSource = constructionStages.Result.ConstructionStages;
}
}
private void timelineListView_ItemTapped(object sender, ItemTappedEventArgs e)
{
var selectedItem = (ConstructionStages) e.Item;
if (!string.IsNullOrEmpty(selectedItem.ActualStartDate))
{
DateTime dateTimeStart;
if (DateTime.TryParse(selectedItem.ActualStartDate, out dateTimeStart))
{
DatePickerTimeline.MinimumDate = dateTimeStart;
}
}
if (!string.IsNullOrEmpty(selectedItem.ActualCompletionDate))
{
DateTime dateTimeEnd;
if (DateTime.TryParse(selectedItem.ActualCompletionDate, out dateTimeEnd))
{
DatePickerTimeline.MaximumDate = dateTimeEnd;
}
}
if (!string.IsNullOrEmpty(selectedItem.ActualStartDate) ||
!string.IsNullOrEmpty(selectedItem.ActualCompletionDate))
{
DatePickerTimeline.Format = "MM-dd-yyyy";
DatePickerTimeline.BackgroundColor = Color.FromHex("#ffffff");
//DatePickerTimeline.IsVisible = true;
DatePickerTimeline.Focus();
}
else
{
timelineListView.SelectedItem = null;
}
}
private async Task<ConstructionStagesResponse> GetConstructionStages()
{
ConstructionStageManager manager = new ConstructionStageManager();
var result = await manager.GetConstructionStages(Application.Current.Properties["SessionId"].ToString());
return result;
}
private async void MenuItem_OnClicked(object sender, EventArgs e)
{
HelperClasses.LogOut();
Application.Current.MainPage = new NavigationPage(new Login());
//Navigation.PushAsync(new Login());
}
}
You need to set your ListView's BackgroundColor property to transparent for the Page's background image to be shown:
<ListView BackgroundColor="Transparent">

UWPCommunityToolkit's PrintHelper produce blank output

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

MasterDetail Icon not shown if the page is navigated to from Login Page at start

So my app tries at the start to log in with saved between sessions login information.
Something like this:
public App()
{
if (DoLogin(UserData) == LoginStatus.Success)
MainPage = new NavigationPageNoLine(new MainAppPage());
else
MainPage = new NavigationPageNoLine(new LoginPage());
}
where MainAppPage is a MasterDetailPage.
If there is UserData saved between the sessions of the app, then the MainPage is the MasterDetailsPage where everything is in place and the MasterDetails icon appears as it should be:
But when there is no UserData saved, the LoginPage appears, where, after the login process is completed, I set:
Application.Current.MainPage = new NavigationPageNoLine(new MainAppPage());
Everything is ok except in this case the master details icon does not appear in the page:
The drawer is working perfectly in both cases. Only the icon disappears in the Login scenario.
How to make sure the icon appears in all cases?
Thank you in advance for any help.
Here is the MainAppPage.xaml:
<?xml version="1.0" encoding="utf-8" ?>
<MasterDetailPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="AppName.View.MainAppPage"
xmlns:local="clr-namespace:AppName.Helpers;assembley=Colors"
Title="{i18n:Translate NewsPageTitle}">
<MasterDetailPage.Master>
<ContentPage Title ="Options">
<StackLayout HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand" Padding="5,0,5,0">
<ListView IsGroupingEnabled="False"
ItemsSource="{x:Static apploc:Settings.OptionMenuItems}"
IsPullToRefreshEnabled="False"
HasUnevenRows="False"
x:Name="ListViewOptions"
HorizontalOptions="FillAndExpand"
SeparatorVisibility="None"
VerticalOptions="FillAndExpand">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Grid Padding="5,5" HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="2*"/>
<ColumnDefinition Width="8*"/>
</Grid.ColumnDefinitions>
<Image Grid.Column="0" Aspect="AspectFit" Source="{Binding IconSource}" HorizontalOptions="Center" VerticalOptions="Center"/>
<Label Grid.Column="1"
Text="{Binding Title}"
HorizontalOptions="Start"
TextColor="{x:Static local:Colors.PrimaryText}"
VerticalOptions="Center"
FontAttributes="Bold">
<Label.FontSize>
<OnPlatform x:TypeArguments="x:Double" iOS="14" Android="14" WinPhone="11" />
</Label.FontSize>
</Label>
</Grid>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
</ContentPage>
</MasterDetailPage.Master>
<MasterDetailPage.Detail>
<ContentPage Title ="Feeds">
<AbsoluteLayout HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand">
...
</AbsoluteLayout>
</ContentPage>
</MasterDetailPage.Detail>
</MasterDetailPage>
and here is my MainAppPage.xaml.cs:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Xamarin.Forms;
using AppName.Resx;
namespace AppName.View
{
public partial class MainAppPage : MasterDetailPage
{
public MainAppPage()
{
InitializeComponent();
ListViewOptions.ItemSelected += async (sender, e) => await NavigateTo(e.SelectedItem as OptionsMenuItem);
}
private async Task NavigateTo(OptionsMenuItem menuItem)
{
if (menuItem == null)
return;
if (menuItem.TargetType == null)
return;
Page displayPage;
try
{
if (menuItem.TargetType == typeof(WebPage))
displayPage = (Page)Activator.CreateInstance(menuItem.TargetType, menuItem.TargetUri, menuItem.Title);
else
displayPage = (Page)Activator.CreateInstance(menuItem.TargetType);
await Navigation.PushAsync(displayPage, true);
}
finally
{
ListViewOptions.SelectedItem = null;
IsPresented = false;
}
}
}
}

Listview not updated when isVisible is set to true

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

Resources