XAML WebView binding to string not working in Xamarin Forms - c#

I'm new to C# and Xamarin Forms. I'm having a webview and getting source url from an API. (For this question , I have hardcode the value). I binded source url instead of adding the value to Source in XAML. But it's not working. There are few solutions in stack and forums. I tried. But didn't work. Someone please help me to sovle this.
This is my XAML
<?xml version="1.0" encoding="UTF-8"?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="MyProject.Views.NewRegistration.PrivacyWebView">
<ContentPage.Content>
<AbsoluteLayout HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand">
<WebView Source="{Binding WebViewSource}" HeightRequest= "300" WidthRequest="250" Navigated="Handle_Navigated" VerticalOptions="FillAndExpand" AbsoluteLayout.LayoutFlags="All" AbsoluteLayout.LayoutBounds="0,0,1,1" />
<ActivityIndicator x:Name="loader" IsRunning="true" IsVisible="true" AbsoluteLayout.LayoutFlags="All" AbsoluteLayout.LayoutBounds="0,0,1,1"/>
</AbsoluteLayout>
</ContentPage.Content>
</ContentPage>
This is how I bind the source. (Tried this in Codebehind and ViewModel too)
public HtmlWebViewSource WebViewSource
{
get
{
return new HtmlWebViewSource { Html = "https://www.stackoverflow.com" };
}
}

You're using it wrong, when using the HtmlWebViewSource you need to specify actual HTML instead of the URL where you want to go to. If you want to navigate to a URL, specify it in the Source property.
If you want to bind it, you have to implement something like this.
In your view model create a string property:
public string UrlToGoTo { get; set; }
Then set it like you normally would, make sure to have INotifyPropertyChanged is implemented somehow.
Then, wire up your WebView like this:
<WebView Source="{Binding UrlToGoTo}"
HeightRequest= "300"
WidthRequest="250"
Navigated="Handle_Navigated"
VerticalOptions="FillAndExpand"
AbsoluteLayout.LayoutFlags="All"
AbsoluteLayout.LayoutBounds="0,0,1,1" />

Related

Bind Properties onto custom controls

I want to create a very basic custom control that will update its label if the label changes in the containing view model
My setup is like this:
public partial class BindTest : ContentView
{
public static readonly BindableProperty TitleProperty = BindableProperty.Create(nameof(Title), typeof(string), typeof(BindTest), string.Empty, propertyChanged: TitleChanged);
static void TitleChanged(BindableObject bindable, object oldValue, object newValue)
{
var control = (BindTest)bindable;
control.Title = newValue as string;
}
public string Title
{
get => (string)GetValue(BindTest.TitleProperty);
set => SetValue(BindTest.TitleProperty, value);
}
public BindTest()
{
InitializeComponent();
}
}
<?xml version="1.0" encoding="utf-8" ?>
<ContentView xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="MauiDemo.BindTest" x:Name="this">
<StackLayout BindingContext="{x:Reference this}">
<Label TextColor="White" Text="{Binding Title}" />
</StackLayout>
</ContentView>
And i consume my custom control like this and bind my property from my VM (annotated as ObservableProperty)
<controls:BindTest Title="{Binding TestString}"/>
<Label Text="{Binding TestString}"/>
If i press a Button that executes a command my TestString will be overriden and updated correctly. the separate label beneath my custom controls displays the changing value correctly.
did i misinterpret the use case of custom controls or is something wrong the way i setup things?
Ok, so i found out, that i indeed messed up my setup for creating a custom control.
As i already stated, the problem was that the custom control wouldn't get updated even if the BindableProperty would update.
The BindableProperty would always properly update but the propertyChanged callback would never be called again after the initial value override.
So now i was wondering, why would the propertyChanged callback only be called once?
The root of my problem was the line control.Title = value as string;
Removing just this one line and leaving the rest of the setup as-is resulted in the custom control correctly updating if the BindableProperty was updated.
Which means, that by calling the setter of the property used by the custom control somehow interrupts the way maui connects the bindings for the properties.
Therefore i missinterpreted the use of the propertyChanged callback, due to the influence on random samples out there in the web.
I will leave the working class and uses down below so u can just test/try out and see how bindings work in custom controls if anybody happens to stumble across the same or similar problems.
BindTest.xaml.cs
public partial class BindTest : ContentView
{
public static readonly BindableProperty TitleProperty = BindableProperty.Create(nameof(Title), typeof(string), typeof(BindTest), string.Empty);
public string Title
{
get => (string)GetValue(BindTest.TitleProperty);
set => SetValue(BindTest.TitleProperty, value);
}
public BindTest()
{
InitializeComponent();
}
}
BindTest.xaml
<?xml version="1.0" encoding="utf-8" ?>
<ContentView xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="MauiDemo.Controls.BindTest" x:Name="this">
<StackLayout BindingContext="{x:Reference this}">
<Label TextColor="White" Text="{Binding Title}" />
</StackLayout>
</ContentView>
usage in different view
<?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:controls="clr-namespace:MauiDemo.Controls"
x:Class="MauiDemo.Test">
<StackLayout>
<controls:BindTest Title="{Binding TestString}"/>
</StackLayout>
</ContentView>
This is not the correct way to create custom controls because this way you will always run into BindingContext related issues as soon as your control becomes a part of another view and the BindingContext gets shared to the branches of that View.
So your code should look like below:
<?xml version="1.0" encoding="utf-8" ?>
<ContentView xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="MauiDemo.BindTest" x:Name="this">
<StackLayout>
<Label TextColor="White" Text="{Binding Title, Source={x:Reference this}}" />
</StackLayout>
</ContentView>
You can check my MAUI library that handles different kinds of custom controls https://github.com/FreakyAli/Maui.FreakyControls.
Hope it helps :)

Displaying the default back button of the navigation bar of a ContentPage assigned to the Xamarin.Forms Shell

I have a ContentPage which is assigned to the Xamarin.Forms.Shell class as a ShellContent and require the back button to be displayed in the navigation bar of the ContentPage.
The XAML source of the ContentPage in concern is as follows:
<?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="UI_test.TestPage">
<Shell.NavBarIsVisible>True</Shell.NavBarIsVisible>
<Shell.BackButtonBehavior>
<BackButtonBehavior IsEnabled="True" />
</Shell.BackButtonBehavior>
<Shell.FlyoutBehavior>Disabled</Shell.FlyoutBehavior>
<ContentPage.Content>
<StackLayout>
<Label Text="Welcome to Xamarin.Forms!"
VerticalOptions="CenterAndExpand"
HorizontalOptions="CenterAndExpand" />
</StackLayout>
</ContentPage.Content>
</ContentPage>
I found that that assigning a custom value to the attributes TextOverride and IconOverride of the <BackButtonBehavior> tag will display the back button, but I am looking for a way to display the platform's default back button (rather than a custom one) as the above ContentPage does not display as back button in its navigation bar as seen in the screenshot below.
Thanks in advance.
Your project seems to contain one page only, It won't appear if no navigation has been made, and it makes sense. If you do a Shell.GotoAsync(..) or Shell.Navigation.PushAsync(..) to navigate to another page it will then appears (the default native back button) allowing to go back to the previous page in the navigation stack.

List of Buttons with Binding and Command in Xamarin

I'm working on a Xamarin App where I want to dynamically display a list of Registry Numbers from a Class Registry.
After the list of numbers is displayed, the user should choose one of them to login in the App.
I have decided that this list will be displayed as a list of Buttons, because once the user clicks it, nothing else needs to be done. However, most of documentation regarding Binding and ListView does not use Buttons as displaying element.
I have decided to follow the steps on this excellent video but I keep receiving the following error:
Binding: 'LocalCommand' property not found on '31348', target property: 'Xamarin.Forms.Button.Command'
Binding: 'LocalCommand' property not found on '10227', target property: 'Xamarin.Forms.Button.Command'
Actually, 31348 and 10227 are the numbers that I want to display. And indeed I indicated them as the Binding context at some point. But I would like to "change" that Binding so I can invoke the LocalCommand method. Probably implementing the LocalCommand in the object would solve the issue, but I definitely don't want to do that!
Questions:
Is there a better and simpler way to do this?
How can I do to "bring back" the Binding to the LocalCommand?
I'm still learning about Binding, so any tips would be really useful!
Thanks!
RegistryPage.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:behavior1="clr-namespace:SPConsigMobile.Utils"
x:Class="App.Views.RegistryPage"
BackgroundColor="{StaticResource AppBackgroundColor}"
Title="App"
NavigationPage.HasNavigationBar="false">
<ContentPage.Content>
<ListView x:Name="RegistryView"
ItemsSource="{Binding User.Registry}"
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Button Text="{Binding Number}"
Command="{Binding LocalCommand}"/>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</ContentPage.Content>
</ContentPage>
RegistryPage.xaml.cs
namespace App.Views
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class RegistryPage : ContentPage
{
public RegistryPage()
{
InitializeComponent();
RegistryView.ItemsSource = User.Registries;
}
public ICommand LocalCommand => new Command(CommandClicked);
private void CommandClicked()
{
Console.WriteLine("Command Button Clicked!");
}
}
}
In general when you set the ItemSource property of a control (in this case of your ListView), you have also to set the DataTemplate.
Now, the BindingContext of view inside the DataTemplate is an item of the collection you have binded to. In your case, because you have set the ItemSource to be a Collection of PhoneNumber, the bindingContext of each view is a PhoneNumber.
So when you are trying to acess your command with 'Command="{Binding LocalCommand}"', what you are doing is to search a LocalCommand Property inside a PhoneNumber class. What you need instead is to search it inside your Page class. So, give a name to your ContentPage with x:Name, then reference the source to your command binding to be the Root Page, and the Path to be the path to the command, starting from the Source (so NumberSelectedCommand in my example). the command parameter should be instead exactly the number, so it's an empty path Binding.
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="App1.RegistryPage"
x:Name="Root"
>
<StackLayout>
<ListView x:Name="RegistryView"
ItemsSource="{Binding User.Registry}"
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Button Text="{Binding Number}"
Command="{Binding Source={x:Reference Root}, Path=NumberSelectedCommand}"
CommandParameter="{Binding}"
/>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
</ContentPage>
RegistryPage.xaml.cs
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class RegistryPage : ContentPage
{
public RegistryPage()
{
InitializeComponent();
RegistryView.ItemsSource = User.Registries;
NumberSelectedCommand = new Command<PhoneNumber>(OnNumberSelected);
}
// Commands
public ICommand NumberSelectedCommand { get; }
// Commands Handlers
private void OnNumberSelected(PhoneNumber selectedNumber)
{
// Do what you need with selected number.
}
}

Display text while Page is loading in Xamarin Forms

I have a page that is quite heavy and its loading for about 1-2 sec after click. During this time I see loading page header and old page content, it looks so irresponsive. All I want to achive is to display temporary Label with content like "Loading..." or activityindicator as long as new page is loading but I have no idea how to do this. What is the better way to achive this? Thanks in advance
Based on your comments, you just want to display Busy indicator while you are loading the Data from your Api. You didn't posted your UI but I hope you understand how to use the below:
<Grid>
<YOUR-UI>
.....
.........
</YOUR-UI>
<ActivityIndicator VerticalOptions="Center" IsVisible="{Binding IsBusy}" IsRunning="{Binding IsBusy}"/>
</Grid>
In your ViewModel, Add IsBusy Property as below:
private bool _isBusy;
public bool IsBusy
{
get{return _isBusy;}
set { _isBusy=value; RaisePropertyChanged(); }
}
Then when you are Calling the API to load your Data do something like:
public async void LoadDataFromApi()
{
IsBusy=true; //Show the Indicator
var response= await YourService.Method(); //this is where you calling your api
//do other stuffs if you need to do;
IsBusy=false; //Hide the Indicator
}
Let me know if you need anymore help.
Based on your comments on other answer - instead of ActivityIndicator - you could use a simple frame that covers all the layout and make it visible only when data are loading i.e. Binding IsVisible to IsBusy property. Then set IsBusy to true before doing the heavy load job and set back to false afterwards as shown in previous answer.
For instance, assuming your UI is based on a Grid layout (in this example 3 rows/2 columns):
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="auto" />
<RowDefinition Height="*" />
<RowDefinition Height="auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<!-- Some XAML for the grid content -->
<!-- Here, Frame covers all the possible space defined using RowSpan/ColumnSpan -->
<Frame Grid.RowSpan="3" Grid.ColumnSpan="2" IsVisible="{Binding IsBusy}" BackgroundColor="LightGray">
<Label VerticalOptions="Center" HorizontalOptions="Center" Text="Loading Samples..." />
</Frame>
</Grid>
-------------------------------------------------
EDIT based on OP comments:
Bases on the code you provided (better paste it in SO next time ;)), you could try this:
Add a grid layout and a frame to the xaml 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="MyApp.Namespace"
xmlns:tabView="clr-namespace:Syncfusion.XForms.TabView;assembly=Syncfusion.SfTabView.XForms"
xmlns:sfPopup="clr-namespace:Syncfusion.XForms.PopupLayout;assembly=Syncfusion.SfPopupLayout.XForms"
BackgroundColor="#d4d4d4">
<ContentPage.ToolbarItems>
<ToolbarItem Icon="search_white_90.png"/>
</ContentPage.ToolbarItems>
<ContentPage.Content>
<Grid>
<tabView:SfTabView x:Name="ProductsTabView" VisibleHeaderCount="3" TabHeaderBackgroundColor="#A74B40" Items="{Binding}">
<tabView:SfTabView.SelectionIndicatorSettings>
<tabView:SelectionIndicatorSettings
Color="#fff" StrokeThickness="3"/>
</tabView:SfTabView.SelectionIndicatorSettings>
</tabView:SfTabView>
<Frame x:Name="theFrame" BackgroundColor="White">
<Label VerticalOptions="Center" HorizontalOptions="Center" Text="Loading Samples..." />
</Frame>
</Grid>
</ContentPage.Content>
</ContentPage>
ProductsTabView and theFrame will cover the same space on screen and frame will be available in code behind. Then you can show/hide theFrame before/afrer the hard work using IsVisible property:
public partial class ProviderPage : ContentPage
{
public ProviderPage()
{
InitializeComponent();
theFrame.IsVisible = true; // Show the Frame on top of tabView
ProductsTabView.Items = GetTabItemCollection();
theFrame.IsVisible = false; // Hide the Frame
}
...
As a side note, you don't use ViewModel at all. I strongly recommand you to check about what a ViewModel is and how to use them with MVVM pattern. It will make your life easier in the long run using Binding!
HIH & Happy coding!
Try https://github.com/aritchie/userdialogs, It's make you easily to implement loading
for example:
using (this.Dialogs.Loading("Loading text"))
{
//Do something i.e: await Task.Delay(3000);
}
OR
this.Dialogs.ShowLoading("Loading text");
//Do something i.e: await Task.Delay(3000);
this.Dialogs.HideLoading();
OR
https://github.com/aritchie/userdialogs/blob/master/src/Samples/Samples/ViewModels/ProgressViewModel.cs
I have a project for advanced page transitions in Xamarin.Forms that may help you. Try this:
Install this nuget: http://www.nuget.org/packages/Xamarin.Forms.Segues (it is currently only prerelease, so you'll need to check the box to see it)
Add this sample to your project: https://github.com/chkn/Xamarin.Forms.Segues/blob/master/Sample/Shared/Custom%20Segues/SpinnerSegue.cs
In the DoWork method of SpinnerSegue add the code to load the data.
In your code that navigates to the other page, replace Navigiation.PushAsync(new MyNewPage()) with new SpinnerSegue().ExecuteAsync(this, new MyNewPage())

CarouselView-Xamarin Forms: Data is not getting displayed

I am trying to display data using carousel view in my forms application. I am not sure what am I missing but data is not getting populated, I have followed almost all available samples but still I have not found any success. Please help me get out of this problem:
I am using the following code in my xaml file:
<forms:CarouselView x:Name="listview" ItemsSource="postIdList" HeightRequest="200" Margin="5">
<forms:CarouselView.ItemTemplate>
<DataTemplate>
<ContentPage >
<StackLayout >
<Label Text="{Binding .}" FontSize="20" TextColor="Black" HorizontalOptions="Center" />
</StackLayout>
</ContentPage>
</DataTemplate>
</forms:CarouselView.ItemTemplate>
</forms:CarouselView>
And I am trying to populate the data in the following manner in .cs file:
List<string> postIdList = new List<string>
{
"Carousel", "Carousel", "carousel"
};
listview.ItemsSource = postIdList;
If I give a background colour for carousal view I am able to see that colour, but text is not getting displayed.
Please let me know my mistake.
In case of a ListView, you need an ObservableCollection instead of a List. Switch it around and see if it works?
On your XAML you are defining a <ContentPage> inside the <Datatemplate>. Replace the <ContentPage> with a <ViewCell> or a <Grid>
Here is a great example on how to use it, you need to keep in mind that CarouselView work like a ListView, it need to have a Datatemplate.

Categories