How to make a local ContentView bind a Label text - xamarin

I made a ContentView with an Expander. I want to duplicate this Xaml code multiple times in an other Xaml file. But I want for each Expander a different Binding.
I linked this code (ContentView code):
<?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:expandable="clr-namespace:Expandable;assembly=ExpandableView"
x:Class="Hello.ExpandableView">
<ContentView.Content>
<expandable:ExpandableView Padding="5">
<expandable:ExpandableView.PrimaryView>
<Frame BackgroundColor="White" Padding="5" CornerRadius="10">
<StackLayout Orientation="Horizontal">
<Label x:Name="label" FontSize="16" TextColor="Black" FontAttributes="Bold" Padding="5" HorizontalTextAlignment="Start" HorizontalOptions="StartAndExpand"/>
<Image Source="arrow.png" WidthRequest="25" HeightRequest="25" Margin="5"/>
</StackLayout>
</Frame>
</expandable:ExpandableView.PrimaryView>
<expandable:ExpandableView.SecondaryViewTemplate>
<DataTemplate>
<Frame BackgroundColor="White" Padding="5" CornerRadius="10">
<Label Text="{Binding Tip1Uitleg}" FontSize="15" TextColor="Black" Padding="5"/>
</Frame>
</DataTemplate>
</expandable:ExpandableView.SecondaryViewTemplate>
</expandable:ExpandableView>
</ContentView.Content>
</ContentView>
namespace Hello
{
public partial class ExpandableView : ContentView
{
public static BindableProperty LabelProperty =
BindableProperty.Create(nameof(Label),
typeof(string),
typeof(ExpandableView),
propertyChanged: (b, o, n) => (b as ExpandableView).OnLabelChanged());
private void OnLabelChanged()
{
label.Text = Label; //label is the x:Name of your Label control in ExpandableView.xaml
}
public string Label
{
get => (string)GetValue(LabelProperty);
set => SetValue(LabelProperty, value);
}
}
}
To this code:
This works:
<local:ExpandableView Label="Hello"/>
But I want this. This does not work:
<?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:expandable="clr-namespace:Expandable;assembly=ExpandableView"
xmlns:local="clr-namespace:Hello"
x:Class="Hello.Health.HealthDetail"
Title="{Binding Name}">
<ContentPage.Content>
<ScrollView>
<StackLayout>
<Image Source="{Binding Image}"/>
<Label Text="{Binding Tip1Uitleg}" FontSize="15" TextColor="Black" Padding="10, 5, 10, 5"/>
<local:ExpandableView Label="{Binding Tip1}"/> //This is what it is all about
</StackLayout>
</ScrollView>
</ContentPage.Content>
</ContentPage>
CodeBehind:
namespace Hello.Health
{
public partial class HealthDetail : ContentPage
{
public HealthDetail(HealthStrings healthStrings)
{
if (healthStrings == null)
throw new ArgumentNullException();
BindingContext = healthStrings;
InitializeComponent();
}
}
}
How do I make this work? I have to make this above more dynamic, but I do not know how.
BTW This also works:
<Label Text="{Binding Tip1}" />
I am sorry for the unclear explanation, I hope someone can help me.
Thank you for your time :)

You should not bind to 'this' since you are using a BindableProperty. Instead you should update the Label.Text property when the value of the BindableProperty changes.
public static BindableProperty LabelProperty =
BindableProperty.Create(nameof(Label),
typeof(string),
typeof(ExpandableView),
propertyChanged:(b, o, n) => (b as ExpandableView).OnLabelChanged());
private void OnLabelChanged()
{
label.Text = Label; //label is the x:Name of your Label control in ExpandableView.xaml
}
public string Label
{
get => (string)GetValue(LabelProperty);
set => SetValue(LabelProperty, value);
}
<?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="YourProject.ExpandableView">
<Grid>
<Label x:Name="label"/>
</Grid>
</ContentView>

Normally, when you want to binding a value for binableproperty, you could try the code below:
ExpandableView: I make a simple contentview with the same binableproperty for you reference.
<?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="XamarinDemo.Pages.ExpandableView">
<ContentView.Content>
<StackLayout>
<Label Text="Hello"></Label>
<Label x:Name="label"></Label>
</StackLayout>
</ContentView.Content>
</ContentView>
Code behind:
public partial class ExpandableView : ContentView
{
public static BindableProperty LabelProperty =
BindableProperty.Create(nameof(Label),
typeof(string),
typeof(ExpandableView),
propertyChanged: (b, o, n) => (b as
ExpandableView).OnLabelChanged());
private void OnLabelChanged()
{
label.Text = Label; //label is the x:Name of your Label control in ExpandableView.xaml
}
public string Label
{
get => (string)GetValue(LabelProperty);
set => SetValue(LabelProperty, value);
}
public ExpandableView()
{
InitializeComponent();
}
}
Usage:
<ContentPage.Content>
<local:ExpandableView Label="{Binding str}"></local:ExpandableView>
</ContentPage.Content>
Code Behind:
public partial class Page1 : ContentPage
{
public string str { get; set; }
public Page1()
{
InitializeComponent();
str = "okay";
this.BindingContext = this;
}
}

Related

Xamarin swipeview, when you swipe again, it returns to the position of the last. What is this?

If you swipe several times on an already opening field, then after closing the swiped element will return to the position from which the last swipe was.
For example:
I upload project for replay problem: link
XAML Code:
<?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="TestSwipeView.MainPage">
<CollectionView x:Name="collectionView"
ItemsSource="{Binding TestSequence}">
<CollectionView.ItemTemplate>
<DataTemplate>
<SwipeView>
<SwipeView.LeftItems>
<SwipeItems>
<SwipeItem Text="F"
BackgroundColor="LightGreen"/>
<SwipeItem Text="D"
BackgroundColor="LightPink"/>
</SwipeItems>
</SwipeView.LeftItems>
<Grid BackgroundColor="White"
Padding="10">
<Label Text="{Binding .}"></Label>
</Grid>
</SwipeView>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
</ContentPage>
Xaml.cs code:
namespace TestSwipeView
{
public partial class MainPage : ContentPage
{
public List<String> TestSequence { get; set; }
public MainPage()
{
TestSequence = new List<string>();
for (int i=0; i<10; i++)
{
TestSequence.Add(String.Format("Test {0}",i));
}
InitializeComponent();
this.BindingContext = this;
}
}
}

ZXing QRCode Scanner Xamarin

I tried to implement the package ZXing.Net.Mobile.Forms
this is my xaml:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:zxing="clr-namespace:ZXing.Net.Mobile.Forms;assembly=ZXing.Net.Mobile.Forms"
Title="{Binding Title}">
<ContentPage.Content>
<StackLayout>
<Label x:Name="scanResultText" />
<zxing:ZXingScannerView
OnScanResult="ScanViewOnScanResult"/>
</StackLayout>
</ContentPage.Content>
and this my xaml.cs :
public partial class QRCodePage : ContentPage
{
public QRCodePage()
{
InitializeComponent();
BindingContext = new QRCodeViewModel();
}
public void ScanViewOnScanResult(Result result)
{
Device.BeginInvokeOnMainThread(async () =>
{
scanResultText.Text = result.Text;
});
}
}
On my device i visualize the fragment but it seems not scanning
Try adding the following code (IsScanning="True") to your xaml:
<StackLayout>
<Label x:Name="scanResultText" />
<zxing:ZXingScannerView IsScanning="True"
OnScanResult="ScanViewOnScanResult"/>
</StackLayout>

How to open a page from a class?

I am trying to create a little application using MVVM. I would like to open a second page that scan a QR code and then back the value of the QR code to the main page.
I have this code:
Main view:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="QRScannerXamarinForms.Views.MainPageView"
xmlns:zxing="clr-namespace:ZXing.Net.Mobile.Forms;assembly=ZXing.Net.Mobile.Forms"
xmlns:vm="clr-namespace:QRScannerXamarinForms.ViewModels">
<ContentPage.BindingContext>
<vm:MainPageViewModel/>
</ContentPage.BindingContext>
<StackLayout>
<Frame BackgroundColor="#2196F3" Padding="24" CornerRadius="0">
<Label Text="Barcode Sample" HorizontalTextAlignment="Center" TextColor="White" FontSize="36" />
</Frame>
<Button x:Name="ucBtnEscanear" Text="Escanear"
Command="{Binding EscanearCommand}"/>
</StackLayout>
</ContentPage>
Main view model:
private INavigationService _navigationService;
public ICommand EscanearCommand { get; private set; }
private void Escanear()
{
string miCodigoQr = _navigationService.AbrirPaginaEscaner();
}
Second view:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="QRScannerXamarinForms.Views.ScannerView"
xmlns:zxing="clr-namespace:ZXing.Net.Mobile.Forms;assembly=ZXing.Net.Mobile.Forms"
xmlns:vm="clr-namespace:QRScannerXamarinForms.ViewModels">
<ContentPage.BindingContext>
<vm:ScannerViewModel/>
</ContentPage.BindingContext>
<ContentPage.Content>
<StackLayout>
<Frame BackgroundColor="#2196F3" Padding="24" CornerRadius="0">
<Label Text="Barcode Sample" HorizontalTextAlignment="Center" TextColor="White" FontSize="36" />
</Frame>
<Label x:Name="ucScanResultText" />
<zxing:ZXingScannerView x:Name="ucZXingScannerView" IsScanning="True" IsAnalyzing="True" Result="{Binding CodigoQr}" />
</StackLayout>
</ContentPage.Content>
</ContentPage>
Second view model:
class ScannerViewModel : BaseViewModel
{
private string _codigoQr;
public string CodigoQr
{
get { return _codigoQr; }
set { _codigoQr = value; }
}
}
Interface to open pages:
namespace QRScannerXamarinForms.Services
{
interface INavigationService
{
string AbrirPaginaEscaner();
}
}
Implementation of the interface that allows navigation.
namespace QRScannerXamarinForms.Services
{
class ViewNavigationService : INavigationService
{
public string AbrirPaginaEscaner()
{
ScannerView miScannerView = new ScannerView();
ScannerViewModel miScannerViewModel = miScannerView.BindingContext as ScannerViewModel;
Application.Current.MainPage.Navigation.PushModalAsync(miScannerView);
return miScannerViewModel.CodigoQr;
}
}
}
But in the implementation of the interface, the second page is not shown. So this line doesn't work:
Application.Current.MainPage.Navigation.PushModalAsync(miScannerView);
How could I open from this class?
Thanks.

Binding ICommand in Xamarin Forms Content View

I have a custom view which will be used across many pages. I have a Close Button in the Custom View where I need to bind the CloseButton Command in my MainViewModel. Any help would be appreciated.
Here is my HeaderView.xaml.cs file
public partial class HeaderView : ContentView
{
public HeaderView ()
{
InitializeComponent ();
}
public static readonly BindableProperty CloseButtonClickedProperty = BindableProperty.Create(nameof(CloseButtonClick), typeof(ICommand), typeof(HeaderView), null);
public ICommand CloseButtonClick
{
get => (ICommand)GetValue(CloseButtonClickedProperty);
set => SetValue(CloseButtonClickedProperty, value);
}
}
Here is my close button code used in HeaderView.Xaml file
<?xml version="1.0" encoding="UTF-8" ?>
<ContentView
HeightRequest="65"
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="NameSpace.Views.HeaderView" x:Name="headerView">
<Image x:Name="CloseButton" Source="ic_closewhite.png" WidthRequest="20" HeightRequest="20" VerticalOptions="CenterAndExpand" HorizontalOptions="EndAndExpand">
<Image.GestureRecognizers>
<TapGestureRecognizer Command="{Binding CloseButtonClick, Source={x:Reference headerView}}" />
</Image.GestureRecognizers>
</Image>
</ContentView>
Here is where I am trying to use the command in my MainView.xaml.
<c:HeaderView CloseButtonClick ="{Binding CloseButtonClickCommand}"/>
But it throws an error:
You are doing correct but only one small thing missing that is the x:Name of the ContentView HeaderView.
Just include this line of code in Xaml of HeaderView.
x:Name="headerView"
Here is your modified Xaml:-
<?xml version="1.0" encoding="UTF-8" ?>
<ContentView
HeightRequest="65"
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Name="headerView"
x:Class="NameSpace.Views.HeaderView" x:Name="headerView">
<Image x:Name="CloseButton" Source="ic_closewhite.png" WidthRequest="20" HeightRequest="20" VerticalOptions="CenterAndExpand" HorizontalOptions="EndAndExpand">
<Image.GestureRecognizers>
<TapGestureRecognizer Command="{Binding CloseButtonClick, Source={x:Reference headerView}}" />
</Image.GestureRecognizers>
</Image>
</ContentView>
public static readonly BindableProperty CloseButtonClickedProperty = BindableProperty.Create(nameof(CloseButtonClick), typeof(ICommand), typeof(HeaderView), null);
the issue is on this line,as the error message said,
event found for 'CloseButtonClick', or mismatching type between value
and property
you should change CloseButtonClickedProperty to CloseButtonClickProperty .
public static readonly BindableProperty CloseButtonClickedProperty = BindableProperty.Create(nameof(CloseButtonClick), typeof(ICommand), typeof(HeaderView), null);
public ICommand CloseButtonClick
{
get => (ICommand)GetValue(CloseButtonClickedProperty);
set => SetValue(CloseButtonClickedProperty, value);
}
to
public static readonly BindableProperty CloseButtonClickProperty = BindableProperty.Create(nameof(CloseButtonClick), typeof(ICommand), typeof(HeaderView), null);
public ICommand CloseButtonClick
{
get => (ICommand)GetValue(CloseButtonClickProperty);
set => SetValue(CloseButtonClickProperty, value);
}

Xamarin Forms Custom Control binding issue

I have a custom control that I supply a List<string> parameter and it draws a box for each string as a label.
Here is a version that is working. Clicking 'Add Tab' adds one tab at a time for each click.
I want to change the code so the List is converted into a different type of object and that is what the control displays.
First I show the code that is working for the image above. Then I show a changed version of the code that I am unable to get working. Hopefully for anyone answering this question, seeing the before code that works and the after code that doesn't work, you can easily spot the issue.
MainPage.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:local="clr-namespace:App7"
x:Class="App7.MainPage">
<StackLayout>
<BoxView HeightRequest="100" />
<local:CustomControl
SideTabs="{Binding MainNavigationTabs}"
/>
<Button Text="Add Tab" Command="{Binding AddTab}" />
</StackLayout>
</ContentPage>
MainPageModel.cs
public class MainPageModel : FreshBasePageModel
{
public MainPageModel() { }
public List<string> MainNavigationTabs { get; set; }
private int _index = 0;
public Command AddTab
{
get
{
return new Command(() =>
{
_index++;
var tabs = new List<string>();
for (var index = 1; index <= _index; index++)
{
tabs.Add($"Tab {index}");
}
MainNavigationTabs = tabs;
});
}
}
}
CustomControl.xaml
<ContentView xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="App7.CustomControl"
BackgroundColor="Beige"
x:Name="this">
<ContentView.Content>
<StackLayout>
<StackLayout Orientation="Vertical"
BindableLayout.ItemsSource="{Binding Source={x:Reference this}, Path=SideTabs}">
<BindableLayout.ItemTemplate>
<DataTemplate>
<ContentView WidthRequest="237"
Margin="0"
BackgroundColor="Blue"
Padding="10">
<Label Text="{Binding .}" TextColor="White" />
</ContentView>
</DataTemplate>
</BindableLayout.ItemTemplate>
</StackLayout>
</StackLayout>
</ContentView.Content>
</ContentView>
CustomControl.xaml.cs
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class CustomControl : ContentView
{
public CustomControl()
{
InitializeComponent();
}
public static readonly BindableProperty SideTabsProperty = BindableProperty.Create(
propertyName: "SideTabs",
returnType: typeof(List<string>),
declaringType: typeof(CustomControl),
defaultBindingMode: BindingMode.OneWay,
defaultValue: new List<string>());
public List<string> SideTabs
{
get { return base.GetValue(SideTabsProperty) as List<string>; }
set { base.SetValue(SideTabsProperty, value); }
}
}
I changed the CustomControl to transform the List<string> to a List<SideTab> object and have the control bind to that. Here's the code...
CustomControl.xaml.cs
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class CustomControl : ContentView
{
public CustomControl()
{
InitializeComponent();
}
public static readonly BindableProperty SideTabsProperty = BindableProperty.Create(
propertyName: "SideTabs",
returnType: typeof(List<string>),
declaringType: typeof(CustomControl),
defaultBindingMode: BindingMode.OneWay,
defaultValue: new List<string>());
public List<string> SideTabs
{
get
{
var tabs = new List<string>();
foreach (var tab in _SideTabs)
{
tabs.Add(tab.Text);
}
return tabs;
}
set
{
var tabs = new List<SideTab>();
foreach (var tab in value)
{
tabs.Add(new SideTab() { Text = tab });
}
_SideTabs = tabs;
}
}
public List<SideTab> _SideTabs
{
get { return base.GetValue(SideTabsProperty) as List<SideTab>; }
set { base.SetValue(SideTabsProperty, value); }
}
}
public class SideTab
{
public string Text { get; set; }
}
CustomControl.xaml
<ContentView xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="App7.CustomControl"
BackgroundColor="Beige"
x:Name="this">
<ContentView.Content>
<StackLayout>
<StackLayout Orientation="Vertical"
BindableLayout.ItemsSource="{Binding Source={x:Reference this}, Path=_SideTabs}">
<BindableLayout.ItemTemplate>
<DataTemplate>
<ContentView WidthRequest="237"
Margin="0"
BackgroundColor="Blue"
Padding="10">
<Label Text="{Binding Text}" TextColor="White" />
</ContentView>
</DataTemplate>
</BindableLayout.ItemTemplate>
</StackLayout>
</StackLayout>
</ContentView.Content>
</ContentView>
Notice the addition of a property _SideTabs. When SideTabs is set, it transforms the List<string> into a List<SideTab>.
How can I make this work? Here is the result from the above code changes...
Try like this,
public static readonly BindableProperty TabsListProperty = BindableProperty.Create(nameof(TabsList), typeof(List<TabItem>), typeof(ScrollableTabs), null, propertyChanged: (bindable, oldValue, newValue) =>
{
((ScrollableTabs)bindable).InitializeTabs();
});
private void InitializeTabs()
{
//Write your logic here
}
public List<TabItem> TabsList
{
get
{
return (List<TabItem>)GetValue(TabsListProperty);
}
set
{
SetValue(TabsListProperty, value);
}
}

Resources