Prism and Control Templates - xamarin

I'm trying to rewrite the sample "SimpleThemeWithTemplateBinding" that uses ControlTemplates, with Prism.
I also added a simple
HeaderText = "changed";
in the button to change the HeaderText in the ControlTemplate, and it works, in the original sample.
So I copied the template in my app.xaml:
<ControlTemplate x:Key="TealTemplate">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="0.1*" />
<RowDefinition Height="0.8*" />
<RowDefinition Height="0.1*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="0.05*" />
<ColumnDefinition Width="0.95*" />
</Grid.ColumnDefinitions>
<BoxView Grid.ColumnSpan="2" Color="Teal" />
<Label Grid.Column="1" Text="{TemplateBinding BindingContext.HeaderText}" TextColor="White" VerticalOptions="Center" />
<ContentPresenter Grid.Row="1" Grid.ColumnSpan="2" />
<BoxView Grid.Row="2" Grid.ColumnSpan="2" Color="Teal" />
<Label Grid.Row="2" Grid.Column="1" Text="(c) Xamarin 2016" TextColor="White" VerticalOptions="Center" />
</Grid>
</ControlTemplate>
and just changed
{TemplateBinding HeaderText}
to
{TemplateBinding BindingContext.HeaderText}
VIEW:
<?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:prism="clr-namespace:Prism.Mvvm;assembly=Prism.Forms"
prism:ViewModelLocator.AutowireViewModel="True"
x:Class="TestAppNSPrism7.Views.PrismContentPage9">
<ContentPage.Content>
<ContentView x:Name="contentView" Padding="0,0,0,0" ControlTemplate="{StaticResource TealTemplate}" >
<StackLayout>
<Label Text="Welcome to Xamarin.Forms! page1"
VerticalOptions="CenterAndExpand"
HorizontalOptions="CenterAndExpand" />
<Label Text="Buazz"
VerticalOptions="CenterAndExpand"
HorizontalOptions="CenterAndExpand" />
<Button Command="{Binding ButtonChangeValueCommand}" Text="Change value" ></Button>
</StackLayout>
</ContentView>
</ContentPage.Content>
</ContentPage>
VIEWMODEL:
public class PrismContentPage9ViewModel : ViewModelBase
{
ControlTemplate tealTemplate;
private string _headerText = "test";
public string HeaderText
{
get
{
return _headerText;
}
set { SetProperty(ref _headerText, value); }
}
public PrismContentPage9ViewModel(INavigationService navigationService) : base(navigationService)
{
tealTemplate = (ControlTemplate)Application.Current.Resources["TealTemplate"];
}
private DelegateCommand _buttonChangeValueCommand;
public DelegateCommand ButtonChangeValueCommand =>
_buttonChangeValueCommand ?? (_buttonChangeValueCommand = new DelegateCommand(ExecuteButtonChangeValueCommand));
void ExecuteButtonChangeValueCommand()
{
HeaderText = "changed";
}
}
The Page gets loaded correctly, with the ControlTemplate, and the HeaderText is "test".
So it seems the HeaderText binding with the ControlTemplate is working.
But when I set the HeaderText to "changed", the Label doesn't get updated.
I debugged and checked that once I press the button it goes through ExecuteButtonChangeValueCommand() and SetProperty(ref _headerText, value)
Any suggestion?
Thanks!

I changed the TemplateBinding from :
Text="{TemplateBinding BindingContext.HeaderText}"
to:
Text="{TemplateBinding Parent.BindingContext.HeaderText}"
and it updates now when I press your changed button.
I believe its due to the template not having a binding context automatically set but the template's parent (PrismContentPage9) has its BindingContext auto-wired from Prism's AutowireViewModel property, e.g.,
prism:ViewModelLocator.AutowireViewModel="True"
Let me know if that works for you.

Related

Xamarin App opens different Page from Navigation.PushAsync() Page

Description
Xamarin application opens different Page upon NavigationPage.Pushaync().
NavigationPage.Asyncpush() is called within a ButtonClicked Method.
That different page used to be the one being pushed but it no longer is.
ViewModel.cs
private async void OnAboutUsClicked()
{
var modifiedContactUs = new ModifiedContactUs() { BackgroundColor = ResourceColourModel.BackgroundColor };
await Application.Current.MainPage.Navigation.PushAsync(modifiedContactUs);
}
ModifiedContactUs.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="Project.Views.Pages.ModifiedContact"
xmlns:mapObject="clr-namespace:Xamarin.Forms.Maps;assembly=Xamarin.Forms.Maps">
<ContentPage.Content>
<StackLayout>
<Grid BackgroundColor="white">
<mapObject:Map x:Name="mapId" HasZoomEnabled="True" MapType="Street">
</mapObject:Map>
</Grid>
</StackLayout>
</ContentPage.Content>
</ContentPage>
ModifiedContactUs.xaml.cs
using Xamarin.Forms;
using Xamarin.Forms.Xaml;
namespace Project.Views.Pages
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class ModifiedContactUs : ContentPage
{
public ModifiedContactUs()
{
InitializeComponent();
}
}
ContactUs.xaml(The different Page)
<?xml version="1.0" encoding="utf-8" ?>
<customControl:CustomContentPage
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:d="http://xamarin.com/schemas/2014/forms/design"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:map ="clr-namespace:Xamarin.Forms.Maps;assembly=Xamarin.Forms.Maps"
xmlns:ffimageloading="clr-namespace:FFImageLoading.Svg.Forms;assembly=FFImageLoading.Svg.Forms"
xmlns:customControl="clr-namespace:Project.CustomControls.Renderers"
xmlns:templates_navbar="clr-namespace:Project.Views.Templates.Menus"
xmlns:fontAwesome="clr-namespace:Project._Utilities"
mc:Ignorable="d"
x:Class="Project.Views.Pages.ContactUs"
>
<ContentPage.Content>
<StackLayout BackgroundColor="Transparent" Spacing="0">
<templates_navbar:NavBar DotMenu_IsVisible="False" BackButton_IsVisible="True"/>
<Grid BackgroundColor="white">
<Grid.RowDefinitions>
<RowDefinition Height=".33*"/>
<RowDefinition Height=".07*"/>
<RowDefinition Height=".6*"/>
</Grid.RowDefinitions>
<Grid Grid.Row="0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width=".7*"/>
<ColumnDefinition Width=".3*"/>
</Grid.ColumnDefinitions>
<StackLayout Grid.Column="0" Padding="10">
<StackLayout>
<Label
Text="Company Name"
FontAttributes="Bold"
FontSize="Medium"
FontFamily="Lato-Bold"
VerticalOptions="CenterAndExpand"
HorizontalOptions="StartAndExpand"
/>
<Label
Text="{Binding Address}"
FontFamily="Lato-Bold"
VerticalOptions="CenterAndExpand"
HorizontalOptions="StartAndExpand"
/>
</StackLayout>
<StackLayout>
<Label
Text="Opening Times"
FontAttributes="Bold"
FontSize="Medium"
FontFamily="Lato-Bold"
VerticalOptions="CenterAndExpand"
HorizontalOptions="StartAndExpand"
/>
<Label
x:Name="weekdays_lbl"
Text="{Binding Weekday}"
FontFamily="Lato-Bold"
VerticalOptions="CenterAndExpand"
HorizontalOptions="StartAndExpand"
/>
<Label
x:Name="weekend_lbl"
Text="{Binding Weekend}"
FontFamily="Lato-Bold"
VerticalOptions="CenterAndExpand"
HorizontalOptions="StartAndExpand"
/>
</StackLayout>
</StackLayout>
<StackLayout Grid.Column="1">
<StackLayout VerticalOptions="CenterAndExpand">
<Button
x:Name="CallUsBtn"
Text="Call Us"
TextColor="White"
BackgroundColor="{StaticResource PrimaryColour}"
FontFamily="Lato-Bold"
FontSize="12"
BorderWidth="2"
BorderColor="White"
Margin="1"
CornerRadius="22"
Command="{Binding PhoneCommand}"
/>
<Button
x:Name="MessageUsBtn"
Text="Message"
TextColor="White"
BackgroundColor="{StaticResource PrimaryColour}"
FontFamily="Lato-Bold"
FontSize="12"
BorderWidth="2"
BorderColor="White"
Margin="1"
CornerRadius="22"
Command="{Binding EmailCommand}"
/>
<Button
x:Name="RequestCallBack"
Text="Request CallBack"
TextColor="White"
BackgroundColor="{StaticResource AccentColour}"
FontFamily="Lato-Bold"
FontSize="11"
BorderWidth="2"
BorderColor="White"
Margin="1"
CornerRadius="22"
Command="{Binding CallbackCommand}"
/>
</StackLayout>
</StackLayout>
</Grid>
<Grid Grid.Row="1" Padding="5">
<Grid.ColumnDefinitions>
<ColumnDefinition Width=".25*"/>
<ColumnDefinition Width=".25*"/>
<ColumnDefinition Width=".25*"/>
<ColumnDefinition Width=".25*"/>
</Grid.ColumnDefinitions>
<StackLayout Grid.Column="0">
<Grid VerticalOptions="CenterAndExpand">
<Image
Source="{FontImage FontFamily={StaticResource MaterialFontFamily},
Glyph={x:Static fontAwesome:IconFonts.Facebook}, Color=#1877F2,Size=60}"
HeightRequest="80"
/>
<Button Clicked="SocialMediaBtnHandler" BackgroundColor="Transparent" Margin="5, 0" CommandParameter="facebook"/>
</Grid>
</StackLayout>
<StackLayout Grid.Column="1">
<Grid VerticalOptions="CenterAndExpand">
<Image
Source="{FontImage FontFamily={StaticResource MaterialFontFamily},
Glyph={x:Static fontAwesome:IconFonts.Twitter}, Color=#00ACED, Size=60}"/>
<Button Clicked="SocialMediaBtnHandler" BackgroundColor="Transparent" Margin="5, 0" CommandParameter="twitter"/>
</Grid>
</StackLayout>
<StackLayout Grid.Column="2">
<Grid VerticalOptions="CenterAndExpand">
<Image
Source="{FontImage FontFamily={StaticResource MaterialFontFamily},
Glyph={x:Static fontAwesome:IconFonts.Linkedin}, Color=#0E76A8, Size=60}"/>
<Button Clicked="SocialMediaBtnHandler" BackgroundColor="Transparent" Margin="5, 0" CommandParameter="linkedIn"/>
</Grid>
</StackLayout>
<StackLayout Grid.Column="3">
<Grid VerticalOptions="CenterAndExpand" Padding="2">
<ffimageloading:SvgCachedImage
Source="social_logo_instagram.png"
HeightRequest="40"/>
<Button Clicked="SocialMediaBtnHandler" BackgroundColor="Transparent" Margin="5, 0" CommandParameter="instagram"/>
</Grid>
</StackLayout>
</Grid>
<Grid Grid.Row="2">
<Grid>
<StackLayout Grid.Row="0" Padding="5">
<map:Map x:Name="mapObject" HasZoomEnabled="True" MapType="Street">
<map:Map.ItemTemplate>
<DataTemplate>
<map:Pin
Position="{Binding Position}"
Address="{Binding Address}"
Label="{Binding PlaceName}"
/>
</DataTemplate>
</map:Map.ItemTemplate>
</map:Map>
</StackLayout>
</Grid>
</Grid>
</Grid>
<AbsoluteLayout BackgroundColor="Red" HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand" AbsoluteLayout.LayoutBounds="0, 0, 1, 1" AbsoluteLayout.LayoutFlags="SizeProportional">
</AbsoluteLayout>
</StackLayout>
</ContentPage.Content>
</customControl:CustomContentPage>
ContactUs.xaml.cs (The different page)
using Project._SharedData;
using Project.CustomControls.Renderers;
using Project.Models;
using Project.ViewModels;
using Rg.Plugins.Popup.Services;
using System;
using Xamarin.Essentials;
using Xamarin.Forms;
using Xamarin.Forms.Maps;
using Xamarin.Forms.Xaml;
namespace Project.Views.Pages
{
[XamlCompilation(XamlCompilationOptions.Compile)]
[System.ComponentModel.DesignTimeVisible(false)]
public partial class ContactUsPage : CustomContentPage
{
ContactUsVM _ContactUsVM;
public ContactUsPage()
{
InitializeComponent();
BindingContext = _ContactUsVM = new ContactUsVM();
initMap();
configureDisplay();
setFontOpeningTime();
}
protected override void OnAppearing()
{
base.OnAppearing();
_ContactUsVM.OnAppearing();
configureDisplay();
}
protected override void OnDisappearing()
{
base.OnDisappearing();
_ContactUsVM.OnDisappearing();
try
{
if (PopupNavigation.Instance != null)
PopupNavigation.Instance.PopAsync(true);
}
catch (Exception) { }
}
private void setFontOpeningTime()
{
DayOfWeek day = DateTime.Now.DayOfWeek;
// Set the font based on whether current day is weekday or weekend
if ((day == DayOfWeek.Saturday) || (day == DayOfWeek.Sunday))
{
weekend_lbl.FontAttributes = FontAttributes.Bold;
}
else
{
weekdays_lbl.FontAttributes = FontAttributes.Bold;
}
}
private void configureDisplay()
{
if (!string.IsNullOrEmpty(SharedPrefs.UserSessionEmail))
{
RequestCallBack.IsVisible = true;
MessageUsBtn.Text = "Email";
}
else
{
RequestCallBack.IsVisible = false;
MessageUsBtn.Text = "Message";
}
}
private async void SocialMediaBtnHandler(object sender, EventArgs e)
{
string socialMediaName = (sender as Button).CommandParameter.ToString().ToLower();
await Launcher.OpenAsync(SocialMediaModel.WebLink(socialMediaName));
}
private void initMap()
{
try
{
double latitude = Convert.ToDouble(SharedData.CompanyDetailsList[0].Latitude);
double longitude = Convert.ToDouble(SharedData.CompanyDetailsList[0].Longitude);
mapObject.MoveToRegion(MapSpan.FromCenterAndRadius(new Position(latitude, longitude), Distance.FromMeters(100)));
mapObject.ItemsSource = SharedData.CompanyDetailsList;
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
}
}
}
Expected behavior
To navigate to a new Page named ModifiedContactUs
Actual behavior:
Navigates to page called named ContactUs
Basic Information:
Android Build:
-minimum level 21
-Target level 30
Devices:
-Huawei Y6 2019
-API level 29
-Pixel 5
-API level 30
Notes
This question is different to from the first question I asked recently (Where the device crashes). This is behavior happens on all devices I test on except the one from that first question
Expected behavior
To navigate to a new Page named ModifiedContactPage
Actual behavior:
Navigates to page called named ContactUsPage
From your code I saw you just push to ModifiedContactUs not ModifiedContactPage .
var modifiedContactUs = new ModifiedContactUs() { BackgroundColor = ResourceColourModel.BackgroundColor };
await Application.Current.MainPage.Navigation.PushAsync(modifiedContactUs);
Suggestion
Try to add breakpoint to see if it triggers button click event es expected .
Delete the old class and try again .
Delete the app from the device and try again .
This worked: Clone the repo into a new folder as a new project and copy the files and changes to this new repo.
Attempted configuration solutions that did not work: Restarting visual studio. Untick and Tick Debug Mode. Untick and tick Deploy in Configuration Manager under Build. In Android Properties> android Options making sure debugging is checked and debugger is Xamarin.

Xamarin Forms CollectionView TapGestureRecognizer not firing on label

I have a XF app with the following collection view defined. The 2nd label has a TapGestureRecognizer that doesn't fire DoSomethingInteresting in the model when I tap the label (trying this on Android). Can someone see what the issue is please?
A working sample can be cloned from here.
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:SampleApp"
x:Class="SampleApp.MainPage">
<StackLayout>
<CollectionView ItemsSource="{Binding GaugeSites}">
<CollectionView.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="40" />
</Grid.RowDefinitions>
<Label Grid.Column="0"
Text="{Binding Description}"
FontSize="20"
Margin="10"
TextColor="Black"
FontAttributes="Bold" />
<Label Grid.Column="1"
Margin="10"
FontSize="20"
Text="Click Me!">
<Label.GestureRecognizers>
<TapGestureRecognizer Command="{Binding DoSomethingInteresting}" />
</Label.GestureRecognizers>
</Label>
</Grid>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
</StackLayout>
</ContentPage>
Model
namespace SampleApp
{
public class MainPageModel : FreshBasePageModel
{
public MainPageModel() : base()
{
GaugeSites = new List<GaugeSite>();
for (var index = 1; index <= 5; index++)
{
GaugeSites.Add(new GaugeSite()
{
Description = $"Gauge Site {index}"
});
}
}
public List<GaugeSite> GaugeSites { get; set; }
public Command DoSomethingInteresting
{
get
{
return new Command(() =>
{
});
}
}
}
[AddINotifyPropertyChangedInterface]
public class GaugeSite
{
public string Description { get; set; }
}
}
Maybe this is redundant but I will take my chances. As other answers indicated you need to set the source within your TapGestureRecognizer to the name of the CollectionView. However, it is probably useful to know which GaugeSite instance was associated with the CollectionView item whose TabGestureRecognizer was tapped. To add this info add a CommandParamter, i.e.
<Label.GestureRecognizers>
<TapGestureRecognizer Command="{Binding BindingContext.DoSomethingInteresting,
CommandParameter="{Binding}"
Source={x:Reference YourCollectionName}}" />
</Label.GestureRecognizers>
When it comes to the command you could use
DoSomethingInteresting = new Command<GaugeSite>((a) => DoSomething(a));
in your viewmodels contructor. And the method referenced by the lambda would be
void DoSomething(GaugeSite gaugeSite)
{
// ....
}
Hope this helps
I have download your sample, please take a look the following step.
1.Binding MainpageModel for MainPage bindingContext, add this line in MainPage.cs.
this.BindingContext = new MainPageModel();
2.Name Collectionview as collection1, then modify label command.
<CollectionView x:Name="collection1" ItemsSource="{Binding GaugeSites}">
<CollectionView.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="40" />
</Grid.RowDefinitions>
<Label
Grid.Column="0"
Margin="10"
FontAttributes="Bold"
FontSize="20"
Text="{Binding Description}"
TextColor="Black" />
<Label
Grid.Column="1"
Margin="10"
FontSize="20"
Text="Click Me!">
<Label.GestureRecognizers>
<TapGestureRecognizer Command="{Binding BindingContext.DoSomethingInteresting, Source={x:Reference collection1}}" />
</Label.GestureRecognizers>
</Label>
</Grid>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
Then you can try again.

How to set BindingContext of MasterDetailPage.Master

I have a MasterDetailPage.Master and a Login page. After the login I call a WebService that returns a filled class to me, and its working, even the BindingContext is getting the values, but the fields on the Page are empty. How do I set the binding context of the MasterDetailPage.Master if it is rendering before the Login page?
This is for a profile page in the MasterDetailPage.Master, using a WebService to fill the binding. I already tried to call a new instance of the MasterDetailPage on the OnDesappearing of the Login page, but not success.
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:views="clr-namespace:HCTaNaMao.Views"
x:Class="HCTaNaMao.Views.MainPage">
<MasterDetailPage.Master>
<views:InformacoesUsuario />
</MasterDetailPage.Master>
<MasterDetailPage.Detail>
<NavigationPage>
<NavigationPage.Icon>
<OnPlatform x:TypeArguments="FileImageSource">
<On Platform="iOS" Value="tab_feed.png"/>
</OnPlatform>
</NavigationPage.Icon>
<x:Arguments>
<views:Menu />
</x:Arguments>
</NavigationPage>
</MasterDetailPage.Detail>
</MasterDetailPage>
MasterDetailPage.Master.xaml
<?xml version="1.0" encoding="utf-8" ?>
<TabbedPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="HCTaNaMao.Views.InformacoesUsuario"
Title="Dados do Usuário">
<TabbedPage.Children>
<ContentPage Title="Usuário">
<StackLayout Padding="0,50,0,0">
<Image BackgroundColor="LightGray" HorizontalOptions="Center" Source="HC_logo.png"></Image>
<Frame
OutlineColor="Silver"
VerticalOptions="CenterAndExpand"
HorizontalOptions="Fill"
Margin="15">
<StackLayout
HorizontalOptions="Center"
VerticalOptions="Center">
<Label x:Name="lblNome" Text="{Binding nome}" FontSize="18" HorizontalTextAlignment="Center"></Label>
<BoxView Color="Gray" HeightRequest="1" HorizontalOptions="Fill"/>
<Label x:Name="lblProntuario" Text="{Binding prontuario}" FontSize="18" HorizontalTextAlignment="Center"></Label>
<BoxView Color="Gray" HeightRequest="1" HorizontalOptions="Fill"/>
<Button x:Name="btnEditar" Text="Perfil" TextColor="White" WidthRequest="110"
HorizontalOptions="Center" BackgroundColor="SteelBlue" BorderRadius="20"/>
</StackLayout>
</Frame>
</StackLayout>
</ContentPage>
<ContentPage Title="Perfil">
<Frame
OutlineColor="Silver"
VerticalOptions="CenterAndExpand"
HorizontalOptions="Fill"
Margin="15">
<StackLayout>
<TableView>
<TableRoot>
<TableSection Title="Dados Pessoais">
<EntryCell x:Name="cellNome" Placeholder="Nome"
Text="{Binding nome}" IsEnabled="True"></EntryCell>
<EntryCell Placeholder="Data de Nascimento" x:Name="cellNasc"
Text="{Binding data_nascimento}" IsEnabled="True"></EntryCell>
<EntryCell Placeholder="CPF" Keyboard="Numeric" x:Name="cellCpf"
Text="{Binding cpf}" IsEnabled="True"></EntryCell>
<EntryCell Placeholder="CNS" Keyboard="Numeric" x:Name="cellCns"
Text="{Binding cns}" IsEnabled="True"></EntryCell>
</TableSection>
</TableRoot>
</TableView>
<Button Text="Editar"
IsVisible="False">
</Button>
<Button x:Name="btnSalvar"
Text="Salvar"
IsVisible="True" TextColor="White" WidthRequest="110"
HorizontalOptions="Center" BackgroundColor="SteelBlue" BorderRadius="20"></Button>
</StackLayout>
</Frame>
</ContentPage>
</TabbedPage.Children>
</TabbedPage>
MaterDetailPage.xaml.cs
namespace HCTaNaMao.Views
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class InformacoesUsuario : TabbedPage
{
HCTMWebService service = new HCTMWebService();
HCTMPacienteDTO paciente;
public InformacoesUsuario ()
{
InitializeComponent();
btnEditar.Command = new Command(() => this.CurrentPage = this.Children[1]);
btnSalvar.Command = new Command(() => this.CurrentPage = this.Children[0]);
paciente = service.InformacoesPaciente(Login.seq_cliente);
BindingContext = paciente;
}
}
}
I expect MasterPage fields are filled by the Binding
How do I set the binding context of the MasterDetailPage.Master if it is rendering before the Login page?
About binding context of MasterDetailedPage.Master, I do one sample, you can take a look:
MainPage:
<MasterDetailPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:MasterDetailPageNavigation;assembly=MasterDetailPageNavigation"
x:Class="MasterDetailPageNavigation.MainPage">
<MasterDetailPage.Master>
<local:MasterPage x:Name="masterPage" />
</MasterDetailPage.Master>
<MasterDetailPage.Detail>
<NavigationPage>
<x:Arguments>
<local:ContactsPage />
</x:Arguments>
</NavigationPage>
</MasterDetailPage.Detail>
</MasterDetailPage>
public partial class MainPage : MasterDetailPage
{
public MainPage()
{
InitializeComponent();
masterPage.listView.ItemSelected += OnItemSelected;
if (Device.RuntimePlatform == Device.UWP)
{
MasterBehavior = MasterBehavior.Popover;
}
}
void OnItemSelected(object sender, SelectedItemChangedEventArgs e)
{
var item = e.SelectedItem as MasterPageItem;
if (item != null)
{
Detail = new NavigationPage((Page)Activator.CreateInstance(item.TargetType));
masterPage.listView.SelectedItem = null;
IsPresented = false;
}
}
}
MasterPage:
I don't understand why your masterPage is TabbedPage, I think it should be contentpage,that has Listview to display detailed page title.
<ContentPage
x:Class="MasterDetailPageNavigation.MasterPage"
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="using:MasterDetailPageNavigation"
Title="Personal Organiser"
Padding="0,40,0,0"
IconImageSource="hamburger.png">
<StackLayout>
<ListView
x:Name="listView"
x:FieldModifier="public"
ItemsSource="{Binding items}">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Grid Padding="5,10">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="30" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Image Source="{Binding IconSource}" />
<Label Grid.Column="1" Text="{Binding Title}" />
</Grid>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
public partial class MasterPage : ContentPage
{
public ObservableCollection<MasterPageItem> items { get; set; }
public MasterPage()
{
InitializeComponent();
//load data from service or other.
items = new ObservableCollection<MasterPageItem>()
{
new MasterPageItem(){ Title="Contacts", IconSource="contacts.png",TargetType=typeof(ContactsPage)},
new MasterPageItem(){Title="TodoList",IconSource="todo.png",TargetType=typeof(TodoListPage)},
new MasterPageItem(){Title="Reminders",IconSource="reminders.png",TargetType=typeof(ReminderPage)}
};
this.BindingContext = this;
}
}
About MasterDetailedPage, I suggest you can take a look:
https://learn.microsoft.com/en-us/xamarin/xamarin-forms/app-fundamentals/navigation/master-detail-page

Get the click command from template to the view model in xamarin

In the template there is a button and created as below called x:Name="cartbutton"
<?xml version="1.0" encoding="UTF-8"?>
<Grid xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:ffimageloading="clr-namespace:FFImageLoading.Forms;assembly=FFImageLoading.Forms"
x:Class="FormsApplication.Templates.ListItemTemplate"
xmlns:controls="clr-namespace:FormsControls.Base;assembly=FormsControls.Base"
ColumnSpacing="0" RowSpacing="0" HeightRequest="350" Padding="8,0,8,8" x:Name="ListItemTempPage">
<Grid.RowDefinitions>
<RowDefinition Height="300" />
<RowDefinition Height="30" />
<RowDefinition Height="30"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<controls:RoundedBoxView Grid.RowSpan="2" BackgroundColor="White" BorderColor="Silver" CornerRadius="8" BorderThickness="2"/>
<ffimageloading:CachedImage Grid.RowSpan="2" Source="{Binding ImageSource}" Aspect="AspectFill" DownsampleToViewSize="False"/>
<Button x:Name="cartbutton" Grid.Row="0" HorizontalOptions="End" VerticalOptions="Start" Image="lowim.png" BackgroundColor="Transparent" Margin="0,5,5,0" Command="{Binding ParentContext.Itemtapped, Source={x:Reference ListItemTempPage}}" CommandParameter="{Binding .}"/>
</Grid>
and the parent page called by the reference the template as below
<ListView x:Name="listView" Grid.Row="1" Margin="0,8,0,0"
HasUnevenRows="true" ItemsSource="{Binding FoodItems}"
SeparatorColor="Transparent" SeparatorVisibility="None">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<templates:ListItemTemplate ParentContext="{Binding BindingContext, Source={x:Reference ListItemPage}}"/>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
<ListView.Behaviors>
<controls:EventToCommandBehavior EventName="ItemTapped" Command="{Binding ListItemTappedCommand}" CommandParameter="{x:Reference listView}"/>
</ListView.Behaviors>
</ListView>
finally the viewmodel goes with the icommand as below
public SelectItemViewModel(IAppContext context, IRestService restService) : base(context)
{
_restService = restService;
Title = "Category Products";
ListItemTappedCommand = new Command<ListView>(OnListItemTappedCommand);
PageAppearingCommand = new Command(OnPageAppearingCommand);
Itemtapped = new Command(OnItemtapped);
}
public ICommand Itemtapped { get; }
private void OnItemtapped()
{
Navigator.PushAsync<SelectItemViewModel>();
}
so the probelm is when compiling there is an error saying that
No property, bindable property, or event found for 'ParentContext', or mismatching type between value and property.
so basically saying that i am unable to give the button command ( in the template page) to trigger in the viewmodel properly, help will be appreciated to figure this out, thanks.
I'd introduce a AddToCartCommand in your child control, which is invoked if the button is clicked.
// in your ListItemTemplate.xaml.cs
public static BindableProperty AddToCartCommandProperty = BindableProperty.Create(/* ... */);
public ICommand AddToCartCommand
{
get => (ICommand)GetValue(AddToCartCommandProperty);
set => SetValue(AddToCartCommandProperty, value);
}
public void Button_OnClick(object sender, EventArgs e)
{
this.AddToCartCommand?.Execute(null);
}
To bind to this from your parent view, you'll need a reference to that parent view with x:Name="Page" (or however you'd like to name your page) and then use a {Binding Source={x:Reference Page}, Path=BindingContext.Itemtapped}
<ContentPage ... x:Name="Page"> <!-- I have elided attributes that are not relevant to my answer -->
<!-- Elided -->
<ListView ...>
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<templates:ListItemTemplate AddToCartCommand="{Binding Source={x:Reference Page}, Path=BindingContext.Itemtapped}" />
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
<!-- Elided -->
</ListView>
<!-- Elided -->
</ContentPage>

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/

Resources