How to communicate between .net MAUI with a BlazorWebView and a Vue3 Project? - webview

We currently have a .net MAUI project with a BlazorWebView:
<?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:local="clr-namespace:MauiApp1"
x:Class="MauiApp1.MainPage">
<VerticalStackLayout Spacing="20" Padding="20" Background="#25a6fb">
<Label Text="Communicate between MAUI (C#) and BlazorWebView (JS)" TextColor="White" FontSize="20"
FontAttributes="Bold" HorizontalTextAlignment="Center" HorizontalOptions="Center" />
<VerticalStackLayout BackgroundColor="#25a6fb" Padding="10" Spacing="10">
<Frame Padding="10, 5">
<Entry x:Name="MauiEntry" Placeholder="Entry from MAUI" PlaceholderColor="Gray" TextColor="Gray" />
</Frame>
<HorizontalStackLayout HorizontalOptions="Center" Spacing="20">
<Button Text="MAUI to WebView" Clicked="MauiToWebView" />
</HorizontalStackLayout>
</VerticalStackLayout>
<BlazorWebView x:Name="BlazorWebView" HostPage="wwwroot/dist/index.html" WidthRequest="375" HeightRequest="400">
<BlazorWebView.RootComponents>
<RootComponent Selector="app" ComponentType="{x:Type local:Main}" />
</BlazorWebView.RootComponents>
</BlazorWebView>
<VerticalStackLayout>
</VerticalStackLayout>
</VerticalStackLayout>
</ContentPage>
And also just a basic Vue3 Project with nothing special.
We build the Vue3 project and then with a SymbolicLink show the dist/build folder in the .net MAUI project. And reference its index.html in the BlazorWebView.
We would like to call information back and forth between both projects.
We tried using JSInterop in the Main.razor file, but this didn't work.
#inject IJSRuntime Js
<Router AppAssembly="#typeof(Main).Assembly">
<Found Context="routeData">
<RouteView RouteData="#routeData"/>
<FocusOnNavigate RouteData="#routeData" Selector="h1"/>
</Found>
<NotFound>
<LayoutView>
<p role="alert">Sorry, there's nothing at this address.</p>
</LayoutView>
</NotFound>
</Router>
<br><br>
<code #ref="_codeBlock"></code>
#code
{
private ElementReference _codeBlock;
public async Task PrintDotNetObject()
{
var obj = new {
FistName = "Jon",
LastName = "Doe",
Age = 27,
BirthDate = DateTime.Now.AddYears(-27),
LikesDotNet = true
};
await Js.InvokeVoidAsync("dotNetMauiApp.getMessage", _codeBlock, obj);
}
}
PrintDotNetObject gets called when we click the button in the xaml file.
And this is what we had in the Vue3 Project:
<script>
export default {
name: "MauiCommunication",
mounted() {
window.dotNetMauiApp = {
getMessage: function(node, objectFromDotNet)
{
node.textContent = JSON.stringify(objectFromDotNet);
return window.alert("Called From MAUI " + node.textContent)
}
};
}
}
</script>
But we got a null reference for JSRuntime when using this. So is there a way to fix this? Or is there another (better) way to do this?

Related

Xamarin IOS Collection View Layout is misaligned at first render

I'm new with xamarin and I have a really weird bug which is making me crazy. I have a Collection View inside a Tab Item from xamarin tool kit.
The item template for the collection view consists on elements inside a Grid. Inside the template I have two grids which each one is visible depending on a boolean (IsStaticCheckList) which tells you if you have custom fields or normal fields.
I put my render function on the tap item command from the tab item (in that way I'm able to force the render each time the checklist item is tapped). The problem is that on IOS version the layout is not rendering well when I open the form for the first time but if we repeat the process it does it.
As an additional info:
I'm using the MVVM pattern so I use commands for all actions inside my view model.
The difference between custom and normal fields. In the UI is that you are able add as many normal fields in the list as you want and edit them. But if we are using custom fields you are not able to do those actions as you have a checklist template selected.
This only happens on IOS version, Android version works as expected.
This issue only happens with normal fields (IsStatiChecklist = true) on ios version. I've been debbuging the code but nothing seems to be wrong. At the end the layout is working as expected at second time. But I'm not able to make it work the first time I open the form.
I attached some pictures for reference
Here it is how it looks at first time (the 3 dot menu does not appear and the other fields neither because the content is using more space that it should) So all the elements are moved in some way to the right.
Render issue
Here it is how it looks after I leave the tab and go again to my checklist tab
Render Second time
Here is a screenshot of the other use case which is working correctly in all cases custom fields screenshot
Here I attached my Item Template XAML Code
<CollectionView.ItemTemplate>
<DataTemplate x:DataType="models:CheckListAnswerModel">
<Frame>
<Grid
Margin="-10,-20,0,0"
ColumnDefinitions="Auto,*,25,50,Auto"
ColumnSpacing="3">
<Grid
Grid.Column="0"
Grid.ColumnSpan="2"
Padding="20"
IsVisible="{Binding IsStaticCheckList, Source={RelativeSource AncestorType={x:Type viewModels:AddTaskViewModel}}}">
<Label
Grid.Row="0"
IsEnabled="{Binding Complete, Converter={x:StaticResource InvertedBoolConverter}}"
HorizontalOptions="Start"
Text="{Binding Question}"
VerticalOptions="Center"
IsVisible="{Binding ShowLabel}">
<Label.GestureRecognizers>
<TapGestureRecognizer Command="{Binding EnableEditionCommand, Source={RelativeSource AncestorType={x:Type viewModels:AddTaskViewModel}}}" CommandParameter="{Binding .}" />
</Label.GestureRecognizers>
<Label.Triggers>
<DataTrigger
Binding="{Binding Complete}"
TargetType="Label"
Value="true">
<Setter Property="TextDecorations" Value="Strikethrough" />
</DataTrigger>
</Label.Triggers>
</Label>
<Entry
Grid.Row="0"
IsEnabled="{Binding Complete, Converter={x:StaticResource InvertedBoolConverter}}"
IsVisible="{Binding ShowEntry}"
Placeholder="Click to add text"
ReturnCommand="{Binding CompleteAddCommand, Source={RelativeSource AncestorType={x:Type viewModels:AddTaskViewModel}}}"
ReturnCommandParameter="{Binding .}"
Text="{Binding Question}"
TextColor="{x:StaticResource PrimaryColor}">
<Entry.Effects>
<effects:BorderlessEntryEffect />
</Entry.Effects>
</Entry>
</Grid>
<ia:Checkbox
Grid.Column="2"
CheckColor="{x:StaticResource WhiteColor}"
FillColor="{x:StaticResource DefaultButtonColor}"
IsChecked="{Binding Complete}"
IsVisible="{Binding Complete}"
OutlineColor="{x:StaticResource DefaultButtonColor}"
Shape="Circle" />
<Image
Grid.Column="3"
IsVisible="{Binding HasTask}"
Scale="0.5"
Source="{x:Static res:Images.CreatedTaskIcon}" />
<ImageButton
Grid.Column="4"
BackgroundColor="Transparent"
Command="{Binding OpenMenuItemCommand, Source={RelativeSource AncestorType={x:Type viewModels:AddTaskViewModel}}}"
CommandParameter="{Binding .}"
Scale="2"
Source="{x:Static res:Images.VerticalMoreIcon}"
VerticalOptions="Center" />
</Grid>
</Frame>
</DataTemplate>
</CollectionView.ItemTemplate>
The Items Source for the collection view is called CheckList which has this definition:
private ObservableCollection<CheckListAnswerModel> _checklist;
public ObservableCollection<CheckListAnswerModel> Checklist
{
get => _checklist;
set
{
_checklist = value;
RaisePropertyChanged(() => Checklist);
}
}
Here is the funtion that tells the view model to add the corresponding items to the Checklist:
private async Task RenderElements()
{
if (_checkListTemplateId > 0)
{
var checklistTemplate = _checkListTemplates.FirstOrDefault(x => x.Id == _checkListTemplateId);
if (checklistTemplate != null)
{
CheckListTemplateText = checklistTemplate.Name;
customFieldAnswer = String.Empty;
_checkListTemplateId = checklistTemplate.Id;
IsStaticCheckList = false;
_checklistAnswerEntities = new List<CheckListAnswerEntity>();
if (Id > 0 && loadAnswers) // taskId > 0
{
_checklistAnswerEntities = await _userTaskManager.GetChecklistAnswerByTaskId(Id);
}
if (_checklistAnswerEntities != null && _checklistAnswerEntities.Count() > 0)
{
await RenderCustomFields(checklistTemplate.ChecklistCustomFields, _checklistAnswerEntities);
}
else
{
await RenderCustomFields(checklistTemplate.ChecklistCustomFields);
}
}
else
{
CheckListTemplateText = AppResources.Label_SelectTemplate;
}
}
else
{
if (HasNormalItems)
{
IsStaticCheckList = true;
Checklist = new ObservableCollection<CheckListAnswerModel>();
foreach (var item in EditTaskParam.TaskListModel.CheckListItems)
{
CheckListAnswerModel model = new CheckListAnswerModel(new CheckListAnswerEntity { TaskId = item.TaskId, ChecklistCustomFieldId = item.Id, Question = item.ItemName });
model.Complete = item.Complete;
model.ShowEntry = false;
model.ShowLabel = true;
model.IsTextEntry = false;
model.IsCustomDropdown = false;
model.IsYesNoEntry = false;
Checklist.Add(model);
}
SetCheckListLabel();
}
else
{
SetCheckListLabel();
}
}
}

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

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)

Buttons that start invisible (Binding to false property) don't fire command when made visible later

In my MainPage.xaml I have a CollectionView in a frame and three image buttons in a frame
I'd like the first button to be visible and when I tap it it makes the other two buttons visible and tap again and make the other two buttons invisible.
Seems pretty straightfoward and I can get it working IF they are all visible at first.
If however the other two buttons start off invisible then the button will not respond.
Aside: I also note that hot reload doesn't seem to work if changes made to the controls(views) inside a frame. I tried without the frames and no difference.
Here's the code:
MainPage.xaml
<?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:local="clr-namespace:Census.ViewModels"
x:Class="Census.MainPage">
<Grid
RowDefinitions="*, 65, 100"
RowSpacing="10">
<Frame Grid.Row="0"
CornerRadius="10"
BorderColor="White"
BackgroundColor="Black"
Margin="0,0,0,0">
<CollectionView ItemsSource="{Binding FriendsOC}"
SelectionMode="Single"
SelectedItem="{Binding SelectedItem}"
SelectionChangedCommand="{Binding SelectionChangedCommand}"
SelectionChangedCommandParameter="{Binding .}" >
<CollectionView.ItemTemplate>
<DataTemplate>
<Grid ColumnDefinitions="*, 200">
<Label Grid.Column="0"
Text="{Binding FName}"
FontSize="20"
TextColor="Yellow" />
<Label Grid.Column="1"
Text="{Binding LName}"
FontSize="20"
TextColor="Yellow" />
</Grid>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
</Frame>
<Frame Grid.Row="1"
CornerRadius="10"
BorderColor="White"
BackgroundColor="Black"
HeightRequest="60"
Margin="0,0,0,0"
Padding="0,2,0,0">
<Grid ColumnDefinitions="*, *, *">
<ImageButton Grid.Column="0"
Source="nullx.svg"
BorderColor="Yellow"
BorderWidth="2"
WidthRequest="45"
HeightRequest="45"
Command="{Binding RevealCommand}"/>
<ImageButton Grid.Column="1"
Source="nullx.svg"
BorderColor="Green"
BorderWidth="2"
WidthRequest="45"
HeightRequest="45"
IsVisible="{Binding ImportVisible}"
Command="{Binding ImportFriendsCommand}"/>
<ImageButton Grid.Column="2"
Source="nullx.svg"
BorderColor="Red"
BorderWidth="2"
WidthRequest="45"
HeightRequest="45"
IsVisible="{Binding DestroyVisible}"
Command="{Binding DestroyCommand}"/>
</Grid>
</Frame>
<Button Grid.Row="2"
Text="Add"
Command="{Binding AddFriendCommand}"
WidthRequest="100"
Margin="25,25,25,25"/>
</Grid>
</ContentPage>
MainPage.xaml.cs
namespace Census;
public partial class MainPage : ContentPage
{
public MainPage()
{
InitializeComponent();
BindingContext = new CensusViewModel();
}
protected override void OnAppearing()
{
base.OnAppearing();
//ImportVisible = false; <--things I tried to no avail
//DestroyVisible = false;
//FriendsList.ItemsSource = await App.Friends.GetFriendsAsync(); //because App.xaml.cs is where th db code is
}
}
and the viewmodel
namespace Census.ViewModels;
public partial class CensusViewModel : ObservableObject
{
[ObservableProperty]
public bool importVisible = true;
[ObservableProperty]
public bool destroyVisible = true;
...
//Reveal Actions
public ICommand RevealCommand => new Command(() =>
{
Console.WriteLine("Reveal");
ImportVisible = !ImportVisible;
DestroyVisible = !DestroyVisible;
});
...
}
This works fine
but if I do
[ObservableProperty]
public bool importVisible = false;
[ObservableProperty]
public bool destroyVisible = false;
It works in terms of hiding the two buttons, but does not respond if I tap the first button, except it does hit the code and change the properties.
Now in my head I am thinking of a question I asked here .net Maui databinding to shell flyout item IsVisible property
I've tried different variation on a theme but haven't been able to figure out what I could do. (Hence why I use the code binding in the codebehind as per the solution.
Just seems so bizarre that it works when the properties start off true and works fine, but not when false.
I've spent a good many hours on this by the way so I do try hard to figure it out myself.

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">

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;
}
}
}
}

Resources