XAML WebView binding to HTML source not working - xaml

I have a XAML page layout defined that includes a WebView populated from an HTML string:
<WebView HorizontalOptions="Fill" VerticalOptions="FillAndExpand">
<WebView.Source>
<HtmlWebViewSource Html="{Binding Flag.Description}" />
<!-- <HtmlWebViewSource Html="<html><body><p>The HTML string.</p></body></html>" />-->
</WebView.Source>
</WebView>
When I hard code the string into the XAML it displays correctly, but it will not bind to the Flag.Description string. The XAML includes a couple of labels that bind correctly but I can't find any reason why the WebView source isn't binding correctly.

I wasn't able to get the binding to work in XAML but there is a code-based solution to the problem. I'm using FreshMvvm so in the page model (not the code-behind for the XAML page) I created a new property that references the HTML string:
public HtmlWebViewSource WebViewSource
{
get {
return new HtmlWebViewSource { Html = Flag.Description };
}
}
And the WebView element in the XAML page becomes:
<WebView
HorizontalOptions="Fill"
VerticalOptions="FillAndExpand"
Source="{Binding WebViewSource}" />

The accepted answer worked for me but I will add that it only works when you specify a height and width.
There are a few combinations that worked:
1.
define HeightRequest and WidthRequest
<WebView Source="{Binding HtmlSource}" HeightRequest="300" WidthRequest="250" />
2.
define HorizontalOptions and VerticalOptions
<WebView x:Name="WebViewTermsOfService" Source="{Binding HtmlSource}" VerticalOptions="FillAndExpand" HorizontalOptions="FillAndExpand" />
However, when I specifed CenterAndExpand as a value, the HTML did not render

Seems like Binding Html property for HtmlWebViewSource just won't work out of the box. I think a IValueConverter will do the job. Please have a look how it is done in this answer. I think that should be enough

Would Flag or Flag.Description ever return null to the WebView? I was having the same problem and it turns out that the default value for my source was null before I initialized it with proper content. The WebView would essentially crashed. When I changed the null to String.Empty, it would work.

public HtmlWebViewSource Description { get { var webView = "html content";return
new HtmlWebViewSource { Html = webView };} }
<WebView
HorizontalOptions="Fill"
VerticalOptions="FillAndExpand"
Source="{Binding Description}" />

Related

Binding with Prism Navigation

I'm trying to bind string path in XAML using prism navigateto extension method from a button in ListView.
Apparently the BindingContext isn't recognized to be the same as in the ListView
Here's the sample code of what i'm trying to achieve.
<ListView
x:Name="MainMenu"
CachingStrategy="RetainElement"
HasUnevenRows="True"
ItemsSource="{Binding MenuItems}"
Margin="20,0,0,0"
SeparatorVisibility="Default">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<buttons:SfButton
Style="{StaticResource BaseButtonStyle}"
HeightRequest="70"
TextColor="{StaticResource MenuTextColor}"
Text="{Binding Title}"
HorizontalTextAlignment="Start"
VerticalOptions="CenterAndExpand"
HorizontalOptions="FillAndExpand"
ImageSource="{Binding MenuItemType, Converter={StaticResource MenuItemTypeConverter}}"
ShowIcon="True"
Command="{prism:NavigateTo Name={Binding Item.View}}"
/>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Any ideas how to set the binding in this situations?
Regards.
Within any sort of ListView, CollectionView, etc where you are binding an ItemsSource and then have some sort of DataTemplate to display an individual item in that collection, the Binding Context within that DataTemplate is the individual item in the collection not the ViewModel that that provides the Binding Context of both your Page and the ListView.
There are technically a couple parts to this that you will want to understand. Let's say that your model looks like:
public class FooModel
{
public string Text { get; set; }
public string NavigationPath { get; set; }
}
And let's say that you have a collection of FooModel like:
public ObservableCollection<FooModel> FooItems { get; }
In XAML you might have something like:
<ListView ItemsSource="{Binding FooItems}">
However when you go to reference properties of FooItem you will just reference them like:
<ListView.DataTemplate>
<DataTemplate>
<ViewCell>
<Button Text="{Binding Text}"
Command="{prism:NavigateTo Name={Binding NavigationPath}" />
</ViewCell>
</DataTemplate>
</ListView.DataTemplate>
Now assuming that the issue isn't that you're just adding Item erroneously, let's look at some other possible issues/solutions. To start let's look at the start of our page.
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:prism="http://prismlibrary.com"
x:Name="page"
x:Class="HelloWorld.Views.ViewA">
The big thing to notice here is that I've added the x:Name attribute so that I can reference the page itself later. In general within the context of something like a ListView if I need to access say the FooCommand property within my ViewModel I might would change my XAML markup from:
<Button Command="{Binding FooCommand}" />
To instead look at the Page's BindingContext like this:
<Button Command="{Binding BindingContext.FooCommand, Source={x:Reference page}}" />
While this will help you in general within your ListView it still doesn't necessarily help you with the issue of using Prism's Navigation Extensions. For this you may need to pass in the SourcePage like the following:
<Button Command="{prism:NavigateTo 'Foo', SourcePage={x:Reference page}}" />
In the event this doesn't work for some reason then you may be possible that the BindingContext isn't getting set properly on the Navigation Extension itself. To work around this you would want to update your command as follows:
<Button x:Name="cellButton"
Command="{prism:NavigateTo Name={Binding View},
BindingContext={x:Reference cellButton}}" />
Note that if you need to reference the SourcePage or add the BindingContext to resolve your issue, please provide a sample that reproduces the issue and open an issue on GitHub.

Xamarin Forms - adding the calling object as a parameter on a tap gesture recognizer

What is the correct syntax for adding the image in the XAML below as the parameter on the Command?
<ffimageloading:CachedImage Source="{Binding Source}" Aspect="AspectFit" CacheType="Memory" Opacity="2" x:Name="smallImage" >
<Image.GestureRecognizers>
<TapGestureRecognizer
Command="{Binding Path=BindingContext.SetImageCommand, Source={x:Reference this}}"
CommandParameter="{Binding smallImage}" />
</Image.GestureRecognizers>
</ffimageloading:CachedImage>
And the command code it's bound to (CustomCachedImage is just a class derived from a cached image, with an imageName field added)
There will be multiple instances of the calling image as it is in a data template as part of an image slider, so I can't just get the control by name I have to make sure it is the calling control being passed.
public ICommand SetImageCommand
{
get
{
return new Command<CustomCachedImage>((_image) =>
{
string imgName = _image.ImageName;
SetImg(imgName);
});
}
}
changed "{Binding smallImage}" to "{Binding .}" and I got what I needed

Refresh UI after Dynamicly adding entry field to stacklayout on stipper value change

I have a stepper and i want to add new entry field when the value increment or delete entry on value decrement , i manged to do that but the UI won't refresh directly after the value of the stepper change (i need to click on other UI element first) then the fields will appear , and after clicking in other UI element the stepper will work properly (adding and deleting functionality) which i don't know why it's happening ?!! .
EDIT :
It's look like the problem is with having scroll view .. if i remove it the new fields will added or deleted directly .. but still don't know why .
The xaml page
<ScrollView>
<StackLayout x:Name="StackLayout">
<Label Text="عدد العناصر المراد اضافتها (اقصى عدد في المرة الواحدة 30)"></Label>
<Stepper Maximum="30"
Minimum="2"
Increment="1"
ValueChanged="Stepper_OnValueChanged"
Value="2"></Stepper>
<StackLayout x:Name="EntryStackLayout">
<Entry></Entry>
<Entry ></Entry>
</StackLayout>
</StackLayout>
</ScrollView>
Code Behind
public partial class AddNewListOfItemsPage : ContentPage
{
public AddNewListOfItemsPage ()
{
InitializeComponent();
}
private void Stepper_OnValueChanged(object sender, ValueChangedEventArgs e)
{
if (e.NewValue > e.OldValue)
{
EntryStackLayout.Children.Add(new Entry());
}
else
{
var childCount = EntryStackLayout.Children.Count;
EntryStackLayout.Children.RemoveAt(childCount - 1);
}
}
}
Ok i found that adding a stackLayout before the scrollView fix the problem
<StackLayout>
<ScrollView>
<StackLayout x:Name="StackLayout">
<Label Text="عدد العناصر المراد اضافتها (اقصى عدد في المرة الواحدة 30)"></Label>
<Stepper Maximum="30"
Minimum="2"
Increment="1"
ValueChanged="Stepper_OnValueChanged"
Value="2"></Stepper>
<StackLayout x:Name="EntryStackLayout">
<Entry></Entry>
<Entry ></Entry>
</StackLayout>
</StackLayout>
</ScrollView>
</StackLayout>
I had a similar problem. Size of elements deep inside ScrollView was not refreshed after layout change (on Android < 4.3).
Specifying height of ScrollView helped (specifying either one of the following for ScrollView in XAML helped):
VerticalOptions="FillAndExpand"
HeightRequest="100"

Xamarin XAML ActivityIndicator, content doesn't show up

I have a ListView in my XAML in which I show some data from my database, the problem is that in the meantime the ActivityIndicator show up as excpected, but when I set it to False, the content that is suppose to show up, doesn't. I don't know if I'm using the ActivityIndicator wrong, how I suppose to use it then?
XAML:
<?xml version="1.0" encoding="UTF-8"?>
<ContentPage...">
<ActivityIndicator x:Name="load1" Color="Red" IsRunning="true" />
<ContentPage.Content>
<ListView x:Name="XPS" ItemTapped="OnItemSelected"
...
</ListView>
</ContentPage.Content>
</ContentPage>
CS:
load1.IsRunning=false;
It looks like you're using ContentPage incorrectly. That page only supports having 1 child element (named Content). You're trying to define 2 different Content on 1 ContentPage. Xamarin.Forms just throws away your ListView so it will never show up.
So now you might wonder... How is that thing even useful? Good question! When you need to put multiple Views in 1 ContentPage you need to use a Layout. Xamarin.Forms has a bunch of layouts available that all behave differently - see https://developer.xamarin.com/guides/cross-platform/xamarin-forms/controls/layouts/ for some more details there.
Let's check out some code.... (I haven't actually tested this, I'm typing it directly into here, so you might need to fix some things...)
An updated ContentPage XAML:
<ActivityIndicator x:Name="load1" Color="Red" IsRunning="true" />
<ContentPage.Content>
<StackLayout x:Name="Layout">
<ActivityIndicator x:Name="load1" Color="Read" IsRunning="true"/>
<ListView x:Name="XPS" ItemTapped="OnItemSelected" Opacity="0"
...
</ListView>
</StackLayout>
</ContentPage.Content>
</ContentPage>
Now for some Code Behind...
public void LoadYourStuff()
{
LoadSomeTotallyAwesomeData();
WriteSomeTotallyAwesomeDataIntoAFancyListView();
PerhapsDoMoreFancyThings();
Layout.Children.Remove(load1);
XPS.Opacity = 1.0;
}
in your xmal file put :
<ActivityIndicator x:Name="popupView" VerticalOptions="Center" HorizontalOptions="Center" Color="DarkBlue" IsRunning="True" IsVisible="false" IsEnabled="True" />
when need this in C# code :
popupView.IsVisible = true;
.
.
.
.
.
.
popupView.IsVisible = false;

Xamarin forms button with no border issue

I try to render a list of clickable items in a view. I would like to add a button with an image and a white border (the first one). I discovered that the buttons in my StackLayout/ViewCell can't render a border.
<?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:controls="clr-namespace:XLabs.Forms.Controls;assembly=XLabs.Forms"
x:Class="*.PlacesPage"
Title="TEST">
<ContentPage.Padding>
<OnPlatform x:TypeArguments="Thickness" iOS="0, 20, 0, 0" />
</ContentPage.Padding>
<ContentPage.Content>
<Grid>
<ListView x:Name="lvPlaces" ItemsSource="{Binding Places}" SeparatorColor="Gray" SeparatorVisibility="Default" RowHeight="120" >
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<ViewCell.View>
<StackLayout Orientation="Horizontal">
<Button HorizontalOptions="Center" VerticalOptions="Center" BorderWidth="3" BorderColor="White" Text="IMG"></Button>
<Button Text="{Binding Name}" BorderWidth="0" FontSize="20" BackgroundColor="Transparent" Clicked="OnButtonClickedPlace"></Button>
</StackLayout>
</ViewCell.View>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
</ContentPage.Content>
I'm using Xamarin.Forms 2.3, and I was also trying to create a button, with no border radius, a background color set to white, and a border color & width, and none of the above answers worked for me.
Actually I still had to set the BorderRadius to 1 (my width was 2), AND add another workaround that I just cannot understand :
In my Android project, I added a Custom renderer, for Button, with nothing in it. Absolutely nothing.
So the behavior of Xamarin forms, is different on Android when you use the Default renderer, and when you use a custom renderer that inherits from the default renderer, and yet with no line of code in it.
My Renderer:
[assembly: ExportRenderer(typeof(Xamarin.Forms.Button), typeof(GenericButtonRenderer))]
namespace Express.CustomRenderers
{
public class GenericButtonRenderer : Xamarin.Forms.Platform.Android.ButtonRenderer
{
}
}
There seems to be a issue in Xamarin.Forms where Button borders don't show on Android when the ButtonRadius is 0. (It doesn't appear that this issue is fixed as of Xamarin.Forms v2.0.0.6482.) It's not ideal since it will slightly change the look of the button, but you can work around it by setting BorderRadius = 1 for just Android, or all platforms, giving a slightly perceptible rounding to the corners.
Workaround (code)
// HACK: fixes a bug where border doesn't work without a radius.
yourButton.BorderRadius = Device.OnPlatform<int>(iOS: 0, Android: 1, WinPhone: 0);
Workaround (XAML)
<Button
x:Name="YourButton"
Text="Some Button Text"
TextColor="Black"
Clicked="OnClickedDiscover"
BackgroundColor="Aqua"
BorderColor="Red"
BorderWidth="1">
<Button.BorderRadius>
<!-- HACK: fixes a bug where border doesn't work without a radius. -->
<OnPlatform x:TypeArguments="x:Int32">
<OnPlatform.Android>1</OnPlatform.Android>
</OnPlatform>
</Button.BorderRadius>
</Button>
Are you using Android? If yes, then:
On Android this property will not have an effect unless
VisualElement.BackgroundColor is set to a non-default color.
In Xamarin.Forms 2.5.0, the patch has been reverted :
"Revert "Fix border on android buttons (#941)" (#1192)"
You have to use a custom renderer for now...
This bug has been fixed in the last version of Xamarin Forms 2.4.0 :
> 36031 - "Button border not drawn on Android without a BorderRadius" (PR)
in Android project go to MainActivity and change it to be inhereted
from
public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity
to
public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsApplicationActivity
now you won't have issue to use the Border
<Button Text="test" TextColor="White"
BackgroundColor="#FFA733" BorderRadius="15"
BorderColor="White" BorderWidth="2" HorizontalOptions="FillAndExpand" />
Got the some problem. I did two things to solve it:
I don't set background color to the buttons for Android (only for iOS)
<Setter Property="BackgroundColor">
<OnPlatform x:TypeArguments="x:String">
<OnPlatform.iOS>Transparent</OnPlatform.iOS>
</OnPlatform>
</Setter>
Manually setting a background drawable to the buttons
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<stroke android:width="2px" android:color="#ffffff" />
</shape>
I found this solution, don't know why is working but it works.
In PCL
namespace xxx.Renderers
{
public class WhiteButton : Button
{
public WhiteButton()
{
}
}
}
Then you have to make the render in android and DO NOTHING
[assembly: ExportRenderer(typeof(WhiteButton), typeof(WhiteButtonRenderer))]
namespace xxxx.Droid.Renderers
{
public class WhiteButtonRenderer : ButtonRenderer
{
protected override void OnElementChanged(ElementChangedEventArgs<Button> e)
{
base.OnElementChanged(e);
if (Control != null)
{
var newElement = e.NewElement as WhiteButton;
if (newElement != null)
{
}
}
}
}
}
Then you only have to call instantiate the button and do the border that you want
var myButton = new WhiteButton()
{
BackgroundColor = Color.Transparent,
TextColor = Color.White,
Text = "Desconectarse",
BorderRadius = 45/2,//rounded border Heightbutton/2
BorderWidth = 2,
BorderColor = Color.White
};
If nobody knows why is working please explain me , I have tried the same but with no render only using the class Button normally and if I do this I don't get the expected result.
in Android project create ButtonRenderer and paste code
protected override void OnElementPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
if (Control != null)
{
var roundableShape = new GradientDrawable();
roundableShape.SetShape(ShapeType.Rectangle);
roundableShape.SetStroke((int) Element.BorderWidth,Element.BorderColor.ToAndroid());
roundableShape.SetColor(Element.BackgroundColor.ToAndroid());
roundableShape.SetCornerRadius(Element.BorderRadius * Resources.DisplayMetrics.Density);
Control.Background = roundableShape;
Control.TransformationMethod = null;
Control.Elevation = 0;
}
base.OnElementPropertyChanged(sender, e);
}