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.
Related
On my page there is a 4 expander ,I want to write a code for at a time one expander is expand ,What can I do for this scenario please help me ,For more clarifications I have a add image of expanders ,In this given code there is a one expander show ,but this type of a 4 expander available on my page and i want to expand one at a time
enter image description here
<StackLayout IsVisible="{Binding Synonyms,Converter={x:StaticResource CorrectionTypeVisiableConverter}}" Orientation="Vertical" VerticalOptions="StartAndExpand" HorizontalOptions="FillAndExpand">
<xct:Expander ExpandAnimationEasing="CubicIn"
ExpandAnimationLength="500"
CollapseAnimationEasing="CubicOut"
CollapseAnimationLength="500">
<xct:Expander.Header>
<Frame HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand" BorderColor="#F0F0F0" HasShadow="False" >
<StackLayout Orientation="Horizontal" VerticalOptions="FillAndExpand" HorizontalOptions="FillAndExpand">
<xct:BadgeView Text="{Binding Synonyms,Converter={StaticResource CorrectionCountBadgeConverter}}" BackgroundColor="#FADBD8" BadgePosition="TopLeft" TextColor="#E74C3C" HorizontalOptions="Start" FontSize="16" AutoHide="True" VerticalOptions="CenterAndExpand">
<Label Text=""></Label>
</xct:BadgeView>
<Label Text="{x:Static resources:AppResources.Synonyms}"
FontAttributes="Bold" VerticalOptions="CenterAndExpand"
FontSize="Medium" Style="{StaticResource MenueLableStyle}" HorizontalOptions="StartAndExpand" />
<Image Source="Expand.png"
HorizontalOptions="EndAndExpand"
VerticalOptions="CenterAndExpand">
<Image.Triggers>
<DataTrigger TargetType="Image"
Binding="{Binding Source={RelativeSource AncestorType={x:Type xct:Expander}}, Path=IsExpanded,Mode=OneTime}"
Value="True">
<Setter Property="Source"
Value="collapse.png" />
</DataTrigger>
</Image.Triggers>
</Image>
</StackLayout>
</Frame>
</xct:Expander.Header>
<xct:Expander.ContentTemplate>
<DataTemplate>
<StackLayout BindableLayout.ItemsSource="{Binding Synonyms}" VerticalOptions="StartAndExpand" HorizontalOptions="FillAndExpand">
<BindableLayout.ItemTemplate>
<DataTemplate>
<Frame HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand" BorderColor="#F0F0F0" HasShadow="False" >
<Grid >
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<StackLayout Grid.Row="0" Orientation="Horizontal">
<Label Text="{Binding s}" HorizontalOptions="Start" VerticalOptions="Center">
</Label>
<Label Text="--->" HorizontalOptions="Start" VerticalOptions="Center"></Label>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"></ColumnDefinition>
</Grid.ColumnDefinitions>
<xct:BadgeView Grid.Column="0" Text="" BackgroundColor="#FADBD8" BadgePosition="TopRight" TextColor="#E74C3C" FontSize="14" HorizontalOptions="CenterAndExpand" AutoHide="True" VerticalOptions="Center" Background="#FADBD8" WidthRequest="80" HeightRequest="25">
<Label Text="{Binding c}"></Label>
</xct:BadgeView>
<ImageButton Grid.Column="0" Source="Storyedit.png"
HorizontalOptions="EndAndExpand" VerticalOptions="Center" WidthRequest="12" HeightRequest="12"/>
</Grid>
<ImageButton Source="Info.png" Command="{Binding Path=BindingContext.CorrectionInfoCommand,Source={x:Reference Name=storyView}}"
CommandParameter="{Binding .}" HorizontalOptions="EndAndExpand"/>
</StackLayout>
<Button Grid.Row="1" Text="{x:Static resources:AppResources.IgnoreButton}" Style="{StaticResource CancelButton}" VerticalOptions="EndAndExpand" HorizontalOptions="CenterAndExpand" ></Button>
<Button Grid.Row="1" Text="{x:Static resources:AppResources.AcceptButton}" Style="{StaticResource AcceptButton}" VerticalOptions="EndAndExpand" HorizontalOptions="StartAndExpand" ></Button>
<FlexLayout IsVisible="False" Grid.Row="3" HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand" AlignItems="Start" AlignContent="Center" Direction="Row" Wrap="Wrap" JustifyContent="Center">
<Button Text="{x:Static resources:AppResources.IgnoreButton}" Padding="5" Style="{StaticResource CancelButton}" HorizontalOptions="StartAndExpand"></Button>
<Button Text="{x:Static resources:AppResources.IgnoreAllButton}" Style="{StaticResource CancelButton}" HorizontalOptions="CenterAndExpand" Padding="5"></Button>
<Button Text="{x:Static resources:AppResources.AcceptButton}" Style="{StaticResource AcceptButton}" HorizontalOptions="StartAndExpand" Padding="5"></Button>
<Button Text="{x:Static resources:AppResources.AcceptAllButton}" Style="{StaticResource AcceptButton}" HorizontalOptions="CenterAndExpand" Padding="5"></Button>
</FlexLayout>
</Grid>
</Frame>
</DataTemplate>
</BindableLayout.ItemTemplate>
</StackLayout>
</DataTemplate>
</xct:Expander.ContentTemplate>
</xct:Expander>
</StackLayout>
You could binding true or false for the IsExpanded property to indicate that the Expander is expanded or collapsed.
I make a simple example for your reference.
Model:
public class Model : INotifyPropertyChanged
{
#region fields
public string _synonyms;
public string _s;
public bool _isexpand;
#endregion
public string Synonyms
{
get { return _synonyms; }
set { _synonyms = value; OnPropertyChanged("Synonyms"); }
}
public string s
{
get { return _s; }
set { _s = value; OnPropertyChanged("s"); }
}
public bool ISexpand
{
get { return _isexpand; }
set { _isexpand = value; OnPropertyChanged("ISexpand"); }
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
var handler = PropertyChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
ViewModel:
public class ViewModel1
{
public ObservableCollection<Model> models { get; set; }
public ViewModel1()
{
CreateCollection();
}
public void CreateCollection()
{
models = new ObservableCollection<Model>()
{
new Model(){ Synonyms="Synonym1", s="A", ISexpand=false},
new Model(){ Synonyms="Synonym2", s="B",ISexpand=false},
new Model(){ Synonyms="Synonym3", s="C",ISexpand=false},
new Model(){ Synonyms="Synonym4", s="D",ISexpand=false},
new Model(){ Synonyms="Synonym5", s="E",ISexpand=false},
};
}
}
Xaml:
<ContentPage.BindingContext>
<local:ViewModel1></local:ViewModel1>
</ContentPage.BindingContext>
<ContentPage.Content>
<StackLayout x:Name="stacklayout" BindableLayout.ItemsSource="{Binding models}" Orientation="Vertical" VerticalOptions="StartAndExpand" HorizontalOptions="FillAndExpand">
<BindableLayout.ItemTemplate>
<DataTemplate>
<xct:Expander IsExpanded="{Binding ISexpand}" Tapped="Expander_Tapped" >
<xct:Expander.Header>
<Label Text="{Binding Synonyms}"></Label>
</xct:Expander.Header>
<xct:Expander.ContentTemplate>
<DataTemplate>
<Label Text="{Binding s}"></Label>
</DataTemplate>
</xct:Expander.ContentTemplate>
</xct:Expander>
</DataTemplate>
</BindableLayout.ItemTemplate>
</StackLayout>
</ContentPage.Content>
Code behind:
public partial class Page10 : ContentPage
{
ViewModel1 viewModel1=new ViewModel1();
public Page10()
{
InitializeComponent();
}
int i = 0;
private void Expander_Tapped(object sender, EventArgs e)
{
var expander = sender as Expander;
var label = expander.Header as Label;
var list = viewModel1.models;
foreach (var item in viewModel1.models)
{
if (item.Synonyms == label.Text)
{
item.ISexpand = true;
if (i >= 1)
{
foreach (var item1 in viewModel1.models.ToList())
{
if (item1.Synonyms!= label.Text)
{
item1.ISexpand = false;
}
}
BindableLayout.SetItemsSource(stacklayout, viewModel1.models);
}
}
}
i++;
}
}
OutPut:
https://imgur.com/4D6x1yB
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.
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.
This my ViewModel:
namespace DietAndFitness.ViewModels
{
public class FoodItemsViewModel
{
private SQLiteAsyncConnection database;
public static ObservableCollection<GlobalFoodItem> FoodItems { get; set; }
public FoodItemsViewModel(SQLiteAsyncConnection _database)
{
database = _database;
}
public async void LoadList()
{
FoodItems = new ObservableCollection<GlobalFoodItem>(await database.Table<GlobalFoodItem>().ToListAsync());
}
public void Add()
{
FoodItems.Add(new GlobalFoodItem("Item"));
}
}
}
I want to bind an instance of this VM to the list in my View but I just can't get it to work properly. The only thing I've managed to get to work is this:
<?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:DietAndFitness.ViewModels"
x:Class="DietAndFitness.Views.FoodDatabasePage">
<ContentPage.Content>
<StackLayout>
<ListView ItemsSource="{Binding FoodItems}" RowHeight="120">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<ContentView Padding="5">
<Frame OutlineColor="Accent" Padding="10">
<StackLayout>
<Grid HorizontalOptions="Center">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Label Text="{Binding Name}" FontSize="22" VerticalOptions="CenterAndExpand" HorizontalOptions="Center" Grid.Row="0" Grid.Column="0"/>
<Label Text="{Binding Calories}" FontSize="22" VerticalOptions="CenterAndExpand" HorizontalOptions="Center" Grid.Row="0" Grid.Column="1"/>
</Grid>
<Grid HorizontalOptions="Center">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Label Text="Carbohydrates" Grid.Row="1" Grid.Column="0" HorizontalOptions="CenterAndExpand" VerticalOptions="CenterAndExpand" FontSize="16"/>
<Label Text="Proteins" Grid.Row="1" Grid.Column="1" HorizontalOptions="CenterAndExpand" VerticalOptions="CenterAndExpand" FontSize="16"/>
<Label Text="Fats" Grid.Row="1" Grid.Column="2" HorizontalOptions="CenterAndExpand" VerticalOptions="CenterAndExpand" FontSize="16"/>
<Label Text="{Binding Carbohydrates}" Grid.Row="2" Grid.Column="0" HorizontalOptions="CenterAndExpand" VerticalOptions="CenterAndExpand" FontSize="16"/>
<Label Text="{Binding Proteins}" Grid.Row="2" Grid.Column="1" HorizontalOptions="CenterAndExpand" VerticalOptions="CenterAndExpand" FontSize="16"/>
<Label Text="{Binding Fats}" Grid.Row="2" Grid.Column="2" HorizontalOptions="CenterAndExpand" VerticalOptions="CenterAndExpand" FontSize="16"/>
</Grid>
</StackLayout>
</Frame>
</ContentView>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<Grid HorizontalOptions="Center">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Button x:Name="AddFoodItemButton" BackgroundColor="LightGray" HorizontalOptions="CenterAndExpand" Text="Add" Grid.Row="0" Grid.Column="0" Clicked="AddFoodItemButton_Clicked" />
<Button x:Name="EditFoodItemButton" BackgroundColor="LightGray" HorizontalOptions="CenterAndExpand" Text="Edit" Grid.Row="0" Grid.Column="1" />
<Button x:Name="DeleteFoodItemButton" BackgroundColor="LightGray" HorizontalOptions="CenterAndExpand" Text="Delete" Grid.Row="0" Grid.Column="2" />
</Grid>
</StackLayout>
</ContentPage.Content>
But this has problems too:
If I create the VM in the page constructor the list won't load unless I refresh the page
If I create the VM inside App.cs the list will load on the first try but then I can't seem to access the instance of the VM
How do I bind my ListView to this VM? And is it possible to do so without making my ObservableCollection static?
I'm using a Master-Detail page in my app if it makes any difference (this is one of the detail pages).
Edit: Here is what I did in the constructor. SQLiteConnection.Database is just a static class that contains the connection to the database.
public FoodDatabasePage ()
{
FoodDatabase = new FoodItemsViewModel(SQLiteConnection.Database);
FoodDatabase.LoadList();
InitializeComponent ();
}
There's a few things that you have there.
When binding a ViewModel usually it's against the whole Page making the ViewModel its BindingContext specially in cases like yours when you have methods that want to access later.
So to do this you will need a few changes.
Your Page 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:DietAndFitness.ViewModels"
x:Class="DietAndFitness.Views.FoodDatabasePage">
<ContentPage.Content>
<StackLayout>
<ListView ItemsSource="{Binding FoodItems}"
RowHeight="120">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Frame OutlineColor="Accent" Padding="15">
<StackLayout>
<Grid HorizontalOptions="Center">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Label Text="{Binding Name}"
FontSize="22"
VerticalOptions="CenterAndExpand"
HorizontalOptions="Center"
Grid.Row="0"
Grid.Column="0"/>
</Grid>
</StackLayout>
</Frame>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</StackLayout>
</ContentPage.Content>
</ContentPage>
Check that I changed the ListView ItemSource for the name of the collection in your FoodItemsViewModel.
Also, part of your XAML disappeared so I could not complete it.
Your Code Behind class: (FoodDatabasePage.xaml.cs)
public FoodItemsViewModel FoodDatabase {get; set;}
public FoodDatabasePage ()
{
InitializeComponent ();
FoodDatabase = new FoodItemsViewModel(SQLiteConnection.Database);
BindingContext = FoodDatabase;
}
public override void OnAppearing()
{
FoodDatabase.LoadList();
}
Here is where the "magic" happens. We are creating and storing the object of our FoodItemsViewModel so you can access it later as you need and then we are setting this object recently created as the BindingContext of the page (as explained above). Setting the ViewModel as the BindingContext of Page the latter will be able to access any public property of the FoodItemsViewModel.
Also, you will notice the LoadList() method was removed from the constructor, this is because there shouldn't be any heavy transaction (actually we should avoid any transactions) in the Page constructor. We moved it instead to the OnAppearing callback method that is executed by the Page when this is about to be displayed.
Note: This method is called every time the Page is presented on the screen so if you navigate away and come back it will be called again.
Your ViewModel:
namespace DietAndFitness.ViewModels
{
public class FoodItemsViewModel
{
private SQLiteAsyncConnection database;
//You don't need it static
public ObservableCollection<GlobalFoodItem> FoodItems { get; set; }
public FoodItemsViewModel(SQLiteAsyncConnection _database)
{
database = _database;
}
public async void LoadList()
{
// separated it only for readability.
var items = await database.Table<GlobalFoodItem>().ToListAsync();
FoodItems = new ObservableCollection<GlobalFoodItem>(items);
}
public void Add()
{
FoodItems.Add(new GlobalFoodItem("Item"));
}
}
}
This remains almost the same. The only change was removing the static qualifier to the FoodItems property since it does not need it anymore.
The above is just the basic of Mvvm and DataBinding. There're a few other things that you will need to take into consideration like INotifiedPropertyChanged, Commands and other important things.
You can find more information regarding this here
Hope this helps.-
PS: I couldn't check all of your code for any error.
I'm following the following example of carousel with the listview, more when I go to open Navigation.PushAsync the item in the listview is called only the first item, on all items clicked his name only the first item, a position error?
void Handle_ItemSelected(object sender, SelectedItemChangedEventArgs e)
{
var monkey = ((ListView)sender).SelectedItem as Monkey;
if (monkey == null)
return;
Navigation.PushAsync(new DetailsPage(monkey));
}
https://blog.xamarin.com/flip-through-items-with-xamarin-forms-carouselview/
EDIT
MonkeysPage.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="Monkeys.Views.MonkeysPage"
xmlns:design="clr-namespace:Monkeys;assembly=Monkeys"
xmlns:controls="clr-namespace:ImageCircle.Forms.Plugin.Abstractions;assembly=ImageCircle.Forms.Plugin.Abstractions"
xmlns:cv="clr-namespace:Xamarin.Forms;assembly=Xamarin.Forms.CarouselView"
BindingContext="{x:Static design:ViewModelLocator.MonkeysViewModel}"
x:Name="MonkeysPage"
Title="Traditional Monkeys">
<ContentPage.Content>
<ListView ItemsSource="{Binding MonkeysGrouped}"
ItemSelected="Handle_ItemSelected"
HasUnevenRows="true"
GroupShortNameBinding = "{Binding Key}"
IsGroupingEnabled = "true"
GroupDisplayBinding = "{Binding Key}">
<ListView.Header>
<cv:CarouselView x:Name="CarouselZoos" ItemsSource="{Binding Path=BindingContext.Zoos, Source={x:Reference MonkeysPage}}" HeightRequest="200">
<cv:CarouselView.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Image Grid.RowSpan="2" Aspect="AspectFill" Source="{Binding ImageUrl}"/>
<StackLayout Grid.Row="1" BackgroundColor="#80000000" Padding="12">
<Label TextColor="White" Text="{Binding Name}" FontSize="16" HorizontalOptions="Center" VerticalOptions="CenterAndExpand"/>
</StackLayout>
</Grid>
</DataTemplate>
</cv:CarouselView.ItemTemplate>
</cv:CarouselView>
</ListView.Header>
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Grid Padding="10" RowSpacing="10" ColumnSpacing="10">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<controls:CircleImage
BorderColor="Aqua"
BorderThickness="3"
HeightRequest="66"
HorizontalOptions="CenterAndExpand"
VerticalOptions="CenterAndExpand"
Aspect="AspectFill"
WidthRequest="66"
Grid.RowSpan="2"
Source="{Binding Image}"/>
<Label Grid.Column="1"
Text="{Binding Name}"
VerticalOptions="End"/>
<Label Grid.Column="1"
Grid.Row="1"
VerticalOptions="Start"
Text="{Binding Location}"/>
</Grid>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
MonkeysPage.xaml.cs
public MonkeysPage()
{
InitializeComponent();
BindingContext = new MonkeysViewModel();
}
async void Handle_ItemSelected(object sender, SelectedItemChangedEventArgs e)
{
var monkey = e.SelectedItem as Monkey;
if (monkey == null)
return;
await Navigation.PushAsync(new DetailsPage(monkey)); // always await Navigations
((ListView)sender).SelectedItem = null;
}
DetailsPage.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="Monkeys.Views.DetailsPage"
xmlns:design="clr-namespace:Monkeys;assembly=Monkeys"
xmlns:controls="clr-namespace:ImageCircle.Forms.Plugin.Abstractions;assembly=ImageCircle.Forms.Plugin.Abstractions"
BindingContext="{x:Static design:ViewModelLocator.DetailsViewModel}"
Title="{Binding Monkey.Name}">
<ContentPage.Content>
<ScrollView>
<StackLayout Padding="10">
<controls:CircleImage
BorderColor="Aqua"
BorderThickness="3"
HeightRequest="200"
WidthRequest="200"
HorizontalOptions="CenterAndExpand"
Aspect="AspectFill"
Source="{Binding Monkey.Image}"/>
<Label Text="{Binding Monkey.Name}" FontAttributes="Bold"/>
<Label Text="{Binding Monkey.Location}" FontSize="Micro"/>
<Label Text="{Binding Monkey.Details}" FontSize="Large"/>
</StackLayout>
</ScrollView>
</ContentPage.Content>
DetailsPage
public partial class DetailsPage : ContentPage
{
public DetailsPage(Monkey monkey)
{
InitializeComponent();
}
}
You're doing everything good except that you forgot to reset the SelectedItem to null, that's why you are getting always the first list item clicked.
Your code should look like this:
async void Handle_ItemSelected(object sender, SelectedItemChangedEventArgs e)
{
var monkey = e.SelectedItem as Monkey;
if (monkey == null)
return;
await Navigation.PushAsync(new DetailsPage(monkey)); // always await Navigations
((ListView)sender).SelectedItem = null;
}
P.S. Make your method async so you don't cause UI Thread to block
EDIT
Try to get the selected item from SelectedItemChangedEventArgs.
var monkey = e.SelectedItem as Monkey;
UPDATE
Okay the SelectedItem is being updated successfully but the 'Monkey Item' on the ViewModel behind the DetailsPage is not!
So on DetailsPage add this line:
public partial class DetailsPage : ContentPage
{
public DetailsPage(Monkey monkey)
{
InitializeComponent();
BindingContext = new DetailsViewModel(monkey); // Update the BindingContext
}
}
And remove this line on the DetailsPage.xaml:
BindingContext="{x:Static design:ViewModelLocator.DetailsViewModel}"