Xamarin.Forms - x:Reference 2nd level - xaml

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.

Related

XAML, Binding, Source and Path

I am learning to understand, how the binding mechanism works in XAML for .NET MAUI. I am assuming this is the same for all XAML projects, WPF, MAUI etc.
At the end is the whole XAML.
This XAML works fine:
<Button WidthRequest="150" Text="Add Activity"
Command="{Binding AddActivityEntityCommand}"
IsEnabled="{Binding IsNotBusy}"
Grid.Row="2"
Margin="8"/>
Is the reason why this works because the Button is part of the ContentPage, which has it's x:DataType set to MainPageViewModel, which is where the command lives?
The Binding is set to AddActivityEntityCommand, while the actual method signature is
async Task AddActivityEntityAsync(). How is this resolved? Since it obviously doesn't match the name, but it works. And what are the method signature requirements for this to work/being recognized?
This on the other hand, doesn't just work as easy out of the box:
<Label HorizontalOptions="End" TextColor="Red" Padding="0,0,10,0" Text="🗑"
IsVisible="{Binding IsSynchronized}">
<Label.GestureRecognizers>
<TapGestureRecognizer
Command="{Binding Source={x:Type viewmodel:MainPageViewModel},
Path=DeleteActivityCommand}" />
</Label.GestureRecognizers>
</Label>
In this context, adding Command="{Binding DeleteActivityCommand} doesn't work, because it derives its Path from <DataTemplate x:DataType="model:ActivityEntity">, I am assuming, which is the data object and not the ViewModel, where the command actually is.
The problem here is, that as soon as I enter this XAML Command="{Binding Source={x:Type viewmodel:MainPageViewModel}, Path=DeleteActivityCommand}", the CollectionView shows empty and there is an unhandled exception thrown when the view is loaded:
System.Reflection.TargetException: Object does not match target type.
The method signature for this command is this async Task DeleteActivityAsync()
What am I missing?
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage
x:Class="OnesieMobile.View.MainPage"
xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:model="clr-namespace:OnesieMobile.Model"
xmlns:viewmodel="clr-namespace:OnesieMobile.ViewModel"
x:DataType="viewmodel:MainPageViewModel"
Title="{Binding Title}">
<Grid
ColumnDefinitions="*"
RowDefinitions="20,50,50,*"
RowSpacing="0">
<Label HorizontalOptions="End" Margin="10,0,10,0" Text="{Binding CurrentDateTime}" Grid.Row="0"/>
<Entry Margin="10,0,10,0"
Grid.Row="1" x:Name="entryNewActivity"
Placeholder="New Activityssss" HeightRequest="30" Text="{Binding NewActivityTitle}" />
<Button WidthRequest="150" Text="Add Activity"
Command="{Binding AddActivityEntityCommand}"
IsEnabled="{Binding IsNotBusy}"
Grid.Row="2"
Margin="8"/>
<CollectionView
Grid.Row="3"
ItemsSource="{Binding ActivityEntities}"
SelectionMode="None">
<CollectionView.ItemTemplate>
<DataTemplate x:DataType="model:ActivityEntity">
<Grid Padding="10,0,10,0">
<Frame Style="{StaticResource CardView}">
<Grid ColumnDefinitions="*,30,50">
<StackLayout Padding="10,5,0,0" Grid.Column="0">
<Label Text="{Binding Title}" />
</StackLayout>
<StackLayout Padding="10,5,0,0" Grid.Column="1">
<Label HorizontalOptions="End" TextColor="Red"
Padding="0,0,10,0" Text="🗑" IsVisible="{Binding IsSynchronized}" >
<Label.GestureRecognizers>
<TapGestureRecognizer
Command="{Binding Source={x:Type viewmodel:MainPageViewModel},
Path=DeleteActivityCommand}" />
</Label.GestureRecognizers>
</Label>
</StackLayout>
<StackLayout Padding="10,5,0,0" Grid.Column="2">
<Label HorizontalOptions="End"
Padding="0,0,10,0" Text="✔" IsVisible="{Binding IsSynchronized}" />
</StackLayout>
</Grid>
</Frame>
</Grid>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
<ActivityIndicator IsVisible="{Binding IsBusy}"
IsRunning="{Binding IsBusy}"
HorizontalOptions="FillAndExpand"
VerticalOptions="CenterAndExpand"
Grid.RowSpan="3"
Grid.ColumnSpan="2"/>
</Grid>
</ContentPage>
Update:
MainPageViewModel.cs contains these Commands
[ICommand]
async Task DeleteActivityAsync()
{
}
[ICommand]
async Task AddActivityEntityAsync()
{
}
Inside an item template, there are several ways to refer to the original BindingContext. I like to do it by getting it from the collection itself:
<CollectionView x:Name="myCollection" ...>
...
<CollectionView.ItemTemplate>
...
Command="{Binding Source={x:Reference myCollection},
Path=BindingContext.DeleteActivityCommand}" />
FUTURE TBD:
I don't like the command name not existing anywhere in the implementing code. Hopefully there will eventually be a way to specify the command name in the ICommand attribute, to make it obvious:
// This won't work today.
[ICommand Name="DeleteActivityCommand"]
...
For now, we have to learn [ICommand]'s magic naming rules, which seem to be "Remove Async from end (if present); Add Command to end".

Resume and modify some graphic elements present in another page

I have created a ControlTemplate in the app.xaml, defining the graphic part, only in the cs I would like to take some elements, to which I have given the name, to assign them the click event, but it signals me that they do not exist in the current context .
Someone who could kindly tell me how to please?
Someone, if possible, modify the source of the webview present in the main page, directly from app.xaml.cs
My code in the template is this:`
<Application.Resources>
<ResourceDictionary>
<ControlTemplate x:Key="HeaderFooterTemplate"><ContentPresenter />
<StackLayout>
<BoxView HorizontalOptions="FillAndExpand" BackgroundColor="#DDDDDD" HeightRequest="2" Margin="0" />
<ScrollView Orientation="Horizontal">
<Grid RowSpacing="0">
<StackLayout Background="#c9ced6" HeightRequest="120" MinimumHeightRequest="120" Orientation="Horizontal" Grid.Row="0" Spacing="2">
<Grid WidthRequest="100" MinimumHeightRequest="120" BackgroundColor="#373B53" Padding="10" x:Name="Dashboard">
<Image Source="homeArancione" x:Name="imgDashboard" HorizontalOptions="Center" VerticalOptions="Center" WidthRequest="30" HeightRequest="30" Grid.Row="0" />
<Label Text="DASHBOARD" TextColor="#c9ced6" FontAttributes="Bold" HorizontalOptions="Center" Grid.Row="1" />
</Grid></Grid>
And in the reference page I recall it like this:
ControlTemplate="{StaticResource HeaderFooterTemplate}"
Graphically it works, but in the cs is I can't modify the texts to the labels or assign click events to the elements because it tells me that they don't exist in the current context
If you want to change the text or trigger the click event of the label in ControlTemplate, you could use the GestureRecognizers. The x:Name could not be found in the ControlTemplate directly.
<ControlTemplate x:Key="HeaderFooterTemplate">
<!--<ContentPresenter />-->
<StackLayout>
<BoxView HorizontalOptions="FillAndExpand" BackgroundColor="#DDDDDD" HeightRequest="2" Margin="0" />
<ScrollView Orientation="Horizontal">
<Grid RowSpacing="0">
<StackLayout Background="#c9ced6" HeightRequest="120" MinimumHeightRequest="120" Orientation="Horizontal" Grid.Row="0" Spacing="2">
<Grid WidthRequest="100" MinimumHeightRequest="120" BackgroundColor="#373B53" Padding="10" x:Name="Dashboard">
<Image Source="homeArancione" x:Name="imgDashboard" HorizontalOptions="Center" VerticalOptions="Center" WidthRequest="30" HeightRequest="30" Grid.Row="0" />
<Label Text="DASHBOARD" TextColor="#c9ced6" FontAttributes="Bold" HorizontalOptions="Center" Grid.Row="1">
<Label.GestureRecognizers>
<TapGestureRecognizer Tapped="TapGestureRecognizer_Tapped"></TapGestureRecognizer>
</Label.GestureRecognizers>
</Label>
</Grid>
</StackLayout>
</Grid>
</ScrollView>
</StackLayout>
</ControlTemplate>
Code behind: App.xaml.cs
private void TapGestureRecognizer_Tapped(object sender, EventArgs e)
{
Console.WriteLine("-----------");
var label = sender as Label;
label.Text = "Hello";
}
Update:
update the text of the label as soon as the page loads, without
having to click
Xamarin provide GetTemplateChild to get a named element from a template. The GetTemplateChild method should only be called after the OnApplyTemplate method has been called. If you want to call the OnApplyTemplate method, this template should be added for the contentpage.
You could refer to the thread i done before.
Xamarin SwipeView Open Left Item In ControlTemplate

Vertical text align on side menu xamarin.forms

I have been trying to centralize the text- "Hello Janine Alexander" on this side menu but to no avail. I have tried YAlign="Center" and also VerticalTextAlignment="Center" but neither have moved it even an inch down. Any help will be appreciated. I do think its in the navigation bar resulting in it not moving vertically down.How do i fix this
Here is the complete xaml code:
<?xml version="1.0" encoding="utf-8" ?>
<MasterDetailPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="loyaltyworx1.Profile">
<MasterDetailPage.Master>
<ContentPage Title="Menu"
BackgroundColor="#2196F3">
<StackLayout Orientation="Vertical">
<Label x:Name="lblMessage" FontSize="Medium" HorizontalOptions="Center" TextColor="White" />
<!--
This StackLayout you can use for other
data that you want to have in your menu drawer
-->
<StackLayout BackgroundColor="#2196F3"
HeightRequest="150">
<Label Text=""
FontSize="20"
VerticalOptions="CenterAndExpand"
TextColor="White"
HorizontalOptions="Center"/>
</StackLayout>
<ListView x:Name="navigationDrawerList"
RowHeight="60"
SeparatorVisibility="None"
BackgroundColor="White"
ItemSelected="OnMenuItemSelected">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<!-- Main design for our menu items -->
<StackLayout VerticalOptions="FillAndExpand"
Orientation="Horizontal"
Padding="20,10,0,10"
Spacing="20">
<Image Source="{Binding Icon}"
WidthRequest="40"
HeightRequest="40"
VerticalOptions="Center"
/>
<Label Text="{Binding Title}"
FontSize="Medium"
VerticalOptions="Center"
TextColor="#343f42"/>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
</ContentPage>
</MasterDetailPage.Master>
<MasterDetailPage.Detail>
<NavigationPage>
</NavigationPage>
</MasterDetailPage.Detail>
</MasterDetailPage>
It looks like you're adding it to the Navigation bar of the MasterDetailPage. If so, you're not going to be able to move it down. What I did on my app was to remove the navigationbar in the MasterDetailPage and in the content page I added the label with binding to add the name of the logged in user.
MainPage.xaml
<?xml version="1.0" encoding="utf-8" ?>
<MasterDetailPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:MyApp"
x:Class="MyApp.MainPage"
NavigationPage.HasNavigationBar="False">
<MasterDetailPage.Master>
<local:MasterPage x:Name ="masterPage"/>
</MasterDetailPage.Master>
</MasterDetailPage>
MasterPage.xaml
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="MyApp.MasterPage"
Padding="0,10,0,0"
Icon="hamburger.png"
Title="Menu">
<ContentPage.Content>
<StackLayout>
<StackLayout Orientation="Horizontal" Padding="10,10,0,0">
<Label Text="" FontSize="40" FontFamily="FontAwesome" />
<Label x:Name="Username" Text="{Binding UserFullName}" VerticalOptions="CenterAndExpand"/>
</StackLayout>
.....
Screen shot
If that's not the issue please add the code that is wrapping the label to get a better idea of what could be happening.

Xamarin Custom ViewCell

while making custom nested views i faced a problem.
i'm trying to use my custom view(ViewCell) from various pages.
and this is how i used it.
it wont raise any error if i use View for Xaml and codebehind,
but it wont display anything.
if i try ViewCell for xaml and its codebehind, i get some error.
and this is how i used it.
in mainPage xaml
<local:UploaderCell Uploader="{Binding post.uploader}"/>
this is the line causing the error, and
<?xml version="1.0" encoding="UTF-8"?>
<ContentPage
xmlns:local="clr-namespace:EodiRoad;assembly=EodiRoad"
xmlns:ic="clr-namespace:ImageCircle.Forms.Plugin.Abstractions;assembly=ImageCircle.Forms.Plugin.Abstractions"
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="EodiRoad.SocialPage"
Title="Story">
<ContentPage.ToolbarItems>
<ToolbarItem Icon="plus.png" Order="Primary" Priority="0" Activated="OnCreatePostClicked"/>
</ContentPage.ToolbarItems>
<StackLayout>
<ListView
x:Name="socialPostsListView"
HasUnevenRows="true"
IsPullToRefreshEnabled="true"
Refreshing="Handle_Refreshing"
SeparatorVisibility="None"
ItemSelected="OnSocialPostSelected"
>
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout Orientation="Vertical" Padding="5">
<local:UploaderCell Uploader="{Binding post.uploader}"/>
<Image
x:Name="postImage"
Source="{Binding post.image}"
Aspect="AspectFill"
MinimumHeightRequest="200"
/>
<StackLayout Orientation="Horizontal">
<Switch IsToggled="{Binding post.isLiked}"/>
<Button Text="comment"/>
<Button Text="share"/>
</StackLayout>
<StackLayout Orientation="Vertical" HorizontalOptions="StartAndExpand">
<Label Text="{Binding post.content}"/>
<Label Text="{Binding post.uploadedTime}" TextColor="Gray"/>
</StackLayout>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
</ContentPage>
this is my full mainPage xaml code.
in UploderCell.xaml
<?xml version="1.0" encoding="UTF-8"?>
<ViewCell
xmlns:ic="clr-namespace:ImageCircle.Forms.Plugin.Abstractions;assembly=ImageCircle.Forms.Plugin.Abstractions"
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="EodiRoad.UploaderCell">
<StackLayout Orientation="Horizontal" Padding="5">
<Button Clicked="Handle_Clicked" Image="{Binding Uploader.userProfile.userThumbnail}" BorderRadius="5" HeightRequest="50" WidthRequest="50"/>
<ic:CircleImage
x:Name="userProfileImage"
HeightRequest="50"
WidthRequest="50"
Aspect="AspectFill"
Source="{Binding Uploader.userProfile.userThumbnail}"
/>
<Label Text="{Binding Uploader.username}"/>
</StackLayout>
</ViewCell>
and the codeBehind
public partial class UploaderCell : ViewCell
{
public static readonly BindableProperty UploaderProperty =
BindableProperty.Create(
propertyName: "Uploader",
returnType: typeof(UserModel),
declaringType: typeof(UploaderCell)
);
public UserModel Uploader
{
get
{ return GetValue(UploaderProperty) as UserModel; }
set
{ SetValue(UploaderProperty, value); }
}
public UploaderCell()
{
InitializeComponent();
BindingContext = this;
}
}
and i get this error from mainPage
object of type Project.UploaderCell cannot be converted to type 'xamarin.forms.view'
what am i doing wrong here?
how could i possibly fi this..

How to change icon color in Xamarin.Forms xaml page?

Currently, I am developing a Xamarin.Forms PCL application for Android and iOS. In one of my pages I define a ListView. The template for the ListView.ItemTemplate displays an icon using <Image Source="some_Icon.png" />. The icon displays as expected (black) but I have not found a way to set the color.
I would like to colorize this icon based on logic in my ViewModel.
I have searched for answers both through the Documentation and StackOverlflow to no avail.
Is there a better way to display and style icons from a Xamarin.Forms PCL project?
Does Xamarin.Forms require a set of resource/image files for every possible color I might use?
I solved my problem with Iconize for Xamarin.Forms. I implemented my solution based off the Iconize sample code below.
<?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:iconize="clr-namespace:FormsPlugin.Iconize;assembly=FormsPlugin.Iconize"
x:Class="Iconize.FormsSample.Page1"
Title="{Binding FontFamily}">
<ContentPage.ToolbarItems>
<iconize:IconToolbarItem Command="{Binding ModalTestCommand}" Icon="fa-500px" />
<iconize:IconToolbarItem Command="{Binding VisibleTestCommand}" Icon="fa-500px" IconColor="Red" />
<iconize:IconToolbarItem Command="{Binding VisibleTestCommand}" Icon="fa-500px" IsVisible="{Binding VisibleTest}" />
</ContentPage.ToolbarItems>
<ListView CachingStrategy="RecycleElement" ItemsSource="{Binding Characters}">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout Orientation="Horizontal">
<iconize:IconImage HeightRequest="20" Icon="{Binding Key}" IconColor="Blue" WidthRequest="20" />
<iconize:IconImage HeightRequest="20" Icon="{Binding Key}" BackgroundColor="Blue" IconColor="Yellow" WidthRequest="20" IconSize="10" />
<iconize:IconButton FontSize="20" Text="{Binding Key}" TextColor="Red" WidthRequest="48" />
<iconize:IconLabel FontSize="20" Text="{Binding Key}" TextColor="Green" VerticalTextAlignment="Center" />
<Label Text="{Binding Key}" VerticalTextAlignment="Center" />
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</ContentPage>
In my case, I simply had to replace my original <Image .../> element and replace it with
<iconize:IconImage HeightRequest="20" Icon="fa-circle" IconColor="{Binding CircleColor}" WidthRequest="20" />.
In my ViewModel, I added the CircleColor property to set the icon color as needed based on my logic.