Reference element from a ContentView within the ContentView - xaml

Within my ContentView (name=CvProduct) I have a ListView (nam=LvProducts) and several controls within each cells with their specific commands that are accessible in the Page's (name=Products) viewmodel (name=ProductsViewModel).
I cannot manage to make the bind works.
<ContentPage x:Name="Products">
<ContentPage.Resources>
<ResourceDictionary>
<DataTemplate x:Key="ContentTemplate">
<views:CvProduct/>
</DataTemplate>
</ResourceDictionary>
</ContentPage.Resources>
</ContentPage>
<ContentView x:Name="CvProduct">
<xForms:SfListView x:Name="LvProducts">
<xForms:SfListView.ItemTemplate>
<DataTemplate>
<forms:SvgCachedImage.GestureRecognizers>
<TapGestureRecognizer Command="{Binding BindingContext.ConsultElement,
Source={x:Reference Name=LvProducts}}" CommandParameter="{Binding .}">
</forms:SvgCachedImage.GestureRecognizers>
</DataTemplate>
</xForms:SfListView.ItemTemplate>
</xForms:SfListView>
</ContentView>
which always returns this error :
Message = "Position xyz Can not find the object referenced by LvProducts"

Reference a command MyCommand in the binding context of a page named myPage:
Command="{Binding Source={x:Reference Name=myPage},
Path=BindingContext.MyCommand}"
Reference a command MyCommand in the binding context of a layout named myLayout containing a ListView:
Command="{Binding Source={x:Reference Name=myLayout},
Path=BindingContext.MyCommand}"

Related

Can't use ObservableCollection in a Grid?

I am currently working with a Xamarin app for reading RFID temperature tags and updating their values in real time. The current setup uses two Horizontal StackLayouts, one with a ListView showing a list of tags and their values, and the other with a Grid of Images of the human body that I wish to display the values on as well (in specific places).
In the ListView side, I can successfully call an object called TagInfoList, which is an ObservableCollection in the Class I call from the BasePage initialization. However, on the Grid side, I've tried multiple methods of using the TagInfoList, but it does not work. I've tried BindableLayouts, DataTemplates + ViewCells, and none allow me to get TagInfoList usable within my second StackLayout.
Here is the XAML code for the two horizontal StackLayouts:
<StackLayout Orientation="Horizontal" HorizontalOptions="StartAndExpand">
<ListView x:Name="liewViewTagData" ItemsSource="{Binding TagInfoList}" SelectedItem="{Binding objItemSelected, Mode=TwoWay}">
<ListView.Header>
<StackLayout BackgroundColor="#cccccc">
<Grid>
<!-- Grid + Label code irrelevant to my issue-->
</Grid>
</StackLayout>
</ListView.Header>
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout Orientation="Vertical">
<Grid>
<!-- Grid + Label code irrelevant to my issue-->
</Grid>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout> <!-- RFID Tag Section -->
<StackLayout Orientation="Horizontal" HorizontalOptions="EndAndExpand"> <!-- Body Model Section -->
<!-- HERE: WHAT TO PUT FOR TagInfoList TO BE USABLE? -->
<Grid VerticalOptions="FillAndExpand" HorizontalOptions="FillAndExpand" WidthRequest="400" ColumnSpacing="0">
<!-- Grid + Label + Images I want to use TagInfoList information in -->
</Grid>
<!-- HERE: WHAT TO PUT FOR TagInfoList TO BE USABLE? -->
</StackLayout> <!-- Body Model Section -->
</StackLayout>```
I've deleted the bulk of inside Grids since it's not relevant. TagInfoList is an ObservableCollection of a class which contains all the data I need (strings and ints). How do I utilize TagInfoList within the second StackLayout? If I do it the same method as the first StackLayout, I get an error that I've called TagInfoList twice (through the 'liewViewTagData' item). Here is that code in the .xaml.cs file:
liewViewTagData.ItemSelected += (sender, e) => {
if (e.SelectedItem == null) return; // Don't do anything if we just de-selected the row
((ListView)sender).SelectedItem = null; // De-select the row
};
My sole goal is to use the TagInfoList ObservableCollection within both StackLayouts, but I'm not sure how to do that.
Thanks!
If I do it the same method as the first StackLayout, I get an error
that I've called TagInfoList twice (through the 'liewViewTagData'
item).
You can do it in the same way as the first StackLayout.And you can change property x:Name of the two listviews to different Name.
You can refer to the following code:
<ScrollView Orientation="Horizontal">
<StackLayout Orientation="Horizontal" HorizontalOptions="StartAndExpand">
<StackLayout Orientation="Horizontal" HorizontalOptions="EndAndExpand">
<ListView x:Name="liewViewTagData" RowHeight="60" ItemsSource="{ Binding TagInfoList}">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<!--other code-->
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
<StackLayout Orientation="Horizontal" HorizontalOptions="EndAndExpand">
<ListView x:Name="liewView2" RowHeight="60" ItemsSource="{ Binding TagInfoList}">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<!--other code-->
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
</StackLayout>
</ScrollView>
Note:
If you run out of horizontal space, I suggest you add a ScrollView to the outer layer.

Reuse listview templates

I am working on a Xamarin.Forms project. It has multiple pages with ListViews that display content that requires templates. How can I easily reuse one template thought the whole project because right now I have to update the templates on each page everytime I change something in one.
I have created a template as such in each page's xaml
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:*snip*.Renderers;assembly=*snip*"
x:Class="*snip*.MainPage">
<ContentPage.Resources>
<ResourceDictionary>
<DataTemplate x:Key="template1">
<local:PostViewCell>
<StackLayout BackgroundColor="White" Margin="10, 10, 10, 10" Padding="10, 10, 10, 10">
. . .
</StackLayout>
</local:PostViewCell>
</DataTemplate>
<DataTemplate x:Key="template2">
<local:PostViewCell>
<StackLayout BackgroundColor="White" Margin="10, 10, 10, 10" Padding="10, 10, 10, 10">
. . .
</StackLayout>
</local:PostViewCell>
</DataTemplate>
<local:PostTemplateSelector x:Key="templateSelector"
Template1="{StaticResource template1}"
Template2="{StaticResource template2}" ></local:PostTemplateSelector>
</ResourceDictionary>
</ContentPage.Resources>
<ContentPage.Content>
<StackLayout>
<local:PostListView x:Name="PostListView" ItemTemplate="{StaticResource templateSelector}"/>
</StackLayout>
</ContentPage.Content>
Define your Datatemplate with Unique key in App.Xaml Like,
<Application xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="YourAppName.App">
<Application.Resources>
<ResourceDictionary>
<DataTemplate x:Key="lstTemplate">
<ViewCell>
<------Your Design here------->
</ViewCell>
</DataTemplate>
</ResourceDictionary>
</Application.Resources>
</Application>
Use that lstTemplate key in your Listview Like,
<ListView x:Name=lst"
ItemsSource="{Binding Name}"
SeparatorColor="#0094FF" ItemTemplate="{StaticResource lstTemplate}">
You can define the DataTemplate as a resource in App.xaml and refer to it from individual XAML pages

Xamarin.Forms - x:Reference 2nd level

I'm currently working in a new ContentPage for my application and I have some controls with DataTemplates.
I would like to use a Command in my ContentPage's ViewModel in one of the DataTemplates, however I'm not sure how to do the right reference for this to work properly. Here's my 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="MaverickMobileOnline.ImagesListingPage"
--MANY NAMESPACE REFERENCES--
>
<ContentPage.Resources>
<ResourceDictionary>
<c:ItemTappedEventArgsConverter x:Key="ItemTappedConverter" />
<c:ItemAppearingEventArgsConverter x:Key="ItemAppearingConverter" />
<c:BooleanNegationConverter x:Key="not" />
</ResourceDictionary>
</ContentPage.Resources>
<StackLayout Spacing="0">
<commonViews:MainCustomNavBar MinimumHeightRequest="120" />
<controls:PullToRefreshLayout
x:Name = "layout"
RefreshCommand="{Binding btn_reload_businesses_images_click}"
IsEnabled = "True"
IsRefreshing="{Binding Path=is_businesses_loading}" >
<ScrollView
x:Name = "scrollView"
HorizontalOptions="FillAndExpand"
VerticalOptions="FillAndExpand">
<templates:ItemsStack
Padding="0"
Margin="0,10,0,10"
x:Name="itmStack"
BackgroundColor="White"
ItemsSource="{Binding Path=photos_list}">
<templates:ItemsStack.ItemTemplate>
<DataTemplate>
<artina:GridOptionsView
Padding="10,0"
ColumnSpacing="10"
RowSpacing="10"
VerticalOptions="Fill"
HeightRequest="120"
ColumnCount="3"
RowCount="1"
ItemsSource="{Binding Path=.}">
<artina:GridOptionsView.ItemTemplate>
<DataTemplate>
<ContentView
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="MaverickMobileOnline.GalleryImageItemTemplate"
xmlns:ffimageloading="clr-namespace:FFImageLoading.Forms;assembly=FFImageLoading.Forms"
xmlns:fftransformations="clr-namespace:FFImageLoading.Transformations;assembly=FFImageLoading.Transformations">
<ContentView.Content>
<ffimageloading:CachedImage
FadeAnimationEnabled="true"
Aspect="AspectFill"
VerticalOptions="FillAndExpand"
HorizontalOptions="FillAndExpand"
LoadingPlaceholder="advertising_photo_placeholder.png"
Source="{Binding Path=image_medium}" />
</ContentView.Content>
<ContentView.GestureRecognizers>
<TapGestureRecognizer
-- HERE! --
Command="{Binding Source={x:Reference X}, Path=BindingContext.command_name}"
CommandParameter="{Binding image_medium}"
/>
</ContentView.GestureRecognizers>
</ContentView>
</DataTemplate>
</artina:GridOptionsView.ItemTemplate>
</artina:GridOptionsView>
</DataTemplate>
</templates:ItemsStack.ItemTemplate>
</templates:ItemsStack>
</ScrollView>
</controls:PullToRefreshLayout>
</StackLayout>
</ContentPage>
Please take a look at the code after the mark "-- HERE! --".
PS: I'm just refining this layout on order to improve performance.
Any help will be appreciated.
UPDATE:
ViewModel:
public RelayCommand<string> btn_image_tap_preview
{
get
{
return new RelayCommand<string>(
OpenImagePreview
);
}
}
//* Image tap
private async void OpenImagePreview(string url)
{
//* Some code
}
Updated XAML:
<StackLayout x:Name="mainStack" Spacing="0">
<commonViews:MainCustomNavBar MinimumHeightRequest="120" />
....
<ContentView.GestureRecognizers>
<TapGestureRecognizer Command="{Binding Source={x:Reference mainStack}, Path=BindingContext.btn_image_tap_preview}"
CommandParameter="{Binding image_medium}" />
</ContentView.GestureRecognizers>
I'm debugging but I can't reach the ViewModel's command.
You can use x:Reference and take the binding context of any layout or element from your content page like StackLayout. Since its binding context is the ViewModel of the page it would have the command you are looking for at the first level itself. You can directly use it.
<StackLayout x:Name="myStackLayout" Spacing="0">
<commonViews:MainCustomNavBar MinimumHeightRequest="120" />
....
<ContentView.GestureRecognizers>
<TapGestureRecognizer Command="{Binding Source={x:Reference myStackLayout}, Path=BindingContext.command_name}"
CommandParameter="image_medium" />
</ContentView.GestureRecognizers>
Or you can set the binding context for TapGesture
<TapGestureRecognizer BindingContext="{Binding Source={x:Reference myStackLayout.BindingContext}}" Command="{Binding command_name}"
DataTemplates does not seem to work well with x:Reference prior to v1.4.4, you can read more about in the forum post - x:Reference not working?.
The implementation in the newer version can be seen in the answer here.
If you are interested the Bugzilla issue that was fixed is here.

DataBinding ListView ItemTemplate UserControl

I have a ListView on my Windows Universal page. I am using a UserControl to define my ItemTemplate so that I can use the RelativePanel and VisualStateManager to control how my items appear depending on the screen size...
<ListView ItemsSource="{Binding Path=AllThings}"
ItemContainerStyle="{StaticResource ListViewItemStyle}">
<ListView.ItemTemplate>
<DataTemplate>
<local:CrossingControl />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
I have a button in my UserControl where I want to bind its Command to a command property in the ViewModel that is the DataContext of the list itself...
<UserControl ...>
<RelativePanel>
<StackPanel x:Name="crossedEntryPanel">
<Button Command="{Binding DataContext.DeleteCommand,
RelativeSource={RelativeSource Mode=TemplatedParent}}"
CommandParameter="{Binding}"
I have tried using the ElementName binding, but it doesn't seem to work (I suppose because my listview element name is defined in another xaml file). I have also tried the above RelativeSource binding, but that doesn't seem to work either. How can I bind this properly?
You can make use of Tag property to save the DataContext of ListView and use it in UserControl
Here how it is done
<ListView ItemsSource="{Binding Path=AllThings}" x:Name="listview"
ItemContainerStyle="{StaticResource ListViewItemStyle}">
<ListView.ItemTemplate>
<DataTemplate>
<local:CrossingControl Tag="{Binding DataContext,ElementName=listview}" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<UserControl x:Name="usercontrol" ...>
<RelativePanel>
<StackPanel x:Name="crossedEntryPanel">
<Button Command="{Binding Tag.DeleteCommand,ElementName=usercontrol}"
CommandParameter="{Binding}"
RelativeSource={RelativeSource Mode=TemplatedParent}}":- this points to controltemplate of button

How to bind FontSize of Label inside a ListView

I have a ListView whose ItemSource is binded to ArticleList. I need to Bind the FontSize of the Label inside it to MyFontSize which is NOT inside ArticleList. Its just another property in my view model, just like ArticleList
XAML code:
<ListView ItemsSource="{Binding ArticleList}"
x:Name="ArticleListView" HasUnevenRows="True"
SeparatorVisibility="None">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<ViewCell.View>
<ContentView>
<StackLayout Padding="10,0,20,10">
<Image Source="{Binding _Image}"/>
<Label Text="{Binding _Description}"
FontSize="{Binding MyFontSize}"/>
</StackLayout>
</ContentView>
</ViewCell.View>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
You need to 'target' the element which holds the correct datacontext for your ViewModel. I would guess you hooked it up to your page? Anyhow, the syntax is as follows - with elementname the element with the correct datacontext.
{Binding DataContext.MyFontSize, ElementName=LayoutRoot}
Great catch by Dushyant Bangal, seems in Xamarin you need to use the Source property for the binding to work :)
FontSize="{Binding BindingContext.MyFontSize, Source={Reference LayoutRoot}}"
Try something like this:
FontSize="{Binding ElementName=ArticleListView, Path=DataContext.MyFontSize}"