Xamarin Forms: WebView data binding - xamarin.forms

I'm trying to build a page which uses data binding to fill a WebView which is hosted on a StackLayout. Here's the XAML for that content page:
<?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="GES.Views.DetailsPage"
xmlns:di="clr-namespace:GES.DI;assembly=GES"
xmlns:c="clr-namespace:GES.Views.Converters;assembly=GES"
di:ServiceLocator.AutoWireViewModel="true"
Padding="15, 10, 15, 10"
Title="Ensino Superior">
<ContentPage.Resources>
<ResourceDictionary>
<c:HtmlSourceConverter x:Key="htmlConverter"/>
<Style x:Key="title" TargetType="Label">
<Setter Property="FontAttributes" Value="Bold" />
<Setter Property="TextColor" Value="{StaticResource colorPrimary}" />
<Setter Property="FontSize" Value="16" />
</Style>
<Style x:Key="dateText" TargetType="Label">
<Setter Property="TextColor" Value="{StaticResource lightGray}" />
<Setter Property="FontSize" Value="10"></Setter>
<Setter Property="FontAttributes" Value="Bold"></Setter>
</Style>
</ResourceDictionary>
</ContentPage.Resources>
<ContentPage.Content>
<StackLayout HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand">
<ScrollView>
<StackLayout HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand">
<Label Text="{Binding Path=News.Title}"
Margin="0, 10, 0, 10"
Style="{StaticResource title}"></Label>
<Label Text="{Binding Path=News.PublicationDate, StringFormat='{0:dd/MM/yyyy}'}"
HorizontalTextAlignment="End"
Margin="0, 0, 10, 10"
Style="{StaticResource dateText}"></Label>
<WebView Margin="0, 10, 0, 10"
Source="{Binding Path=News.Contents, Converter={StaticResource htmlConverter}}">
</WebView>
</StackLayout>
</ScrollView>
</StackLayout>
</ContentPage.Content>
</ContentPage>
Since the News.Contents property returns an HTML snippet, I've built a custom converter to transform the text into an HtmlWebViewSource:
public class HtmlSourceConverter: IValueConverter{
public object Convert(object value,
Type targetType,
object parameter,
CultureInfo culture) {
var html = new HtmlWebViewSource();
if (value is string h &&
!string.IsNullOrEmpty(h)) {
html.Html = HttpUtility.HtmlDecode(h);
}
return html;
}
public object ConvertBack(object value,
Type targetType,
object parameter,
CultureInfo culture) =>
throw new NotImplementedException();
}
Now, the problem. When the page is loaded, the WebView component will only render a small white rectangle...Rotating the device (ex.: going from portrai to landscape or vice-versa) does show the WebView's content. For instance, lets assume I'm using the device in portrait mode. Loading the page shows the following:
However, if I rotate my device, it will show the contents:
It looks like the WebView is not rendering correctly on first load...Btw, if I load the page on landscape, then I'll have to turn into portrait mode to see anything...
I've tried searching online and I did find an old bug, but it seems like it has been fixed (I'm using the latest stable version of the Xamarin Forms assembly)
Any clues on what's going on?

For anyone interested, I've followed this post to create a custom webview renderer...

Related

How to make behavior binding on customer control in Xamarin form or dotnet maui?

I created a customer entry which contain a label for error message
<Grid xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="ExampleApp.Contols.CustomerEntry"
Title="Customer"
x:Name="AppEntry">
<Entry x:Name="ControlEntry"
BackgroundColor="{Binding BackgroundColor, Source={x:Reference AppEntry}}"/>
<Label x:Name="ConrolErrorMessage"
Text="{Binding ErrorMessage, Source={x:Reference AppEntry}}"/>
</Grid>
Now using the control, in any page.
<CustomerEntry
x:Name="Password"
BackgroundColor="Blue">
<controls:EntryControl.Behaviors>
<toolkit:TextValidationBehavior
InvalidStyle="{StaticResource InvalidEntry}"
MinimumLength="10"
MaximumLength="100" />
</controls:EntryControl.Behaviors>
</CustomerEntry>
The InvalidEntry style is simple:
<Style
x:Key="InvalidEntry"
TargetType="controls:CustomerEntry">
<Setter Property="BackgroundColor" Value="Red" />
</Style>
Now the behavior is not working. Is it possible to bind pass the behavior to the Entry of the customer control?
I try to make the entry of the customer control as follow:
<Entry x:Name="ControlEntry"
BackgroundColor="{Binding BackgroundColor, Source={x:Reference AppEntry}}"
Behaviors="{Binding Behaviors, Source={x:Reference AppEntry}}"/>
The above code didn't work.

CollectionView VisualStateManager can't change selection color

I am trying to customize the selection color of a cell in a CollectionView but no matter how I try it, it's always an ugly grey.
I want my item template to have rounded corners, but when I select an item the I see ugly square grey corners behind it, as in this image:
Here's my current 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"
x:Class="Tests.CollectionViewTest">
<ContentView.Content>
<CollectionView
x:Name="collectionView"
Margin="15,0"
ItemSizingStrategy="MeasureFirstItem"
Grid.Row="1"
Grid.RowSpan="2"
VerticalScrollBarVisibility="Never"
BackgroundColor="Transparent"
SelectionMode="Multiple"
HorizontalOptions="Center"
VerticalOptions="Center"
>
<CollectionView.ItemsLayout>
<GridItemsLayout
Orientation="Vertical"
HorizontalItemSpacing="1"
VerticalItemSpacing="1"
Span="3" />
</CollectionView.ItemsLayout>
<CollectionView.ItemTemplate>
<DataTemplate>
<Frame
x:Name="selectionFrame"
CornerRadius="18"
BackgroundColor="Transparent"
Padding="0"
HasShadow="False"
IsClippedToBounds="True"
BorderColor="Transparent">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup
Name="CommonStates">
<VisualState
Name="Normal" />
<VisualState
Name="Focused">
<VisualState.Setters>
<Setter
Property="BackgroundColor"
Value="Transparent" />
</VisualState.Setters>
</VisualState>
<VisualState
Name="Selected">
<VisualState.Setters>
<Setter
Property="BackgroundColor"
Value="#e25fc4" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<StackLayout
BackgroundColor="#f7f0f6"
HorizontalOptions="FillAndExpand"
VerticalOptions="FillAndExpand"
Orientation="Vertical"
Padding="8,0,8,10"
Margin="10"
Spacing="0"
HeightRequest="100">
<Label
Padding="10"
x:Name="ServiceName"
BackgroundColor="Transparent"
Text="Some Text"
HorizontalTextAlignment="Center"
TextColor="HotPink"
FontSize="Micro"
FontAttributes="Bold"
HorizontalOptions="Center"
VerticalOptions="End" />
<Label
BackgroundColor="Transparent"
Text="Some More Text"
HorizontalTextAlignment="Center"
TextColor="HotPink"
FontSize="Micro"
HorizontalOptions="Center"
VerticalOptions="Start" />
</StackLayout>
</Frame>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
</ContentView.Content>
</ContentView>
And my code-behind:
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using Xamarin.Forms;
namespace Tests
{
public partial class CollectionViewTest : ContentView
{
public CollectionViewTest()
{
InitializeComponent();
collectionView.ItemsSource = new ObservableCollection<string>()
{
"", "", "", "", "", "", "", "", "", "", "", "", "", ""
};
}
}
}
I've tried other ways of doing this too, but nothing worked.
Is there a way to do it, or is this just a bug with CollectionView?
I found a kludgey solution, and in the absence of one that works the right way, it will have to do.
Set selection behavior in the CollectionView to none.
Put a tapGestureRecognizer into the itemTemplate
To simulate selection states, in the event handler for the tapGestureRecognizer, cast the sender to a Frame (or whatever element you attached the gesture recognizer to) and turn the frame border on or off (or do whatever you need to for your own custom selected-state appearance).
Manually handle whatever would normally be triggered by the CollectionView in response to selections. In other words, if you can select multiple items, you might be tracking the selected items in a separate list, and you will now have to do that from inside the tapGestureRecognizer.
It’s wrong but it works and sometimes that’s how you gotta do.
I start again to try to make it work, now i have a Frame in a StackLayout. And not a the other way around. But no luck is see , now there are no corners around the selected item.Sorry but i cannot make it to work.

How to provide BindingContext to Control template

I have a template defined in App.Xaml
<ResourceDictionary>
<ControlTemplate x:Key="HomePageTemplate">
<Label Text="{Binding MyLabelText}"/>
</ControlTemplate>
</ResourceDictionary>
And I use it in my Home page
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:cv="clr-namespace:Xamarin.Forms;assembly=Xamarin.Forms.CarouselView"
xmlns:local="clr-namespace:App.Converters"
x:Class="App.Views.HomePage"
ControlTemplate="{StaticResource HomePageTemplate}">
</ContentPage>
I set the BindingContext of my Homepage in code behind.
Now shouldn't the ControlTemplate inherit the HomePage's BindingContext ? Because I thought that was the case but my Label doesn't keep the text from MyLabelText. What's the work around to work with Bindings in these templates ?
EDIT:
Using this option
<ResourceDictionary>
<ControlTemplate x:Key="HomePageTemplate">
<Label Text="{TemplateBinding Parent.BindingContext.MyLabelText}"/>
</ControlTemplate>
</ResourceDictionary>
Also does not work for me, because I use the ControlTemplatein the header of the HomePage and not inside it's body.
This works BUT IT'S NOT what I'm looking for:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:cv="clr-namespace:Xamarin.Forms;assembly=Xamarin.Forms.CarouselView"
xmlns:local="clr-namespace:App.Converters"
x:Class="App.Views.HomePage"
>
<ContentView ControlTemplate="{StaticResource HomePageTemplate}" />
</ContentPage>
With ControlTemplate controls the binding is slightly different. Have a look at these docs: https://learn.microsoft.com/en-us/xamarin/xamarin-forms/app-fundamentals/templates/control-templates/template-binding
Assuming that the MyLabelText property is part of the BindingContext of the parent control your code could look like this:
<ResourceDictionary>
<ControlTemplate x:Key="HomePageTemplate">
<Label Text="{TemplateBinding Parent.BindingContext.MyLabelText }"/>
</ControlTemplate>
</ResourceDictionary>
App.xaml should have a BindingContext. Let's call it AppViewmodel, with a namespace vm (or any other key) defined at the top of App.xaml. This AppViewmodel can be bound to from within the ControlTemplate. In this example a button (placed in a stacklayout) is bound to a command named AppCommand, which resides in AppViewmodel. When this ControlTemplate is applied in a ContentPage, it binds to the ViewModel of App.xaml, not to the VM of that particular ContentPage. Perhaps the latter is also possible, for example by choosing the right settings for RelativeSource and AncestorType, but I haven't figured that out.
see also
<ControlTemplate x:Key="HomePageTemplate">
<StackLayout BindingContext="{Binding Source={RelativeSource TemplatedParent}}">
<Button
Command="{Binding Source={RelativeSource AncestorType={x:Type vm:AppViewModel}}, Path=AppCommand}"
/>
</StackLayout>
</ControlTemplate>
If anyone its interested, the way this works its just adding Path after TemplateBinding, specifying BindingContext on it and then the public var name:
<ResourceDictionary>
<ControlTemplate x:Key="HomePageTemplate">
<Label Text="{ TemplateBinding Path=BindingContext.MyLabelText }"/>
</ControlTemplate>
</ResourceDictionary>

Xamarin.Forms: How to hide the Navigation Bar Separator on iOS devices?

I have changed the default color of the navigation bar by using these codes in app.xaml as I am unable to make it transparent on iOS devices:
<Style TargetType="NavigationPage">
<Setter Property="BarBackgroundColor" Value="#0a82b8" />
<Setter Property="BarTextColor" Value="#ffffff" />
</Style>
Now, there is an unnecessary navigation bar separator on iOS:
On Microsoft's official website, it says these codes can be helpful:
This platform-specific hides the separator line and shadow that is at
the bottom of the navigation bar on a NavigationPage. It's consumed in
XAML by setting the NavigationPage.HideNavigationBarSeparator bindable
property to false:
<NavigationPage ...
xmlns:ios="clr-namespace:Xamarin.Forms.PlatformConfiguration.iOSSpecific;assembly=Xamarin.Forms.Core"
ios:NavigationPage.HideNavigationBarSeparator="true">
</NavigationPage>
Alternatively, it can be consumed from C# using the fluent API:
using Xamarin.Forms.PlatformConfiguration;
using Xamarin.Forms.PlatformConfiguration.iOSSpecific;
public class iOSTitleViewNavigationPageCS : Xamarin.Forms.NavigationPage
{
public iOSTitleViewNavigationPageCS()
{
On<iOS>().SetHideNavigationBarSeparator(true);
}
}
Source: Hiding the Navigation Bar Separator on a NavigationPage
The problem is that when I want to paste the ios:NavigationPage.HideNavigationBarSeparator="true" property into the <NavigationPage>tag, it gives this error:
No property, bindable property, or event found for 'HideNavigationBarSeparator', or mismatching type between value and property.
My full codes are:
<?xml version="1.0" encoding="utf-8"?>
<MasterDetailPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:local="clr-namespace:My.App" x:Class="My.App.MainPage">
<MasterDetailPage.Master>
<ContentPage Title="Menu" BackgroundColor="#0a82b8" Icon="menu.png">
<StackLayout Orientation="Vertical">
<ListView x:Name="navigationDrawerList" RowHeight="55" SeparatorVisibility="None" BackgroundColor="#ffffff" ItemSelected="OnMenuItemSelected">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<!-- Main design for our menu items -->
<StackLayout VerticalOptions="FillAndExpand" Orientation="Horizontal" Padding="20,10,0,10" Spacing="20">
<Label Text="{Binding Title}" FontSize="Large" VerticalOptions="Start" HorizontalOptions="CenterAndExpand" TextColor="#28DDFF" FontAttributes="Bold" />
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
</ContentPage>
</MasterDetailPage.Master>
<MasterDetailPage.Detail>
<NavigationPage xmlns:ios="clr-namespace:Xamarin.Forms.PlatformConfiguration.iOSSpecific;assembly=Xamarin.Forms.Core"
ios:NavigationPage.HideNavigationBarSeparator="true">
</NavigationPage>
</MasterDetailPage.Detail>
</MasterDetailPage>
Here is the tutorial which I followed for the MasterDetail navigation. The file which has <NavigationPage> tag is named as MainPage.xaml
You can use a custom renderer to hide that shadow:
[assembly: ExportRenderer(typeof(NavigationPage), typeof(CustomNavigationPage))]
namespace CustomNavigationPage.iOS
{
public class CustomNavigationPage : NavigationRenderer
{
protected override void OnElementChanged(VisualElementChangedEventArgs e)
{
base.OnElementChanged(e);
NavigationBar.SetBackgroundImage(new UIKit.UIImage(), UIKit.UIBarMetrics.Default);
NavigationBar.ShadowImage = new UIKit.UIImage();
}
}
}
While you have accepted the answer, this one is more precise - you haven't followed the instructions that you have pasted from the site as you have ommited this line:
xmlns:ios="clr-namespace:Xamarin.Forms.PlatformConfiguration.iOSSpecific;assembly=Xamarin.Forms.Core"
Place UINavigationBar.Appearance.ShadowImage = new UIImage(); inside the public override bool FinishedLaunching(UIApplication app, NSDictionary options) method within AppDelegate.cs file. AppDelegate.cs resides within the iOS project of Xamarin.Forms

Xamarin : Adding condition inside isvisible in xaml

I am new with xamarin, I am facing an issue in my xamarin forms project.
I have an inside listview-viewcell, having 250 width and height.
Sometimes the value of mediaUrl is null. I want to hide the Image for null mediaUrl values and make visible for other values.
My problem is if the value of the mediaUrl is null showing blank space in the UI. Inside isVisible property how can I apply this condition?
My code is giving below:
<StackLayout>
<ListView>
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout>
<Image
WidthRequest="250"
HeightRequest="250"
Source="{Binding mediaUrl}"/>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
Anybody please suggest a solution with working code.
Thanks in advance
You can achieve this using a value converter
Create a converter like this
public class NullToBoolConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return value != null;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
This will return false if the value is null
Register it in your page like this
<ContentPage
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:yourNameSpaceToWhereYourConverteClassIs"
x:Class="yourNameSpace.Views.MyPage">
<ContentPage.Resources>
<ResourceDictionary>
<local:NullToBoolConverter x:Key="NullToBoolConverter"/>
</ResourceDictionary>
</ContentPage.Resources>
Then add it like this
<StackLayout>
<ListView>
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout>
<Image
WidthRequest="250"
HeightRequest="250"
Source="{Binding mediaUrl}"
IsVisible={Binding mediaUrl, Converter={StaticResource NullToBoolConverter}}/>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
You can also use trigger for that
<Image.Triggers>
<DataTrigger TargetType="Image" Binding="{Binding isMediaUrlNull}" Value="True">
<Setter Property="IsVisible" Value="False" />
</DataTrigger>
</Image.Triggers>
Edit
You can also add property in model isMediaUrlNull and try above code
public bool isMediaUrlNull {get {return !string.IsNullOrEmpty(mediaUrl);}}
<Image WidthRequest="250" HeightRequest="250" IsVisible="{Binding mediaUrl}" Source="{Binding mediaUrl}"/>
the solution of Steve Chadbourne is good.
you should declare the converter this :
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="SAMPLE.Sample">
<!--RESOURCES-->
<ContentPage.Resources>
<ResourceDictionary>
<local:NullToBoolConverter x:Key="NullToBoolConverter"/>
</ResourceDictionary>
</ContentPage.Resources>
<!-- CONTENT -->
<ContentPage.Content>
<ListView >
Use Converter
</ListView>
</ContentPage.Content>

Resources