BindableProperty for Image Source in contentview - xamarin

Im trying to bind DogImage source which in my Contentview. Im trying to build reusable view objects like frame buttons. I just need to give them Image and Text from outside of contentview. Im using Images from resources folder.
this is my contentView
<ContentView xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="PawsApp.Views.AboutMyDogViews.DogsBreedPicker"
BindingContext="{Binding .}">
<ContentView.Content>
<StackLayout x:Name="breedStack" Margin="30,2,30,2">
<Frame HeightRequest="64" CornerRadius="8" BorderColor="White" HasShadow="False" BackgroundColor="White"
VerticalOptions="FillAndExpand" Padding="18,0,18,0">
<Frame.Content>
<StackLayout Orientation="Vertical" VerticalOptions="CenterAndExpand">
<Grid ColumnSpacing="18">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*"></ColumnDefinition>
<ColumnDefinition Width="9*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Image x:Name="DogImage" Source="{Binding ImageSourceOf}" Grid.Row="0" Grid.Column="0" ></Image>
<Label x:Name="breedSelector" Text="Breed" FontSize="Medium" TextColor="#5f5d70" Grid.Row="0" Grid.Column="1">
</Label>
</Grid>
</StackLayout>
</Frame.Content>
</Frame>
</StackLayout>
</ContentView.Content>
</ContentView>
And this is CS file
public partial class DogsBreedPicker : ContentView
{
public static readonly BindableProperty ImageSourceProperty =
BindableProperty.Create("ImageSourceOf", typeof(ImageSource), typeof(DogsBreedPicker));
public ImageSource ImageSourceOf
{
get { return GetValue(ImageSourceProperty) as ImageSource; }
set { SetValue(ImageSourceProperty, value);}
}
public DogsBreedPicker()
{
InitializeComponent ();
BindingContext = ImageSourceOf;
}
}
And this is how i want to use it.
<views:DogsBreedPicker ImageSourceOf="dog" TextOf="Breed Selector" x:Name="DogBreed" ></views:DogsBreedPicker>

You are setting your BindingContext to ImageSourceOf in your constructor. Anyway, the properties are not set at this moment, hence ImageSourceOf is still null. However, you do not need to use BindingContext in this case anyway, since you can directly bind to the ImageSourceOf property:
<ContentView xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="PawsApp.Views.AboutMyDogViews.DogsBreedPicker"
x:Name="View">
<!-- Elided all the other stuff -->
<Image x:Name="DogImage" Source="{Binding ImageSourceOf, Source={x:Reference View}}" Grid.Row="0" Grid.Column="0" />
</ContentView>
remove the assignment of BindingContext from your constructor.
Why does this work?
The default source of all bindings is the BindingContext (of the view, which is propagated to all child views). Anyway, you are by no means restricted to the BindingContext as the source of a binding, but can set the Source of the binding to another object. In this case we are referencing the view (which we have given the name View with x:Name="View") by its name and use it as the source of the binding of the Source of the Image. Since the Path of the binding is ImageSourceOf, the Image.Source will be bound to View.ImageSourceOf.

Content.BindingContext = this;
Solved my issue. But is there any way to make it better? Add comment please.

Related

How to place focus on my custom input , I am using mvvm , prism

One query I have an entry in a contentview to be able to call it from another page.
the question is how to locate the focus when I click on a button
<ContentView xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:xct="http://xamarin.com/schemas/2020/toolkit"
x:Class="MovilERP.Controls.SacTextBox"
x:Name="this">
<Grid RowDefinitions="Auto,Auto">
<!--#region Entry Normal-->
<Grid RowDefinitions="Auto,1"
ColumnDefinitions="*,Auto" RowSpacing="0">
<Entry x:Name="txtBox"
Margin="{OnPlatform Android=0 , iOS=5}"
Text="{Binding Text, Source={x:Reference this}}"
PlaceholderColor="{DynamicResource PlaceHolderColorEntry}"
Style="{StaticResource EstiloEntry}"
IsPassword="{Binding ShowIcoPassword, Source={x:Reference this}}"
IsSpellCheckEnabled="{Binding IsCorrector, Source={x:Reference this}}"
IsTextPredictionEnabled="True">
</grid>
</grid>
</Conteview>
this is how i invoke it from another page
<sofadControls:SacTextBox
x:Name="Descripcion"
Grid.Column="1"
Placeholder="Descripción"
SacAlineacion="Start"
SacValidacion="AlfaNumerico"
Text="{Binding Source={RelativeSource AncestorType={x:Type dialogos:FrmArticulosGeneralViewModel}}, Path=DetalleActual.Descripcion}"/>
In this way I try to invoke the focus but it does not work
Descripcion.Focus();
you need to create a public method in your control
public void Focus()
{
txtBox.Focus();
}
then you can call that public Focus method from the oage that contains your control

Converting CarouselPage to CarouselView

Is it possible to add direct content to a CarouselView in Xaml?
I need to convert an old CarouselPage to the newer CarouselView, but it looks like it must be data bound. But my content for each slide is entirely different and does not easily lend itself to an item template.
<?xml version="1.0" encoding="utf-8" ?>
<CarouselPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="myCarousel"
x:Name="carouselPage"
NavigationPage.HasNavigationBar="false" BackgroundColor="#000000">
<ContentPage BackgroundColor="#000000">
<StackLayout BackgroundColor="#000000">
<Label Text="Lorem ipsum" />
<ImageButton Margin="0,20" Source="btn_continue" HorizontalOptions="Center" HeightRequest="30" Clicked="Go_Right" />
</StackLayout>
</ContentPage>
<ContentPage BackgroundColor="#000000">
<StackLayout BackgroundColor="#000000">
<Button Text="X" TextColor="White" BackgroundColor="Transparent" VerticalOptions="Start" HorizontalOptions="End" Clicked="Go_Home"></Button>
<ImageButton Source="slider-2" Aspect="AspectFill" HorizontalOptions="FillAndExpand" VerticalOptions="Start" />
<Grid Margin="0,20" VerticalOptions="Start">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<ImageButton Source="btn_back" HorizontalOptions="Center" HeightRequest="30" Grid.Column="0" Clicked="Go_Left" />
<ImageButton Source="btn_close" HorizontalOptions="Center" HeightRequest="30" Grid.Column="1" Clicked="Go_Home" />
</Grid>
</StackLayout>
</ContentPage>
</CarouselPage>
I need to create a multi-slide carousel, where each slide has different content like above. How would I convert that using CarouselViews?
The problem I am having with the CarouselPage is that it does not always behave consistently, which is why I imagine they are slowly deprecating it.
But my content for each slide is entirely different and does not
easily lend itself to an item template.
You need to create different DataTemplate for each slide and Choose item appearance at runtime, for example:
<ContentPage ...
xmlns:controls="clr-namespace:CarouselViewDemos.Controls"
x:Class="CarouselViewDemos.Views.HorizontalLayoutDataTemplateSelectorPage">
<ContentPage.Resources>
<DataTemplate x:Key="StyleATemplate">
...
</DataTemplate>
<DataTemplate x:Key="StyleBTemplate">
...
</DataTemplate>
<controls:ContentSelector x:Key="myContentSelector"
AmericanMonkey="{StaticResource StyleATemplate}"
OtherMonkey="{StaticResource StyleBTemplate}" />
</ContentPage.Resources>
<CarouselView ItemsSource="{Binding Monkeys}"
ItemTemplate="{StaticResource myContentSelector}" />
</ContentPage>
And in code behind, use DataTemplateSelector to select different template for each slide:
public class ContentSelector: DataTemplateSelector
{
public DataTemplate StyleATemplate{ get; set; }
public DataTemplate StyleBTemplate{ get; set; }
protected override DataTemplate OnSelectTemplate(object item, BindableObject container)
{
if(item.style == A){
return StyleATemplate;
}else{
return StyleBTemplate;
}
}
}
Yes of course you can add different content on every view.
It's necessary to create a CustomViewSelector.
Check my GitHub for a full sample code https://github.com/georgemichailou/ShaXam
Pay attention to these files
https://github.com/georgemichailou/ShaXam/blob/master/ShaXam/UI%20Helper/CustomViewSelector.cs
https://github.com/georgemichailou/ShaXam/blob/master/ShaXam/MainPage.xaml
https://github.com/georgemichailou/ShaXam/blob/master/ShaXam/MainPage.xaml.cs (Line 17-25-27)

Control template Binding Not working in MVVM Cross Xamarin Forms

I am currently working on a Xamarin Forms project which is using MVVMCross. In App.xaml i created a control template as follows
<ControlTemplate x:Key="ContentPageTemplate">
<Grid HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand" RowSpacing="0" RowDefinitions="Auto,Auto,*">
<!--StackLayout as Header-->
<StackLayout Grid.Row="0" HeightRequest="6" BackgroundColor="{StaticResource SecondaryBrandColor}"></StackLayout>
<Frame Padding="0" Grid.Row="1" >
<Label Grid.Row="1" Text="Server Not Available" BindingContext="{TemplateBinding BindingContext}"
Style="{StaticResource ServerAvailableLabel}"
IsVisible="{TemplateBinding Parent.BindingContext.IsServerAvailableLabelVisible}"/>
</Frame>
<!--Content Page Body-->
<ContentPresenter Grid.Row="2" BackgroundColor="{StaticResource PrimaryBodyColor}">
</ContentPresenter>
</Grid>
</ControlTemplate>
Now i am using this control template in contentPage as follows
<views:MvxContentPage xmlns:views="clr-namespace:MvvmCross.Forms.Views;assembly=MvvmCross.Forms"
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Project.UI.Views.ReqView"
ControlTemplate="{StaticResource ContentPageTemplate}"
Title="Reqs">
</views:MvxContentPage>
In the view model i am trying to make the label visible and invisible as follows
public bool _isServerAvailableLabelVisible = false;
public bool IsServerAvailableLabelVisible
{
get
{
return _isServerAvailableLabelVisible;
}
set
{
SetProperty(ref _isServerAvailableLabelVisible, value);
}
}
The problem i am facing now is, by default the label is being visible and also it is not updating even when i am setting the value as false.
What am i missing ?
I had exactly the same issue !
After one day I found the following solution which works for me.
I added BindingContext="{TemplateBinding BindingContext.DataContext}"into the Grid and change the IsVisible property by IsVisible="{Binding IsServerAvailableLabelVisible}"
Here the recap that you can try, hope it will work for you too !
For information I use Xamarin.Forms 4.8.0 & MvvmCross 6.3.1
<ControlTemplate x:Key="ContentPageTemplate">
<Grid HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand" RowSpacing="0" RowDefinitions="Auto,Auto,*"
BindingContext="{TemplateBinding BindingContext.DataContext}">
<!--StackLayout as Header-->
<StackLayout Grid.Row="0" HeightRequest="6" BackgroundColor="{StaticResource SecondaryBrandColor}"></StackLayout>
<Frame Padding="0" Grid.Row="1" >
<Label Grid.Row="1" Text="Server Not Available"
Style="{StaticResource ServerAvailableLabel}"
IsVisible="{Binding IsServerAvailableLabelVisible}"/>
</Frame>
<!--Content Page Body-->
<ContentPresenter Grid.Row="2" BackgroundColor="{StaticResource PrimaryBodyColor}">
</ContentPresenter>
</Grid>
</ControlTemplate>
Are you sure that the OnPropertyChanged Event gets fired when you chcange the value?
If yes then you need to check the binding context of the view
If no you need to fire it manually (as mentioned in the MVVMCross documentation)
private string _myProperty;
public string MyProperty
{
get => _myProperty;
set
{
_myProperty = value;
RaisePropertyChanged(() => MyProperty);
// take any additional actions here which are required when MyProperty is updated
}
}

Can I set up a template in Xamarin Forms for Xaml that's repetitive?

I use the same XAML many times in my application with two minor differences which are the value of the Text and Selected that I pass in:
<ViewCell Tapped="selectValue" >
<Grid VerticalOptions="CenterAndExpand" Padding="20,0" >
<local:StyledLabel Text="{Binding [1].Name}" HorizontalOptions="StartAndExpand" />
<local:StyledLabel IsVisible="{Binding [1].IsSelected}" TextColor="Gray" HorizontalOptions="End" Text="✓" />
</Grid>
</ViewCell>
Does Xamarin forms have any template feature where I could for example shorten this to something like:
<local:SwitchViewCell Text="{Binding [1].Name}" Selected="{Binding [1].IsSelected}" />
Here's what I have so far:
<?xml version="1.0" encoding="utf-8" ?>
<ViewCell xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
mlns:local="clr-namespace:Japanese;assembly=Japanese"
x:Class="Japanese.SwitchViewCell""
Tapped="selectValue" >
<Grid VerticalOptions="CenterAndExpand" Padding="20,0" >
<local:StyledLabel Text="{Binding Text, Source={x:Reference this}}" HorizontalOptions="StartAndExpand" />
<local:StyledLabel IsVisible="{Binding IsVisible, Source={x:Reference this}}" TextColor="Gray" HorizontalOptions="End" Text="✓" />
</Grid>
</ViewCell>
With this code behind right now:
namespace Japanese.Templates
{
public partial class SwitchViewCell : ViewCell
{
public SwitchViewCell()
{
InitializeComponent();
}
public static readonly BindableProperty TextProperty = BindableProperty.Create(nameof(Text), typeof(string), typeof(SwitchViewCell));
public static readonly BindableProperty IsVisibleProperty = BindableProperty.Create(nameof(IsVisible), typeof(bool), typeof(SwitchViewCell));
public string Text
{
get
{
return (string)GetValue(TextProperty);
}
set
{
SetValue(TextProperty, value);
}
}
public bool IsVisible
{
get
{
return (bool)GetValue(IsVisibleProperty);
}
set
{
SetValue(IsVisibleProperty, value);
}
}
}
}
I'm not sure if this is 100% the way to go but when I try to implement this I get the message:
EventHandler "selectValue" not found in type "Japanese.Templates.SwitchViewCell" (Japanese)
ListView Cell:
For the ListView cell, you can define the ViewCell layout of ListView item.
Ex. PersonCell.xaml
<ViewCell xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="DataTemplates.PersonCell">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="0.3*" />
<ColumnDefinition Width="0.3*" />
<ColumnDefinition Width="0.4*" />
</Grid.ColumnDefinitions>
<Label Text="{Binding FirstName}" />
<Label Grid.Column="1" Text="{Binding LastName}" />
<Label Grid.Column="2" Text="{Binding Email}" />
</Grid>
</ViewCell>
Then you can use this into ListView's DataTemplate as:
<ListView ItemSource="{Binding PersonList}">
<ListView.ItemTemplate>
<DataTemplate>
<local:PersonCell />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
This way ListView resuses the cell design for each item.
Reusable View:
You can also make a reusable view which can be simply included in your page.
Ex. MyCustomView.xaml:
<Grid xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="Xam.Control.MyCustomView">
<StackLayout>
<Label Text="Hello"/>
<Label Text="How Are You?"/>
</StackLayout>
</Grid>
Page:
<ContentPage
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:customView="clr-namespace:Xam.Control;assembly=Xam"
x:Class="Xam.View.HomePage">
Here, notice that you have to include the namespace and assembly where your custom view resides.
And then in this page, simply add it like:
<customView:MyCustomView />
Sure, just put it in a separate XAML file and define a the bindable properties that you need and map the values onto the controls in your custom control. In fact, the latter is not absolutely needed but is nicer to make it completely reusable.
If you just want to reuse it in your project and always data bind it to the same fields, you can leave it as-is, as the BindingContext will be inherited.
To get you started, you might want to have a look at a blog post of mine about this: https://blog.verslu.is/xamarin/xamarin-forms-xamarin/reusable-custom-usercontrols-with-bindableproperty/

Xamarin Get ListItem Context

I've got a user control that I'm including in a ListView and I want to add dynamic content when the user control initialises based on the listitem binding. I'm not sure how to do this. See the "HOW DO I BIND THIS???"... I presume I should binding my PropertyBinding to the listitem somehow?
Here's my original view with my listview
<?xml version="1.0" encoding="UTF-8"?>
<TabbedPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:local="clr-namespace:EngineerApp" x:Class="EngineerApp.GigsPage" NavigationPage.HasNavigationBar="false" xmlns:controls="clr-namespace:XLabs.Forms.Controls;assembly=XLabs.Forms">
<ContentPage Title="Gigs" Icon="icon.png">
<StackLayout Orientation="Vertical">
<local:HeaderBar LeftButtonText="Back" RightButtonText="Leave" LeftButtonClickEvent="Back" RightButtonClickEvent="Back"></local:HeaderBar>
<local:ButtonBar LeftButtonText="Add Gig" RightButtonText="Month View" LeftButtonClickEvent="AddGig" RightButtonClickEvent="Back"></local:ButtonBar>
<ActivityIndicator IsRunning="{Binding IsBusy}" IsVisible="{Binding IsBusy}"></ActivityIndicator>
<ListView HasUnevenRows="true" SeparatorVisibility="Default" ItemsSource="{Binding gigs}" ItemSelected="Handle_ItemSelected">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Frame Padding="20,20,20,20">
<Frame.Content>
<Frame Padding="15,15,15,15" OutlineColor="Gray" BackgroundColor="White">
<Frame.Content>
<StackLayout Padding="20,0,0,0" Orientation="Vertical" HorizontalOptions="CenterAndExpand">
<Label Text="{Binding venue}"
HorizontalTextAlignment="Center"
TextColor="#69add1"/>
<Label Text="{Binding date}"
HorizontalTextAlignment="Center"
FontFamily="OpenSans-Light"
FontSize="9"
TextColor="#69add1"/>
<local:AuthorisationBar SelectedGig="{Binding .}"></local:AuthorisationBar>
</StackLayout>
</Frame.Content>
</Frame>
</Frame.Content>
</Frame>
</ViewCell>
<local:GigCard />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
</ContentPage>
</TabbedPage>
And then here's the user control 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="EngineerApp.AuthorisationBar">
<ContentView.Content>
<StackLayout BackgroundColor="Red" HeightRequest="50" x:Name="barcontent" Orientation="Horizontal" HorizontalOptions="FillAndExpand">
<Label HorizontalOptions="FillAndExpand" VerticalOptions="Center" Text="I want to show some data here once the bindings are working"></Label>
</StackLayout>
</ContentView.Content>
</ContentView>
And here's the code behind for the user control
using System;
using System.Collections.Generic;
using EngineerApp.ViewModels;
using Xamarin.Forms;
namespace EngineerApp
{
public partial class AuthorisationBar : ContentView
{
public static readonly BindableProperty GigProperty = BindableProperty.Create("SelectedGig", typeof(GigViewModel), typeof(AuthorisationBar), new GigViewModel());
public GigViewModel SelectedGig
{
get
{
return (GigViewModel)GetValue(GigProperty);
}
set
{
SetValue(GigProperty, value);
}
}
public AuthorisationBar()
{
InitializeComponent();
BindingContext = this;
}
}
}
UPDATE 1 - Updated all pages to reflect most recent suggestion. With the {Binding .} I now get the error stated below:
"Cannot assign property "SelectedGig": Property does not exists, or is not assignable, or mismatching type between value and property"
Try this:
<local:AuthorisationBar SelectedGig="{Binding .}"></local:AuthorisationBar>
where "." will be the "current" item of the list.
Notice I used SelectedGid in my code as this is the name of the property you defined your custom control, not SelectedGigId.
Also you need to remove this line from the constructor of your custom control:
BindingContext = SelectedGig;
The BindingContext will already be of type GigViewModel
Hope this helps!
UPDATE
Your Custom Control code behind should look:
public partial class AuthorisationBar : ContentView
{
public AuthorisationBar()
{
InitializeComponent();
}
public static readonly BindableProperty SelectedGigProperty = BindableProperty.Create(nameof(SelectedGig), typeof(GigViewModel), typeof(AuthorisationBar), null);
public GigViewModel SelectedGig
{
get
{
return (GigViewModel)GetValue(SelectedGigProperty);
}
set
{
SetValue(SelectedGigProperty, value);
}
}
}
As previously stated you don't need to set anything to the BindingContext
UPDATE 2
In response to your question. The error was that the BindableProperty of your custom control was not complying with the naming conventions requirements.
From Xamarin BindableProperty documentation:
The naming convention for bindable properties is that the bindable property identifier must match the property name specified in the Create method, with "Property" appended to it.
This is why updating your BindableProperty from GigProperty to SelectedGigProperty fixed your issue.
I've done something similar for my current project - passing the current item's ID to my ViewModel (from the code you've posted, it seems you're not using proper separation of concerns under the MVVM paradigm - the VM shouldn't be initialising anything in your view):
<Button Text="Reconnect" Command="{Binding Path=BindingContext.ReconnectCommand, Source={x:Reference Name=ServicesList}}" CommandParameter="{Binding}" VerticalOptions="Center"/>
For reference, the button which is passing the command is embedded in a ListView.DataTemplate, and is passing the ID of the list item it's part of

Resources