Android/WASM : prismMvvm:ViewModelLocator.AutoWireViewModel="True" is not Wire-up the ViewModel for Android and Wasm - prism

I am trying to auto-wire up the ViewModel using prismMvvm:ViewModelLocator.AutoWireViewModel="True" in my View. For UWp it working perfectly. But with-in Android and WASM, View not able to wire up the ViewModel in my Uno platform Application using Prism.
<UserControl
x:Class="RepayablClient.Shared.Views.Login"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:prismMvvm="using:Prism.Mvvm"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
prismMvvm:ViewModelLocator.AutoWireViewModel="True"
mc:Ignorable="d">
<Grid Background="#881798">
<Grid.RowDefinitions>
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid
Width="250"
Height="300"
HorizontalAlignment="Center"
VerticalAlignment="Center">
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="25" />
<RowDefinition Height="25" />
</Grid.RowDefinitions>
<Image
Grid.Row="0"
Source="/Assets/Icon.png"
Stretch="Fill" />
<TextBlock Grid.Row="1" Text="{Binding LoginUser}" />
<ProgressBar
Grid.Row="2"
Width="250"
Margin="0,20,0,0"
Foreground="White"
IsIndeterminate="True"
ShowError="False"
ShowPaused="False" />
</Grid>
</Grid>
</UserControl>
using Microsoft.Identity.Client;
using RepayablClient.ViewModels;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace RepayablClient.Shared.ViewModels
{
public class LoginViewModel : ViewModelBase
{
//public ICommand LoginCommand { get; set; }
string graphAPIEndpoint = "https://graph.microsoft.com/v1.0/me";
private string _loginUser;
public string LoginUser
{
get { return _loginUser; }
set
{
_loginUser = value;
RaisePropertyChanged();
}
}
public LoginViewModel()
{
Title = "Login Page";
LoginUser = "Attempt to Login";
_ = LoginCommandExecutedAsync();
//LoginCommand = new AsyncCommand(LoginCommandExecutedAsync);
}
private async Task LoginCommandExecutedAsync()
{
AuthenticationResult authResult = null;
IEnumerable<IAccount> accounts = await App.publicClientApplication.GetAccountsAsync().ConfigureAwait(false);
IAccount firstAccount = accounts.FirstOrDefault();
try
{
authResult = await App.publicClientApplication.AcquireTokenSilent(Consts.Scopes, firstAccount)
.ExecuteAsync();
}
catch (MsalUiRequiredException ex)
{
System.Diagnostics.Debug.WriteLine($"MsalUiRequiredException: {ex.Message}");
try
{
authResult = await App.publicClientApplication.AcquireTokenInteractive(Consts.Scopes)
.ExecuteAsync();
}
catch (MsalException msalex)
{
// await DisplayMessageAsync($"Error Acquiring Token:{System.Environment.NewLine}{msalex}");
}
}
catch
{
// await DisplayMessageAsync($"Error Acquiring Token Silently:{System.Environment.NewLine}{ex}");
return;
}
if (authResult != null)
{
var content = await GetHttpContentWithTokenAsync(graphAPIEndpoint,
authResult.AccessToken).ConfigureAwait(false);
LoginUser = content;
}
}
public async Task<string> GetHttpContentWithTokenAsync(string url, string token)
{
var httpClient = new System.Net.Http.HttpClient();
System.Net.Http.HttpResponseMessage response;
try
{
var request = new System.Net.Http.HttpRequestMessage(System.Net.Http.HttpMethod.Get, url);
// Add the token in Authorization header
request.Headers.Authorization =
new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", token);
response = await httpClient.SendAsync(request);
var content = await response.Content.ReadAsStringAsync();
return content;
}
catch (Exception ex)
{
return ex.ToString();
}
}
}
}
For more details visit my repo: https://github.com/avikeid2007/Repayabl

You're facing an issue that is still standing in Uno, that will be adjusted soon. Basically, if you use a UserControl, some of the properties defined there may not be taken into account when the control is created.
You can fix this in one of two ways:
Change the UserControl to a ContentControl
Add the following property in your csproj:
<UnoSkipUserControlsInVisualTree>false</UnoSkipUserControlsInVisualTree>
This issue is a remnant of a time where Android had a very short UI Thread stack space, and that every layer in the visual tree counted. It's not as critical anymore.

Related

Getting StateLayout with CustomState to show image thumbnail

In my Xamarin Forms 5 app, I have a form users will fill out to create a post -- similar to Facebook posts.
The effect I'm trying to create is this:
There's an "Add Image" button that allows user to upload an image. Once the image is uploaded, I want to no longer display the button but display a thumbnail version of the uploaded image.
Here's what my XAML looks like:
<StackLayout
xct:StateLayout.CurrentState="{Binding MainState.None}"
xct:StateLayout.CurrentCustomStateKey="{Binding PostImageState}">
<xct:StateLayout.StateViews>
<xct:StateView StateKey="Custom" CustomStateKey="Image set">
<Image
Grid.Row="0"
Grid.Column="0"
Source="{Binding PostImageUrl}"
WidthRequest="30"
HeightRequest="30"/>
</xct:StateView>
</xct:StateLayout.StateViews>
<Button
Text="Add Image"
Command="{Binding AddImageCommand}"
BackgroundColor="{StaticResource SecondaryBackground}"
WidthRequest="100"
HeightRequest="35"
HorizontalOptions="Start"
Margin="10,0,0,0"/>
</StackLayout>
Here's an abbreviated version of my view model:
public class MyViewModel : BaseViewModel
{
public LayoutState _mainState;
string postImageUrl { get; set; }
string postImageState { get; set; } = "No image";
public MyViewModel()
{
Title = string.Empty;
IsBusy = true;
MainState = LayoutState.None;
AddImageCommand = new AsyncCommand(Add_Image_Tapped);
}
public LayoutState MainState
{
get => _mainState;
set => SetProperty(ref _mainState, value);
}
public string PostImageUrl
{
get => postImageUrl;
set
{
if (postImageUrl == value)
return;
postImageUrl = value;
OnPropertyChanged();
}
}
public string PostImageState
{
get => postImageState;
set
{
if (postImageState == value)
return;
postImageState = value;
OnPropertyChanged();
}
}
async Task Add_Image_Tapped()
{
// Upload image
// Once upload is done
PostImageUrl = uploadedFileUrl;
PostImageState = "Image set";
}
}
I haven't been able to get this to work. Currently, it's not even showing the "Add Image" button. Where am I making a mistake?
There are several problems with your code.
1.Since you use Binding for xct:StateLayout.CurrentState, we should bind it to a variable in ViewModel, here we should use MainState not MainState.None:
xct:StateLayout.CurrentState="{Binding MainState}"
2.Based on your requirement, we can use the value from LayoutState enumeration(for example StateKey="Success"),, we don't need add custom states.
3.If we want to hidden the button once uploading the image, we can bind MainState to property IsVisible of button , but need use Converter StateToBooleanConverter to convert State to bool.
Based on your code ,I created a simple demo, and it works properly on my side.
You can refer to the following code:
MyPage.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"
xmlns:xct="http://xamarin.com/schemas/2020/toolkit"
xmlns:viewmodel="clr-namespace:FormApp0314.ViewModel"
x:Class="FormApp0314.TestPage1">
<ContentPage.BindingContext>
<viewmodel:MyViewModel></viewmodel:MyViewModel>
</ContentPage.BindingContext>
<ContentPage.Resources>
<xct:StateToBooleanConverter x:Key="StateToBooleanConverter" />
</ContentPage.Resources>
<StackLayout
xct:StateLayout.CurrentState="{Binding MainState}">
<xct:StateLayout.StateViews>
<xct:StateView StateKey="Success" CustomStateKey="Image set">
<Image
Grid.Row="0"
Grid.Column="0"
Source="{ Binding PostImageUrl}"
WidthRequest="60"
HeightRequest="60"/>
</xct:StateView>
</xct:StateLayout.StateViews>
<Button
Text="Add Image"
Command="{Binding AddImageCommand}"
IsVisible="{Binding MainState, Converter={StaticResource StateToBooleanConverter}, ConverterParameter={x:Static xct:LayoutState.None}}"
WidthRequest="100"
HeightRequest="35"
HorizontalOptions="Start"
Margin="10,0,0,0" />
</StackLayout>
</ContentPage>
MyViewModel.cs
public class MyViewModel: BaseViewModel
{
public LayoutState _mainState;
string postImageUrl;
string postImageState = "No image";
public ICommand AddImageCommand { get; }
public MyViewModel()
{
MainState = LayoutState.None;
PostImageUrl = "bell.png";
AddImageCommand = CommandFactory.Create(Add_Image_Tapped);
}
async Task Add_Image_Tapped()
{
MainState = LayoutState.Success;
await Task.Delay(3000);
MainState = LayoutState.None;
}
public LayoutState MainState
{
get => _mainState;
set => SetProperty(ref _mainState, value);
}
public string PostImageUrl
{
get => postImageUrl;
set => SetProperty(ref postImageUrl, value);
}
public string PostImageState
{
get => postImageState;
set => SetProperty(ref postImageState, value);
}
}

Adding content on a custom content page

Ok, so I am trying to add a searchbar in the toolbar of my page.
The Search bar appears correctly in the toolbar and I can catch the on text changed event.
I created a new Xaml and cs page and changed content page to 'MySearchContentPage'
I Tried to add a grid and label on my new page created but nothing will show except for the searchbar. I added this just to see if I can get anything to display.
Am I adding it in the right place ? Or how do you add content to this page ?
I have done this by doing the following:
MySearchContentPage Class:
public class MySearchContentPage : ContentPage, ISearchPage
{
public MySearchContentPage()
{
SearchBarTextChanged += HandleSearchBarTextChanged;
}
public event EventHandler<string> SearchBarTextChanged;
public void OnSearchBarTextChanged(string text) => SearchBarTextChanged?.Invoke(this, text);
void HandleSearchBarTextChanged(object sender, string searchBarText)
{
//Logic to handle updated search bar text
}
}
ISearchPage:
public interface ISearchPage
{
void OnSearchBarTextChanged(string text);
event EventHandler<string> SearchBarTextChanged;
}
iOS renderer page:
public class MySearchContentPageRenderer : PageRenderer, IUISearchResultsUpdating
{
readonly UISearchController searchController;
bool _isFirstAppearing = true;
public override void WillMoveToParentViewController(UIViewController parent)
{
base.WillMoveToParentViewController(parent);
var searchController = new UISearchController(searchResultsController: null)
{
SearchResultsUpdater = this,
DimsBackgroundDuringPresentation = false,
HidesNavigationBarDuringPresentation = true,
HidesBottomBarWhenPushed = true
};
searchController.SearchBar.Placeholder = "Search Symptoms";
parent.NavigationItem.SearchController = searchController;
DefinesPresentationContext = true;
}
public override void ViewDidAppear(bool animated)
{
base.ViewDidAppear(animated);
//Work-around to ensure the SearchController appears when the page first appears https://stackoverflow.com/a/46313164/5953643
if (_isFirstAppearing)
{
ParentViewController.NavigationItem.SearchController.Active = true;
ParentViewController.NavigationItem.SearchController.Active = false;
_isFirstAppearing = false;
}
}
public void UpdateSearchResultsForSearchController(UISearchController searchController)
{
if (Element is ISearchPage searchPage)
searchPage.OnSearchBarTextChanged(searchController.SearchBar.Text);
}
public MySearchContentPageRenderer()
{
var searchControllerr = new UISearchController(searchResultsController: null)
{
SearchResultsUpdater = this,
DimsBackgroundDuringPresentation = false,
HidesNavigationBarDuringPresentation = false,
HidesBottomBarWhenPushed = true
};
searchControllerr.SearchBar.Placeholder = string.Empty;
}
public override void ViewWillAppear(bool animated)
{
base.ViewWillAppear(animated);
UINavigationBar.Appearance.TitleTextAttributes = new UIStringAttributes
{
ForegroundColor = UIColor.Red
};
}
public override void ViewDidLoad()
{
// base.ViewDidLoad();
// NavigationController.NavigationBar.PrefersLargeTitles = true;
// NavigationController.NavigationBar.BackgroundColor = UIColor.Red;
// var searchController = new UISearchController(searchResultsController: null);
// searchController.SearchBar.SearchBarStyle = UISearchBarStyle.Default;
// searchController.SearchBar.BackgroundColor = UIColor.Green;
// NavigationItem.SearchController = searchController;
// NavigationItem.HidesSearchBarWhenScrolling = false;
//searchController.SearchBar.SizeToFit();
//searchController.SearchBar.SearchBarStyle = UISearchBarStyle.Prominent;
////NavigationController.TabBarController
//this.sea
//NavigationController.TabBarController.NavigationItem.HidesSearchBarWhenScrolling = true;
//NavigationController.TabBarController.NavigationItem.SearchController = searchController;
//this.Title = "Search";
}
}
So far the outcome is this :
I can't seem to get anything else to add to this page. Can anyone explain why?
AddSymptomNew.xaml page:
<?xml version="1.0" encoding="UTF-8"?>
<visiblegyapp:MySearchContentPage
xmlns:visiblegyapp="clr-namespace:VisiblegyApp"
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="VisiblegyApp.AddSymptomNew"
xmlns:ios="clr-namespace:Xamarin.Forms.PlatformConfiguration.iOSSpecific;assembly=Xamarin.Forms.Core"
ios:Page.LargeTitleDisplay="Always"
Title="Search Symptoms"
BackgroundColor="{DynamicResource BasePageColor}"
>
<ScrollView
x:Name="outerScrollView"
Padding="0"
>
<Grid
x:Name="layeringGrid"
RowSpacing="0"
VerticalOptions="FillAndExpand">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Label Text="test label" TextColor="Red" Grid.Row="1"/>
</Grid>
</ScrollView>
The cause is ContentPage is inheritable while XAML is not inheritable.
I would recommend you to use a custom contentview and add this contentView to MySearchContentPage .
For example, create a custom contentView here:
public partial class MainPage : ContentPage
{
public MainPage()
{
InitializeComponent();
}
}
And in Xaml:
<?xml version="1.0" encoding="UTF-8"?>
<ContentView xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:d="http://xamarin.com/schemas/2014/forms/design"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
x:Class="App132.AddSymptomNewView">
<ContentView.Content>
<ScrollView
x:Name="outerScrollView"
Padding="0">
<Grid
x:Name="layeringGrid"
RowSpacing="0"
VerticalOptions="FillAndExpand">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Label Text="test label" TextColor="Red" Grid.Row="1"/>
</Grid>
</ScrollView>
</ContentView.Content>
</ContentView>
And use it in the MySearchContentPage :
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:d="http://xamarin.com/schemas/2014/forms/design"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:app132="clr-namespace:App132"
mc:Ignorable="d"
x:Class="App132.MainPage">
<app132:AddSymptomNewView/>
</ContentPage>

Xamarin ItemTemplate with a WebView control

I am trying to add a WebView control inside a ItemTemplate and set the height of the row. I know that I can't have the webview control scroll so I need to setting the height to the correct size to display the full html content. I have created an IValueConverter class that I was thinking can return the correct height needed but what height value to return depending on how long the content is?
Anyway I can load the webview and get the height needed to display the full content I get -1 for height in my writeline?
XAML Code
<telerikListView:ListViewTemplateCell>
<Grid BackgroundColor="{StaticResource LightBlueColor}"
Padding="10">
<telerikPrimitives:RadBorder Padding="10"
HorizontalOptions="Fill"
BorderThickness="2"
BorderColor="{StaticResource DarkBlueColor}"
BackgroundColor="White">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="50" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<!--<RowDefinition Height="*"/>-->
<RowDefinition Height="{Binding AssetItem.Description, Converter={StaticResource DescriptionToHeightConverter}}" />
</Grid.RowDefinitions>
<!--<Grid Grid.Row="0" Grid.Column="0">
<HtmlLabelControl:HtmlLabel
Text="{Binding AssetItem.Description}"
HeightRequest="100"/>-->
<WebView HeightRequest="800" MinimumHeightRequest="300" HorizontalOptions="FillAndExpand">
<WebView.Source>
<HtmlWebViewSource Html="{Binding AssetItem.Description}"/>
</WebView.Source>
</WebView>
<!--</Grid>-->
<!--<WebView Grid.Column="0" Grid.Row="0" HeightRequest="200" HorizontalOptions="FillAndExpand">
<WebView.Source>
<HtmlWebViewSource Html="{Binding AssetItem.Description}"/>
</WebView.Source>
</WebView>-->
<!--<Label Text="{Binding AssetItem.Description}"
TextColor="{StaticResource GrayTextColor}"
Grid.Row="0"
Grid.Column="0"/>-->
<!--Star-->
<telerikPrimitives:RadPath
x:Name="path"
Grid.Row="0"
Grid.Column="1"
WidthRequest="40"
HeightRequest="35"
StrokeThickness="2"
VerticalOptions="Start"
Fill="{Binding AssetItem.IsBookmark, Converter={StaticResource FavFillColorConverter}}"
Stroke="#3e7dc5"
Geometry="{x:Static telerikInput:Geometries.Star}">
<telerikPrimitives:RadPath.GestureRecognizers>
<TapGestureRecognizer NumberOfTapsRequired="1" Tapped="BookmarkCommand" CommandParameter="{Binding AssetItem.AssetId}" />
</telerikPrimitives:RadPath.GestureRecognizers>
</telerikPrimitives:RadPath>
</Grid>
<!--</Grid>-->
</telerikPrimitives:RadBorder>
</Grid>
</telerikListView:ListViewTemplateCell>
CS Converter Logic
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var description = value as string;
//WebView wv = new WebView();
//wv.Source = description;
HtmlWebViewSource HtmlSource = new HtmlWebViewSource();
HtmlSource.Html = description;
WebView webView = new WebView()
{
Source = HtmlSource
};
Debug.WriteLine($"Web View Height: {webView.Height}");
if (!string.IsNullOrEmpty(description))
{
if (description.Length == 300)
{
return 50;
}
}
return 300;
}
Test code
HtmlWebViewSource HtmlSource = new HtmlWebViewSource();
HtmlSource.Html = "<html><body><div><h1>MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM</h1></div></body></html>";
WebView webView = new WebView()
{
Source = HtmlSource
};
string htmlheight = "";
Task.Run(async () => {
try
{
htmlheight = await webView.EvaluateJavaScriptAsync("document.body.scrollHeight");
}
catch (Exception ex)
{
Debug.WriteLine(ex.Message);
}
});
//WebView_NavigatedAsync(webView);
Debug.WriteLine($"Web View Height: {htmlheight}");
If you want to get the height of the html .You can implement by using Custom Renderer
in Forms
public MainPage()
{
InitializeComponent();
HtmlWebViewSource HtmlSource = new HtmlWebViewSource();
HtmlSource.Html = #"<html><body>
<h1>Xamarin.Forms</h1>
<p>Welcome to WebView.</p>
</body></html>";
Webview webView = new Webview()
{
WidthRequest = 100,
HeightRequest = 20,
Source =HtmlSource
};
MessagingCenter.Subscribe<Object, float>(this,"webview_loaded",(sender,value)=>{
Console.WriteLine(value); //value is the height of html
});
Content = new StackLayout
{
Children =
{
webView,
},
VerticalOptions = LayoutOptions.FillAndExpand,
HorizontalOptions=LayoutOptions.FillAndExpand
};
}
in iOS project
using Foundation;
using UIKit;
using CoreGraphics;
using xxx;
using xxx.iOS;
using Xamarin.Forms;
using Xamarin.Forms.Platform.iOS;
[assembly:ExportRenderer(typeof(WebView),typeof(MyWebViewRenderer))]
namespace App7.iOS
{
public class MyWebViewRenderer:WebViewRenderer,IUIWebViewDelegate
{
protected override void OnElementChanged(VisualElementChangedEventArgs e)
{
base.OnElementChanged(e);
if(NativeView!=null)
{
// WeakDelegate = this;
}
}
[Export("webViewDidFinishLoad:")]
public void LoadingFinished(UIWebView webView)
{
string htmlHeight = webView.EvaluateJavascript("document.body.scrollHeight");
float height = float.Parse(htmlHeight);
MessagingCenter.Send<System.Object, float>(this, "webview_loaded", height);
}
}
}
in Android
using Android.Content;
using Android.Webkit;
using Android.Widget;
using xxx;
using xxx.Droid;
using Java.Lang;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;
[assembly: ExportRenderer(typeof(Xamarin.Forms.WebView), typeof(MyWebViewRenderer))]
namespace xxx.Droid
{
public class MyWebViewRenderer:WebViewRenderer
{
public MyWebViewRenderer(Context context):base(context)
{
}
protected override void OnElementChanged(ElementChangedEventArgs<Xamarin.Forms.WebView> e)
{
base.OnElementChanged(e);
if(Control!=null)
{
Android.Webkit.WebView webview =(Android.Webkit.WebView) Control;
WebSettings settings = webview.Settings;
settings.JavaScriptEnabled = true;
webview.SetWebViewClient(new JavascriptWebViewClient());
}
}
}
public class JavascriptWebViewClient : WebViewClient
{
public override void OnPageFinished(Android.Webkit.WebView view, string url)
{
base.OnPageFinished(view, url);
view.EvaluateJavascript("javascript:document.body.scrollHeight;", new EvaluateBack() );
}
}
class EvaluateBack : Java.Lang.Object, IValueCallback
{
public void OnReceiveValue(Java.Lang.Object value)
{
string htmlHeight = value.ToString();
float height = float.Parse(htmlHeight);
MessagingCenter.Send<System.Object, float>(this,"webview_loaded",height);
}
}
}
Notes: in your test code ,you get call the method when the html didn't finish loading ,so the result is -1.

Xamarin: Binding a ContentView content not working, wrong implementation

I need help understanding how I can bind a ContentView's Content's to my Xamarin page. I have tried maybe 20 different methods and I can only get the binded contentview to render when I don't use bindings and set the property directly.
<ContentPage.Content>
<Grid VerticalOptions="FillAndExpand" HorizontalOptions="FillAndExpand" x:Name="GridLayout" RowSpacing="0" ColumnSpacing="0">
<Grid.RowDefinitions>
<RowDefinition Height="60" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<nav:NavView Grid.Row="0" Grid.Column="0" Padding="0,0,0,0" Margin="0,0,0,0" HorizontalOptions="FillAndExpand" VerticalOptions="StartAndExpand"
HeightRequest="60" x:Name="nav"/>
<ContentView BindingContext="{Binding MainView}" Grid.Row="1" Grid.Column="0" HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand" />
</Grid>
</ContentPage.Content>
and then below is my code behind
public partial class DetailLayoutView : ContentPage, INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string name)
{
if (this.PropertyChanged != null)
this.PropertyChanged(this, new PropertyChangedEventArgs(name));
}
private ContentView mainView;
public ContentView MainView
{
get { return mainView; }
set
{
mainView = value;
OnPropertyChanged("MainView");
}
}
public DetailLayoutView()
{
InitializeComponent ();
MainView = new PyxusChatView();
BindingContext = MainView;
}
}
Can someone help guide me to the right direction? I dont have the bandwidth to implement an entire MVVM refactor at this time I would just like to know how I can achieve this w/ minimal code.
DETECT WHEN A UI IS RENDERED IOS & ANDROID BELOW...
public DetailLayoutView()
{
InitializeComponent ();
MainContentArea.LayoutChanged += MainContentArea_LayoutChanged;
}
private void MainContentArea_LayoutChanged(object sender, EventArgs e)
{
Device.BeginInvokeOnMainThread(() => {
//UI Is now visible, send a msg to let everyone subscribed to "UpdateDetailView" event now that the
//detail page was changed and it is now okay to make API calls!
MessagingCenter.Send<ContentView, string>(MainView, "UpdateDetailView", "From BlePairingViewModel");
});
}
YOU HAVE SENT MSG NOW YOU CAN MAKE API CALLS & YOUR UI IS VISIBLE W/O MVVM... ALTHOUGH MVVM WOULD BE A BETTER PRACTICE TO INDIVIDUALS W/ MORE TIME TO CODE..
MessagingCenter.Subscribe<ContentView, string>("Pyx", "UpdateDetailView", (sender, arg) =>
{
if (sender == this)
{
Task.Run(async () =>
{
await OnAppearing();
});
}
});
I AM NOT ADVISING THIS AS BEST PRACTICE BUT IT DOES ANSWER THE QUESTION

Xamarin button visible in iOS simulator but not on device

I'm new to Xamarin but know some iOS, got someone else's code dumped in my lap to fix some bugs. I've googled quite a bit, but I can't solve this one.
Everything works fine deployed on Android, deployed on iPhone 4/iOS 7.1.2, and simulated as iPhone 6/iOS 9.3 in the iOS simulator.
Problem
The btnNews button is visible in the iOS simulator (iPhone 6/iOS 9.3) but not when the code is deployed to an iPhone 6/iOS 9.3.1.
MainPage.xaml in a portable project
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:c="clr-namespace:Cookies;assembly=Cookies"
x:Class="MyCompany.MainPage" Title="MyCompany">
<Grid ColumnSpacing="1" RowSpacing="0">
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="50" x:Name ="buttonsRow"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<c:CookieWebView x:Name="webView" Grid.Row="0" Grid.ColumnSpan="2">
</c:CookieWebView>
<ActivityIndicator x:Name="activityIndicator"
VerticalOptions="FillAndExpand" HorizontalOptions="Center" Grid.Row="0" Grid.ColumnSpan="2" />
<Button x:Name="btnNews" Text="back to news list" BackgroundColor="#E15F59" BorderRadius="0" BorderColor="#E15F59"
TextColor="White" Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="2" BorderWidth="0" IsVisible="False"
HorizontalOptions="FillAndExpand"/>
</Grid>
</ContentPage>
MainPage.xaml.cs MainPage()
namespace MyCompany
{
public partial class MainPage : ContentPage
{
private Button _btnNews;
private ActivityIndicator Loader { get; set; }
private RowDefinition _row;
public const string URL_NEWS = "http://www.mycompany.com/";
public const string QUERY = "master=app";
public static CookieWebView WebView;
public static string CurrentUrl;
bool externalUrlClicked = false;
public MainPage(string url = null)
{
InitializeComponent();
// cookies common : https://github.com/seansparkman/CookiesWebView + nuget package CookiesWebView nuget : https://www.nuget.org/packages/CookieWebView/1.0.0
// ios: http://stackoverflow.com/questions/29768019/cookies-in-web-view-xamarin-ios
NavigationPage.SetHasNavigationBar(this, false);
WebView = this.FindByName<CookieWebView>("webView");
_row = this.FindByName<RowDefinition>("buttonsRow");
Loader = this.FindByName<ActivityIndicator>("activityIndicator");
WebView.Navigated += _webView_Navigated;
WebView.Navigating += WebView_Navigating;
var source = new UrlWebViewSource();
source.Url = GetUrl(!string.IsNullOrEmpty(url) ? url : URL_NEWS);
WebView.Source = source;
ReadCookiesFromSettings();
_btnNews = this.FindByName<Button>("btnNews");
_btnNews.Clicked += _btnNews_Clicked;
// Keep free from iPhone status bar
this.Padding = new Thickness(0, Device.OnPlatform(20, 0, 0), 0, 0);
}
}
}
MainPage.xaml.cs _webView_Navigated()
This method runs whenever a link is clicked in the web view. If the url is a "news item page" then btnNews ("Back to news list") should be shown, else btnNews should be hidden. I have set breakpoints to validate that the correct if/else blocks run at appropriate times. The deployed app uses the same urls/web content as the simulated app.
public void _webView_Navigated(object sender, CookieNavigatedEventArgs args)
{
if (externalUrlClicked)
{
args.Url = CurrentUrl;
externalUrlClicked = false;
return;
}
else
CurrentUrl = args.Url;
if (args.Url != GetUrl(URL_NEWS))
{
_row.Height = 50;
_btnNews.IsVisible = true;
}
else
{
_row.Height = 0;
_btnNews.IsVisible = false;
}
try
{
var cookie = WebView.Cookies[Settings.CookiesSettingsKey];
Settings.CookiesSettings = cookie.Value;
}
catch (Exception)
{
}
Loader.IsVisible = Loader.IsRunning = false;
}
In a native iOS app I would have started investigating layout and things like setNeedsDisplay, but I haven't yet found out how to do that with Xamarin.
Thanks for helping out!

Resources